001/*
002 * A class to serve JavaRendererServer
003 */
004package armyc2.c5isr.RenderMultipoints;
005
006import android.content.Context;
007
008import java.util.ArrayList;
009import java.util.Collections;
010import java.util.HashMap;
011import java.util.Map;
012
013import armyc2.c5isr.JavaLineArray.CELineArray;
014import armyc2.c5isr.JavaLineArray.DISMSupport;
015import armyc2.c5isr.JavaLineArray.POINT2;
016import armyc2.c5isr.JavaLineArray.Shape2;
017import armyc2.c5isr.JavaLineArray.TacticalLines;
018import armyc2.c5isr.JavaLineArray.lineutility;
019import armyc2.c5isr.JavaTacticalRenderer.Modifier2;
020import armyc2.c5isr.JavaTacticalRenderer.TGLight;
021import armyc2.c5isr.JavaTacticalRenderer.mdlGeodesic;
022import armyc2.c5isr.graphics2d.BasicStroke;
023import armyc2.c5isr.graphics2d.BufferedImage;
024import armyc2.c5isr.graphics2d.Font;
025import armyc2.c5isr.graphics2d.Graphics2D;
026import armyc2.c5isr.graphics2d.Point2D;
027import armyc2.c5isr.graphics2d.Rectangle;
028import armyc2.c5isr.graphics2d.Rectangle2D;
029import armyc2.c5isr.renderer.utilities.Color;
030import armyc2.c5isr.renderer.utilities.DistanceUnit;
031import armyc2.c5isr.renderer.utilities.DrawRules;
032import armyc2.c5isr.renderer.utilities.ErrorLogger;
033import armyc2.c5isr.renderer.utilities.IPointConversion;
034import armyc2.c5isr.renderer.utilities.MSInfo;
035import armyc2.c5isr.renderer.utilities.MSLookup;
036import armyc2.c5isr.renderer.utilities.MilStdSymbol;
037import armyc2.c5isr.renderer.utilities.Modifiers;
038import armyc2.c5isr.renderer.utilities.RendererException;
039import armyc2.c5isr.renderer.utilities.RendererSettings;
040import armyc2.c5isr.renderer.utilities.ShapeInfo;
041import armyc2.c5isr.renderer.utilities.SymbolID;
042import armyc2.c5isr.renderer.utilities.SymbolUtilities;
043
044/**
045 * Rendering class
046 *
047*
048 */
049public final class clsRenderer {
050
051    private static final String _className = "clsRenderer";
052
053    /**
054     * Set tg geo points from the client points
055     *
056     * @param milStd
057     * @param tg
058     */
059    private static void setClientCoords(MilStdSymbol milStd,
060                                        TGLight tg) {
061        try {
062            ArrayList<POINT2> latLongs = new ArrayList();
063            int j = 0;
064            ArrayList<Point2D> coords = milStd.getCoordinates();
065            Point2D pt2d = null;
066            POINT2 pt2 = null;
067            int n = coords.size();
068            //for (j = 0; j < coords.size(); j++) 
069            for (j = 0; j < n; j++) {
070                pt2d = coords.get(j);
071                pt2 = clsUtility.Point2DToPOINT2(pt2d);
072                latLongs.add(pt2);
073            }
074            tg.set_LatLongs(latLongs);
075        } catch (Exception exc) {
076            ErrorLogger.LogException("clsRenderer", "setClientCoords",
077                    new RendererException("Failed to set geo points or pixels for " + milStd.getSymbolID(), exc));
078        }
079    }
080
081    private static ArrayList<Point2D> getClientCoords(TGLight tg) {
082        ArrayList<Point2D> coords = null;
083        try {
084            int j = 0;
085            Point2D pt2d = null;
086            POINT2 pt2 = null;
087            coords = new ArrayList();
088            int n = tg.LatLongs.size();
089            //for (j = 0; j < tg.LatLongs.size(); j++) 
090            for (j = 0; j < n; j++) {
091                pt2 = tg.LatLongs.get(j);
092                pt2d = new Point2D.Double(pt2.x, pt2.y);
093                coords.add(pt2d);
094            }
095        } catch (Exception exc) {
096            ErrorLogger.LogException("clsRenderer", "getClientCoords",
097                    new RendererException("Failed to set geo points or pixels for " + tg.get_SymbolId(), exc));
098        }
099        return coords;
100    }
101
102    /**
103     * Build a tactical graphic object from the client MilStdSymbol
104     *
105     * @param milStd MilstdSymbol object
106     * @param converter geographic to pixels converter
107     * @param lineType {@link armyc2.c5isr.JavaLineArray.BasicShapes}
108     * @return tactical graphic
109     */
110    public static TGLight createTGLightFromMilStdSymbolBasicShape(MilStdSymbol milStd,
111                                                                  IPointConversion converter,
112                                                                  int lineType) {
113        TGLight tg = new TGLight();
114        try {
115            boolean useLineInterpolation = milStd.getUseLineInterpolation();
116            tg.set_UseLineInterpolation(useLineInterpolation);
117            tg.set_LineType(lineType);
118            String status = tg.get_Status();
119            tg.set_VisibleModifiers(true);
120            //set tg latlongs and pixels
121            setClientCoords(milStd, tg);
122            //build tg.Pixels
123            tg.Pixels = clsUtility.LatLongToPixels(tg.LatLongs, converter);
124            //tg.set_Font(new Font("Arial", Font.PLAIN, 12));
125            RendererSettings r = RendererSettings.getInstance();
126            int type = r.getMPLabelFontType();
127            String name = r.getMPLabelFontName();
128            int sz = r.getMPLabelFontSize();
129            Font font = new Font(name, type, sz);
130            tg.set_Font(font);
131            tg.set_FillColor(milStd.getFillColor());
132            tg.set_LineColor(milStd.getLineColor());
133            tg.set_LineThickness(milStd.getLineWidth());
134            tg.set_TexturePaint(milStd.getFillStyle());
135            tg.set_Fillstyle(milStd.getPatternFillType());
136            tg.set_patternScale(milStd.getPatternScale());
137
138            tg.set_IconSize(milStd.getUnitSize());
139            tg.set_KeepUnitRatio(milStd.getKeepUnitRatio());
140
141            tg.set_FontBackColor(Color.WHITE);
142            tg.set_TextColor(milStd.getTextColor());
143            if (milStd.getModifier(Modifiers.W_DTG_1) != null) {
144                tg.set_DTG(milStd.getModifier(Modifiers.W_DTG_1));
145            }
146            if (milStd.getModifier(Modifiers.W1_DTG_2) != null) {
147                tg.set_DTG1(milStd.getModifier(Modifiers.W1_DTG_2));
148            }
149            if (milStd.getModifier(Modifiers.H_ADDITIONAL_INFO_1) != null) {
150                tg.set_H(milStd.getModifier(Modifiers.H_ADDITIONAL_INFO_1));
151            }
152            if (milStd.getModifier(Modifiers.H1_ADDITIONAL_INFO_2) != null) {
153                tg.set_H1(milStd.getModifier(Modifiers.H1_ADDITIONAL_INFO_2));
154            }
155            if (milStd.getModifier(Modifiers.H2_ADDITIONAL_INFO_3) != null) {
156                tg.set_H2(milStd.getModifier(Modifiers.H2_ADDITIONAL_INFO_3));
157            }
158            if (milStd.getModifier(Modifiers.T_UNIQUE_DESIGNATION_1) != null) {
159                tg.set_Name(milStd.getModifier(Modifiers.T_UNIQUE_DESIGNATION_1));
160            }
161            if (milStd.getModifier(Modifiers.T1_UNIQUE_DESIGNATION_2) != null) {
162                tg.set_T1(milStd.getModifier(Modifiers.T1_UNIQUE_DESIGNATION_2));
163            }
164            if (milStd.getModifier(Modifiers.V_EQUIP_TYPE) != null) {
165                tg.set_V(milStd.getModifier(Modifiers.V_EQUIP_TYPE));
166            }
167            if (milStd.getModifier(Modifiers.AS_COUNTRY) != null) {
168                tg.set_AS(milStd.getModifier(Modifiers.AS_COUNTRY));
169            }
170            if (milStd.getModifier(Modifiers.AP_TARGET_NUMBER) != null) {
171                tg.set_AP(milStd.getModifier(Modifiers.AP_TARGET_NUMBER));
172            }
173            if (milStd.getModifier(Modifiers.Y_LOCATION) != null) {
174                tg.set_Location(milStd.getModifier(Modifiers.Y_LOCATION));
175            }
176            if (milStd.getModifier(Modifiers.N_HOSTILE) != null) {
177                tg.set_N(milStd.getModifier(Modifiers.N_HOSTILE));
178            }
179            tg.set_UseDashArray(milStd.getUseDashArray());
180            tg.set_UseHatchFill(milStd.getUseFillPattern());
181            //tg.set_UsePatternFill(milStd.getUseFillPattern());
182            tg.set_HideOptionalLabels(milStd.getHideOptionalLabels());
183            boolean isClosedArea = armyc2.c5isr.JavaTacticalRenderer.clsUtility.isClosedPolygon(lineType);
184
185         if (isClosedArea) {
186                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(tg.Pixels);
187                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(tg.LatLongs);
188            }
189
190            String strXAlt = "";
191            //construct the H1 and H2 modifiers for sector from the mss AM, AN, and X arraylists
192            if (lineType == TacticalLines.BS_ELLIPSE) {
193                ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
194                ArrayList<Double> AN = milStd.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH);
195                //ensure array length 3
196                double r2=0;
197                double b=0;
198                if(AM.size()==1)
199                {
200                    r2=AM.get(0);
201                    AM.add(r2);
202                    AM.add(0d);
203                }
204                else if(AM.size()==2)
205                {
206                    r2=AM.get(0);
207                    b=AM.get(1);
208                    AM.set(1, r2);
209                    AM.add(b);
210                }
211                if (AN == null) {
212                    AN = new ArrayList<Double>();
213                }
214                if (AN.size() < 1) {
215                    AN.add(new Double(0));
216                }
217                if (AM != null && AM.size() >= 2 && AN != null && AN.size() >= 1) {
218                    POINT2 ptAzimuth = new POINT2(0, 0);
219                    ptAzimuth.x = AN.get(0);
220                    POINT2 ptCenter = tg.Pixels.get(0);
221                    POINT2 pt0 = mdlGeodesic.geodesic_coordinate(tg.LatLongs.get(0), AM.get(0), 90);//semi-major axis
222                    POINT2 pt1 = mdlGeodesic.geodesic_coordinate(tg.LatLongs.get(0), AM.get(1), 0);//semi-minor axis
223                    Point2D pt02d = new Point2D.Double(pt0.x, pt0.y);
224                    Point2D pt12d = new Point2D.Double(pt1.x, pt1.y);
225                    pt02d = converter.GeoToPixels(pt02d);
226                    pt12d = converter.GeoToPixels(pt12d);
227                    pt0 = new POINT2(pt02d.getX(), pt02d.getY());
228                    pt1 = new POINT2(pt12d.getX(), pt12d.getY());
229                    tg.Pixels = new ArrayList<POINT2>();
230                    tg.Pixels.add(ptCenter);
231                    tg.Pixels.add(pt0);
232                    tg.Pixels.add(pt1);
233                    tg.Pixels.add(ptAzimuth);
234                }
235                if(AM != null && AM.size()>2)
236                {
237                    //use AM[2] for the buffer, so PBS_CIRCLE requires AM size 3 like PBS_ELLIPSE to use a buffer
238                    double dist=AM.get(2);
239                    POINT2 pt0=mdlGeodesic.geodesic_coordinate(tg.LatLongs.get(0), dist, 45);   //azimuth 45 is arbitrary
240                    Point2D pt02d = new Point2D.Double(tg.LatLongs.get(0).x,tg.LatLongs.get(0).y);
241                    Point2D pt12d = new Point2D.Double(pt0.x, pt0.y);
242                    pt02d = converter.GeoToPixels(pt02d);
243                    pt12d = converter.GeoToPixels(pt12d);
244                    pt0=new POINT2(pt02d.getX(),pt02d.getY());
245                    POINT2 pt1=new POINT2(pt12d.getX(),pt12d.getY());
246                    dist=lineutility.CalcDistanceDouble(pt0, pt1);
247                    //arraysupport will use line style to create the buffer shape
248                    tg.Pixels.get(0).style=(int)dist;
249                }
250            }
251            int j = 0;
252            if (lineType == TacticalLines.BBS_RECTANGLE || lineType == TacticalLines.BS_BBOX) {
253                double minLat = tg.LatLongs.get(0).y;
254                double maxLat = tg.LatLongs.get(0).y;
255                double minLong = tg.LatLongs.get(0).x;
256                double maxLong = tg.LatLongs.get(0).x;
257                for (j = 1; j < tg.LatLongs.size(); j++) {
258                    if (tg.LatLongs.get(j).x < minLong) {
259                        minLong = tg.LatLongs.get(j).x;
260                    }
261                    if (tg.LatLongs.get(j).x > maxLong) {
262                        maxLong = tg.LatLongs.get(j).x;
263                    }
264                    if (tg.LatLongs.get(j).y < minLat) {
265                        minLat = tg.LatLongs.get(j).y;
266                    }
267                    if (tg.LatLongs.get(j).y > maxLat) {
268                        maxLat = tg.LatLongs.get(j).y;
269                    }
270                }
271                tg.LatLongs = new ArrayList();
272                tg.LatLongs.add(new POINT2(minLong, maxLat));
273                tg.LatLongs.add(new POINT2(maxLong, maxLat));
274                tg.LatLongs.add(new POINT2(maxLong, minLat));
275                tg.LatLongs.add(new POINT2(minLong, minLat));
276                if (lineType == TacticalLines.BS_BBOX) {
277                    tg.LatLongs.add(new POINT2(minLong, maxLat));
278                }
279                tg.Pixels = clsUtility.LatLongToPixels(tg.LatLongs, converter);
280            }
281            //these have a buffer value in meters which we'll stuff tg.H2
282            //and use the style member of tg.Pixels to stuff the buffer width in pixels
283            switch (lineType) {
284                case TacticalLines.BBS_AREA:
285                case TacticalLines.BBS_LINE:
286                case TacticalLines.BBS_RECTANGLE:
287                    String H2 = null;
288                    double dist = 0;
289                    POINT2 pt0;
290                    POINT2 pt1;//45 is arbitrary
291                    ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
292                    if (AM != null && AM.size() > 0) {
293                        H2 = AM.get(0).toString();
294                        tg.set_H2(H2);
295                    }
296                    if (H2 != null && !H2.isEmpty()) {
297                        for (j = 0; j < tg.LatLongs.size(); j++) {
298                            if (tg.LatLongs.size() > j) {
299                                if (!Double.isNaN(Double.parseDouble(H2))) {
300                                    if (j == 0) {
301                                        dist = Double.parseDouble(H2);
302                                        pt0 = new POINT2(tg.LatLongs.get(0));
303                                        pt1 = mdlGeodesic.geodesic_coordinate(pt0, dist, 45);//45 is arbitrary
304                                        Point2D pt02d = new Point2D.Double(pt0.x, pt0.y);
305                                        Point2D pt12d = new Point2D.Double(pt1.x, pt1.y);
306                                        pt02d = converter.GeoToPixels(pt02d);
307                                        pt12d = converter.GeoToPixels(pt12d);
308                                        pt0.x = pt02d.getX();
309                                        pt0.y = pt02d.getY();
310                                        pt1.x = pt12d.getX();
311                                        pt1.y = pt12d.getY();
312                                        dist = lineutility.CalcDistanceDouble(pt0, pt1);
313                                    }
314                                    tg.Pixels.get(j).style = Math.round((float) dist);
315                                } else {
316                                    tg.Pixels.get(j).style = 0;
317                                }
318                            }
319                        }
320                    }
321                    break;
322                default:
323                    break;
324            }
325            if (lineType == TacticalLines.PBS_ELLIPSE) //geo ellipse
326            {
327                ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
328                ArrayList<Double> AN = milStd.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH);
329                if (AM != null && AM.size() > 1) {
330                    String strAM = AM.get(0).toString(); // major axis
331                    tg.set_AM(strAM);
332                    String strAM1 = AM.get(1).toString(); // minor axis
333                    tg.set_AM1(strAM1);
334                }
335                if (AN != null && AN.size() > 0) {
336                    String strAN = AN.get(0).toString(); // rotation
337                    tg.set_AN(strAN);
338                }
339            }
340            switch (lineType) {
341                case TacticalLines.BBS_AREA:
342                case TacticalLines.BBS_LINE:
343                case TacticalLines.BBS_POINT:
344                case TacticalLines.BBS_RECTANGLE:
345                    if (tg.get_FillColor() == null) {
346                        tg.set_FillColor(Color.LIGHT_GRAY);
347                    }
348                    break;
349                default:
350                    break;
351            }
352            switch (lineType) {
353                case TacticalLines.PBS_CIRCLE:
354                case TacticalLines.BBS_POINT:
355                    ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
356                    if (AM != null && AM.size() > 0) {
357                        String strAM = Double.toString(AM.get(0));
358                        //set width for rectangles or radius for circles
359                        tg.set_AM(strAM);
360                    } else if (lineType == TacticalLines.BBS_POINT && tg.LatLongs.size() > 1) {
361                        double dist = mdlGeodesic.geodesic_distance(tg.LatLongs.get(0), tg.LatLongs.get(1), null, null);
362                        String strT1 = Double.toString(dist);
363                        tg.set_T1(strT1);
364                    }
365                    break;
366                default:
367                    break;
368            }
369            if (lineType == TacticalLines.PBS_RECTANGLE || lineType == TacticalLines.PBS_SQUARE) {
370                ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
371                ArrayList<Double> AN = milStd.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH);
372                if (lineType == TacticalLines.PBS_SQUARE) //for square
373                {
374                    double r2=AM.get(0);
375                    double b=0;
376                    if(AM.size()==1)
377                    {
378                        AM.add(r2);
379                        AM.add(b);
380                    }
381                    else if(AM.size()==2)
382                    {
383                        b=AM.get(1);
384                        AM.set(1,r2);
385                        AM.add(b);
386                    }
387                    else if(AM.size()>2)
388                        AM.set(1, r2);
389                }
390                //if all these conditions are not met we do not want to set any tg modifiers
391                if (lineType == TacticalLines.PBS_SQUARE) //square
392                {
393                    double am0 = AM.get(0);
394                    if (AM.size() == 1) {
395                        AM.add(am0);
396                    } else if (AM.size() >= 2) {
397                        AM.set(1, am0);
398                    }
399                }
400                if (AN == null) {
401                    AN = new ArrayList<>();
402                }
403                if (AN.isEmpty()) {
404                    AN.add(0d);
405                }
406
407                if (AM != null && AM.size() > 1) {
408                    String strAM = Double.toString(AM.get(0));    //width
409                    String strAM1 = Double.toString(AM.get(1));     //length
410                    //set width and length in meters for rectangular target
411                    tg.set_AM(strAM);
412                    tg.set_AM1(strAM1);
413                    //set attitude in degrees
414                    String strAN = Double.toString(AN.get(0));
415                    tg.set_AN(strAN);
416                }
417                /*
418                if(AM.size()>2)
419                {
420                    String strH1 = Double.toString(AM.get(2));     //buffer size
421                    tg.set_H1(strH1);
422                }
423                 */
424            }
425        } catch (Exception exc) {
426            ErrorLogger.LogException("clsRenderer", "createTGLightFromBasicMilStdSymbol",
427                    new RendererException("Failed to build multipoint TG for " + lineType, exc));
428        }
429        return tg;
430    }
431
432    /**
433     * Create MilStdSymbol from tactical graphic
434     *
435     * @deprecated
436     * @param tg tactical graphic
437     * @param converter geographic to pixels to converter
438     * @return MilstdSymbol object
439     */
440    public static MilStdSymbol createMilStdSymboFromTGLight(TGLight tg, IPointConversion converter) {
441        MilStdSymbol milStd = null;
442        try {
443            String symbolId = tg.get_SymbolId();
444            int lineType = armyc2.c5isr.JavaTacticalRenderer.clsUtility.GetLinetypeFromString(symbolId);
445            String status = tg.get_Status();
446            //build tg.Pixels
447            tg.Pixels = clsUtility.LatLongToPixels(tg.LatLongs, converter);
448            boolean isClosedArea = armyc2.c5isr.JavaTacticalRenderer.clsUtility.isClosedPolygon(lineType);
449            if (isClosedArea) {
450                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(tg.Pixels);
451                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(tg.LatLongs);
452            }
453
454            ArrayList<Point2D> coords = getClientCoords(tg);
455            tg.set_Font(new Font("Arial", Font.PLAIN, 12));
456            Map<String,String> modifiers = new HashMap<>();
457            modifiers.put(Modifiers.W_DTG_1, tg.get_DTG());
458            modifiers.put(Modifiers.W1_DTG_2, tg.get_DTG1());
459            modifiers.put(Modifiers.H_ADDITIONAL_INFO_1, tg.get_H());
460            modifiers.put(Modifiers.H1_ADDITIONAL_INFO_2, tg.get_H1());
461            modifiers.put(Modifiers.H2_ADDITIONAL_INFO_3, tg.get_H2());
462            modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, tg.get_Name());
463            modifiers.put(Modifiers.T1_UNIQUE_DESIGNATION_2, tg.get_T1());
464            modifiers.put(Modifiers.Y_LOCATION, tg.get_Location());
465            modifiers.put(Modifiers.N_HOSTILE, tg.get_N());
466
467            milStd = new MilStdSymbol(symbolId, "1", coords, modifiers);
468            milStd.setFillColor(tg.get_FillColor());
469            milStd.setLineColor(tg.get_LineColor());
470            milStd.setLineWidth(tg.get_LineThickness());
471            milStd.setFillStyle(tg.get_TexturePaint());
472            milStd.setPatternScale(tg.get_patternScale());
473        } catch (Exception exc) {
474            ErrorLogger.LogException("clsRenderer", "createMilStdSymboFromTGLight",
475                    new RendererException("Failed to set geo points or pixels for " + tg.get_SymbolId(), exc));
476        }
477        return milStd;
478    }
479
480    /**
481     * Build a tactical graphic object from the client MilStdSymbol
482     *
483     * @param milStd MilstdSymbol object
484     * @param converter geographic to pixels converter
485     * @return tactical graphic
486     */
487    public static TGLight createTGLightFromMilStdSymbol(MilStdSymbol milStd,
488            IPointConversion converter) {
489        TGLight tg = new TGLight();
490        try {
491            String symbolId = milStd.getSymbolID();
492            tg.set_SymbolId(symbolId);
493            boolean useLineInterpolation = milStd.getUseLineInterpolation();
494            tg.set_UseLineInterpolation(useLineInterpolation);
495            int lineType = armyc2.c5isr.JavaTacticalRenderer.clsUtility.GetLinetypeFromString(symbolId);
496            tg.set_LineType(lineType);
497            String status = tg.get_Status();
498            if (status != null && status.equals("A")) {
499                tg.set_LineStyle(1);
500            }
501            tg.set_VisibleModifiers(true);
502            //set tg latlongs and pixels
503            setClientCoords(milStd, tg);
504            //build tg.Pixels
505            tg.Pixels = clsUtility.LatLongToPixels(tg.LatLongs, converter);
506            //tg.set_Font(new Font("Arial", Font.PLAIN, 12));
507            RendererSettings r = RendererSettings.getInstance();
508            int type = r.getMPLabelFontType();
509            String name = r.getMPLabelFontName();
510            int sz = r.getMPLabelFontSize();
511            Font font = new Font(name, type, sz);
512            tg.set_Font(font);
513            tg.set_FillColor(milStd.getFillColor());
514            tg.set_LineColor(milStd.getLineColor());
515            tg.set_LineThickness(milStd.getLineWidth());
516            tg.set_TexturePaint(milStd.getFillStyle());
517            tg.set_patternScale(milStd.getPatternScale());
518
519            tg.set_IconSize(milStd.getUnitSize());
520            tg.set_KeepUnitRatio(milStd.getKeepUnitRatio());
521
522            tg.set_FontBackColor(Color.WHITE);
523            tg.set_TextColor(milStd.getTextColor());
524            if (milStd.getModifier(Modifiers.W_DTG_1) != null) {
525                tg.set_DTG(milStd.getModifier(Modifiers.W_DTG_1));
526            }
527            if (milStd.getModifier(Modifiers.W1_DTG_2) != null) {
528                tg.set_DTG1(milStd.getModifier(Modifiers.W1_DTG_2));
529            }
530            if (milStd.getModifier(Modifiers.H_ADDITIONAL_INFO_1) != null) {
531                tg.set_H(milStd.getModifier(Modifiers.H_ADDITIONAL_INFO_1));
532            }
533            if (milStd.getModifier(Modifiers.H1_ADDITIONAL_INFO_2) != null) {
534                tg.set_H1(milStd.getModifier(Modifiers.H1_ADDITIONAL_INFO_2));
535            }
536            if (milStd.getModifier(Modifiers.H2_ADDITIONAL_INFO_3) != null) {
537                tg.set_H2(milStd.getModifier(Modifiers.H2_ADDITIONAL_INFO_3));
538            }
539            if (milStd.getModifier(Modifiers.T_UNIQUE_DESIGNATION_1) != null) {
540                tg.set_Name(milStd.getModifier(Modifiers.T_UNIQUE_DESIGNATION_1));
541            }
542            if (milStd.getModifier(Modifiers.T1_UNIQUE_DESIGNATION_2) != null) {
543                tg.set_T1(milStd.getModifier(Modifiers.T1_UNIQUE_DESIGNATION_2));
544            }
545            if (milStd.getModifier(Modifiers.V_EQUIP_TYPE) != null) {
546                tg.set_V(milStd.getModifier(Modifiers.V_EQUIP_TYPE));
547            }
548            if (milStd.getModifier(Modifiers.AS_COUNTRY) != null) {
549                tg.set_AS(milStd.getModifier(Modifiers.AS_COUNTRY));
550            }
551            if (milStd.getModifier(Modifiers.AP_TARGET_NUMBER) != null) {
552                tg.set_AP(milStd.getModifier(Modifiers.AP_TARGET_NUMBER));
553            }
554            if (milStd.getModifier(Modifiers.Y_LOCATION) != null) {
555                tg.set_Location(milStd.getModifier(Modifiers.Y_LOCATION));
556            }
557            if (milStd.getModifier(Modifiers.N_HOSTILE) != null) {
558                tg.set_N(milStd.getModifier(Modifiers.N_HOSTILE));
559            }
560            tg.set_UseDashArray(milStd.getUseDashArray());
561            tg.set_UseHatchFill(milStd.getUseFillPattern());
562            //tg.set_UsePatternFill(milStd.getUseFillPattern());
563            tg.set_HideOptionalLabels(milStd.getHideOptionalLabels());
564            boolean isClosedArea = armyc2.c5isr.JavaTacticalRenderer.clsUtility.isClosedPolygon(lineType);
565
566            if (lineType == TacticalLines.STRIKWARN) {
567                ArrayList<POINT2> poly1Pixels = new ArrayList<>(tg.Pixels.subList(0, tg.Pixels.size() / 2));
568                ArrayList<POINT2> poly1LatLons = new ArrayList<>(tg.LatLongs.subList(0, tg.LatLongs.size() / 2));
569                ArrayList<POINT2> poly2Pixels = new ArrayList<>(tg.Pixels.subList(tg.Pixels.size() / 2, tg.Pixels.size()));
570                ArrayList<POINT2> poly2LatLons = new ArrayList<>(tg.LatLongs.subList(tg.LatLongs.size() / 2, tg.LatLongs.size()));
571
572                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(poly1Pixels);
573                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(poly1LatLons);
574                tg.Pixels = poly1Pixels;
575                tg.LatLongs = poly1LatLons;
576
577                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(poly2Pixels);
578                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(poly2LatLons);
579                tg.Pixels.addAll(poly2Pixels);
580                tg.LatLongs.addAll(poly2LatLons);
581            }
582            else if (isClosedArea) {
583                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(tg.Pixels);
584                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(tg.LatLongs);
585            }
586
587            //implement meters to feet for altitude labels
588            String altitudeLabel = milStd.getAltitudeMode();
589            if (altitudeLabel == null || altitudeLabel.isEmpty()) {
590                altitudeLabel = "AMSL";
591            }
592            DistanceUnit altitudeUnit = milStd.getAltitudeUnit();
593            if(altitudeUnit == null){
594                altitudeUnit = DistanceUnit.FEET;
595            }
596            DistanceUnit distanceUnit = milStd.getDistanceUnit();
597            if(distanceUnit == null){
598                distanceUnit = DistanceUnit.METERS;
599            }
600
601            String strXAlt = "";
602            //construct the H1 and H2 modifiers for sector from the mss AM, AN, and X arraylists
603            if (lineType == TacticalLines.RANGE_FAN_SECTOR) {
604                ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
605                ArrayList<Double> AN = milStd.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH);
606                ArrayList<Double> X = milStd.getModifiers_AM_AN_X(Modifiers.X_ALTITUDE_DEPTH);
607                if (AM != null) {
608                    String strAM = "";
609                    for (int j = 0; j < AM.size(); j++) {
610                        strAM += Double.toString(AM.get(j));
611                        if (j < AM.size() - 1) {
612                            strAM += ",";
613                        }
614                    }
615                    tg.set_AM(strAM);
616                }
617                if (AN != null) {
618                    String strAN = "";
619                    for (int j = 0; j < AN.size(); j++) {
620                        strAN += AN.get(j);
621                        if (j < AN.size() - 1) {
622                            strAN += ",";
623                        }
624                    }
625                    tg.set_AN(strAN);
626                }
627                if (X != null) {
628                    String strX = "";
629                    for (int j = 0; j < X.size(); j++) {
630                        strXAlt = createAltitudeLabel(X.get(j), altitudeUnit, altitudeLabel);
631                        strX += strXAlt;
632
633                        if (j < X.size() - 1) {
634                            strX += ",";
635                        }
636                    }
637                    tg.set_X(strX);
638                }
639                if (AM != null && AN != null) {
640                    int numSectors = AN.size() / 2;
641                    double left, right, min = 0, max = 0;
642                    //construct left,right,min,max from the arraylists
643                    String strLeftRightMinMax = "";
644                    for (int j = 0; j < numSectors; j++) {
645                        left = AN.get(2 * j);
646                        right = AN.get(2 * j + 1);
647                        if (j + 1 == AM.size()) {
648                            break;
649                        }
650                        min = AM.get(j);
651                        max = AM.get(j + 1);
652                        strLeftRightMinMax += Double.toString(left) + "," + Double.toString(right) + "," + Double.toString(min) + "," + Double.toString(max);
653                        if (j < numSectors - 1) {
654                            strLeftRightMinMax += ",";
655                        }
656
657                    }
658                    int len = strLeftRightMinMax.length();
659                    String c = strLeftRightMinMax.substring(len - 1, len);
660                    if (c.equalsIgnoreCase(",")) {
661                        strLeftRightMinMax = strLeftRightMinMax.substring(0, len - 1);
662                    }
663                    tg.set_LRMM(strLeftRightMinMax);
664                }
665            } else if (lineType == TacticalLines.RADAR_SEARCH) {
666                ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
667                ArrayList<Double> AN = milStd.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH);
668                if (AM != null) {
669                    String strAM = "";
670                    for (int j = 0; j < AM.size() && j < 2; j++) {
671                        strAM += Double.toString(AM.get(j));
672                        if (j < AM.size() - 1) {
673                            strAM += ",";
674                        }
675                    }
676                    tg.set_AM(strAM);
677                }
678                if (AN != null) {
679                    String strAN = "";
680                    for (int j = 0; j < AN.size() && j < 2; j++) {
681                        strAN += AN.get(j);
682                        if (j < AN.size() - 1) {
683                            strAN += ",";
684                        }
685                    }
686                    tg.set_AN(strAN);
687                }
688                if (AM != null && AN != null) {
689                    double left, right, min = 0, max = 0;
690                    //construct left,right,min,max from the arraylists
691                    String strLeftRightMinMax = "";
692                    left = AN.get(0);
693                    right = AN.get(1);
694                    min = AM.get(0);
695                    max = AM.get(1);
696                    strLeftRightMinMax += Double.toString(left) + "," + Double.toString(right) + "," + Double.toString(min) + "," + Double.toString(max);
697                    tg.set_LRMM(strLeftRightMinMax);
698                }
699            }
700            int j = 0;
701            if (lineType == TacticalLines.LAUNCH_AREA || lineType == TacticalLines.DEFENDED_AREA_CIRCULAR || lineType == TacticalLines.SHIP_AOI_CIRCULAR) //geo ellipse
702            {
703                ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
704                ArrayList<Double> AN = milStd.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH);
705                if (AM != null && AM.size() > 1) {
706                    String strAM = AM.get(0).toString(); // major axis
707                    tg.set_AM(strAM);
708                    String strAM1 = AM.get(1).toString(); // minor axis
709                    tg.set_AM1(strAM1);
710                }
711                if (AN != null && AN.size() > 0) {
712                    String strAN = AN.get(0).toString(); // rotation
713                    tg.set_AN(strAN);
714                }
715            }
716            switch (lineType) {
717                case TacticalLines.ROZ:
718                case TacticalLines.AARROZ:
719                case TacticalLines.UAROZ:
720                case TacticalLines.WEZ:
721                case TacticalLines.FEZ:
722                case TacticalLines.JEZ:
723                case TacticalLines.FAADZ:
724                case TacticalLines.HIDACZ:
725                case TacticalLines.MEZ:
726                case TacticalLines.LOMEZ:
727                case TacticalLines.HIMEZ:
728                case TacticalLines.ACA:
729                case TacticalLines.ACA_RECTANGULAR:
730                case TacticalLines.ACA_CIRCULAR:
731                case TacticalLines.WFZ:
732                    ArrayList<Double> X = milStd.getModifiers_AM_AN_X(Modifiers.X_ALTITUDE_DEPTH);
733                    if (X != null && X.size() > 0) {
734                        strXAlt = createAltitudeLabel(X.get(0), altitudeUnit, altitudeLabel);
735                        tg.set_X(strXAlt);
736                    }
737                    if (X != null && X.size() > 1) {
738                        strXAlt = createAltitudeLabel(X.get(1), altitudeUnit, altitudeLabel);
739                        tg.set_X1(strXAlt);
740                    }
741                    break;
742                case TacticalLines.SC:
743                case TacticalLines.MRR:
744                case TacticalLines.SL:
745                case TacticalLines.TC:
746                case TacticalLines.LLTR:
747                case TacticalLines.AC:
748                case TacticalLines.SAAFR:
749                    POINT2 pt = tg.LatLongs.get(0);
750                    Point2D pt2d0 = new Point2D.Double(pt.x, pt.y);
751                    Point2D pt2d0Pixels = converter.GeoToPixels(pt2d0);
752                    POINT2 pt0Pixels = new POINT2(pt2d0Pixels.getX(), pt2d0Pixels.getY());
753
754                    //get some point 10000 meters away from pt
755                    //10000 should work for any scale                    
756                    double dist = 10000;
757                    POINT2 pt2 = mdlGeodesic.geodesic_coordinate(pt, dist, 0);
758                    Point2D pt2d1 = new Point2D.Double(pt2.x, pt2.y);
759                    Point2D pt2d1Pixels = converter.GeoToPixels(pt2d1);
760                    POINT2 pt1Pixels = new POINT2(pt2d1Pixels.getX(), pt2d1Pixels.getY());
761                    //calculate pixels per meter
762                    double distPixels = lineutility.CalcDistanceDouble(pt0Pixels, pt1Pixels);
763                    double pixelsPerMeter = distPixels / dist;
764
765                    ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
766                    if (AM != null) {
767                        String strAM = "";
768                        for (j = 0; j < AM.size(); j++) {
769                            strAM += AM.get(j).toString();
770                            if (j < AM.size() - 1) {
771                                strAM += ",";
772                            }
773                        }
774                        tg.set_AM(strAM);
775                    }
776                    String[] strRadii = null;
777                    //get the widest value
778                    //the current requirement is to use the greatest width as the default width
779                    double maxWidth = 0,
780                    temp = 0;
781                    double maxWidthMeters = 0;
782                    if (tg.get_AM() != null && tg.get_AM().isEmpty() == false) {
783                        strRadii = tg.get_AM().split(",");
784                        if (strRadii.length > 0) {
785                            for (j = 0; j < strRadii.length; j++) {
786                                if (!Double.isNaN(Double.parseDouble(strRadii[j]))) {
787                                    temp = Double.parseDouble(strRadii[j]);
788                                    if (temp > maxWidth) {
789                                        maxWidth = temp;
790                                    }
791                                }
792                            }
793                            maxWidthMeters = maxWidth;
794                            maxWidth *= pixelsPerMeter / 2;
795
796                            for (j = 0; j < tg.Pixels.size(); j++) {
797                                if (strRadii.length > j) {
798                                    if (!Double.isNaN(Double.parseDouble(strRadii[j]))) {
799                                        double pixels = Double.parseDouble(strRadii[j]) * pixelsPerMeter / 2;
800                                        tg.Pixels.get(j).style = (int) pixels;
801                                        tg.LatLongs.get(j).style = (int) pixels;
802                                    } else {
803                                        tg.Pixels.get(j).style = (int) maxWidth;
804                                        tg.LatLongs.get(j).style = (int) maxWidth;
805                                    }
806                                } else {
807                                    tg.Pixels.get(j).style = (int) maxWidth;
808                                    tg.LatLongs.get(j).style = (int) maxWidth;
809                                }
810                            }
811                        }
812                    }
813
814                    maxWidthMeters *= distanceUnit.conversionFactor;
815                    maxWidthMeters *= 10.0;
816                    maxWidthMeters = Math.round(maxWidthMeters);
817                    int tempWidth = (int) maxWidthMeters;
818                    maxWidthMeters = tempWidth / 10.0;
819
820                    tg.set_AM(Double.toString(maxWidthMeters) + " " + distanceUnit.label);
821                    //use X, X1 to set tg.H, tg.H1
822                    X = milStd.getModifiers_AM_AN_X(Modifiers.X_ALTITUDE_DEPTH);
823                    if (X != null && X.size() > 0) {
824                        strXAlt = createAltitudeLabel(X.get(0), altitudeUnit, altitudeLabel);
825                        tg.set_X(strXAlt);
826                    }
827                    if (X != null && X.size() > 1) {
828                        strXAlt = createAltitudeLabel(X.get(1), altitudeUnit, altitudeLabel);
829                        tg.set_X1(strXAlt);
830                    }
831                    break;
832                default:
833                    break;
834            }
835            //circular range fans
836            if (lineType == TacticalLines.RANGE_FAN) {
837                ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
838                ArrayList<Double> X = milStd.getModifiers_AM_AN_X(Modifiers.X_ALTITUDE_DEPTH);
839                String strAM = "";
840                String strX = "";
841                if (AM != null) {
842                    // Range fan circular has a maximum of 3 circles
843                    for (j = 0; j < AM.size() && j < 3; j++) {
844                        strAM += Double.toString(AM.get(j));
845                        if (j < AM.size() - 1) {
846                            strAM += ",";
847                        }
848
849                        if (X != null && j < X.size()) {
850                            strXAlt = createAltitudeLabel(X.get(j), altitudeUnit, altitudeLabel);
851                            strX += strXAlt;
852                            if (j < X.size() - 1) {
853                                strX += ",";
854                            }
855                        }
856                    }
857                }
858                tg.set_AM(strAM);
859                tg.set_X(strX);
860            }
861            switch (lineType) {
862                case TacticalLines.PAA_RECTANGULAR:
863                case TacticalLines.RECTANGULAR_TARGET:
864                case TacticalLines.FSA_RECTANGULAR:
865                case TacticalLines.SHIP_AOI_RECTANGULAR:
866                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
867                case TacticalLines.FFA_RECTANGULAR:
868                case TacticalLines.ACA_RECTANGULAR:
869                case TacticalLines.NFA_RECTANGULAR:
870                case TacticalLines.RFA_RECTANGULAR:
871                case TacticalLines.ATI_RECTANGULAR:
872                case TacticalLines.CFFZ_RECTANGULAR:
873                case TacticalLines.SENSOR_RECTANGULAR:
874                case TacticalLines.CENSOR_RECTANGULAR:
875                case TacticalLines.DA_RECTANGULAR:
876                case TacticalLines.CFZ_RECTANGULAR:
877                case TacticalLines.ZOR_RECTANGULAR:
878                case TacticalLines.TBA_RECTANGULAR:
879                case TacticalLines.TVAR_RECTANGULAR:
880                case TacticalLines.CIRCULAR:
881                case TacticalLines.BDZ:
882                case TacticalLines.FSA_CIRCULAR:
883                case TacticalLines.NOTACK:
884                case TacticalLines.ACA_CIRCULAR:
885                case TacticalLines.FFA_CIRCULAR:
886                case TacticalLines.NFA_CIRCULAR:
887                case TacticalLines.RFA_CIRCULAR:
888                case TacticalLines.PAA_CIRCULAR:
889                case TacticalLines.ATI_CIRCULAR:
890                case TacticalLines.CFFZ_CIRCULAR:
891                case TacticalLines.SENSOR_CIRCULAR:
892                case TacticalLines.CENSOR_CIRCULAR:
893                case TacticalLines.DA_CIRCULAR:
894                case TacticalLines.CFZ_CIRCULAR:
895                case TacticalLines.ZOR_CIRCULAR:
896                case TacticalLines.TBA_CIRCULAR:
897                case TacticalLines.TVAR_CIRCULAR:
898                case TacticalLines.KILLBOXBLUE_CIRCULAR:
899                case TacticalLines.KILLBOXPURPLE_CIRCULAR:
900                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
901                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
902                    ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
903                    if (AM != null && AM.size() > 0) {
904                        String strAM = Double.toString(AM.get(0));
905                        //set width for rectangles or radius for circles
906                        tg.set_AM(strAM);
907                    }
908                    break;
909                default:
910                    break;
911            }
912            if (lineType == TacticalLines.RECTANGULAR || lineType == TacticalLines.CUED_ACQUISITION) {
913                ArrayList<Double> AM = milStd.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
914                ArrayList<Double> AN = milStd.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH);
915                if (AN == null) {
916                    AN = new ArrayList<>();
917                }
918                if (AN.isEmpty()) {
919                    AN.add(0d);
920                }
921
922                if (AM != null && AM.size() > 1) {
923                    String strAM = Double.toString(AM.get(0));    //width
924                    String strAM1 = Double.toString(AM.get(1));     //length
925                    //set width and length in meters for rectangular target
926                    tg.set_AM(strAM);
927                    tg.set_AM1(strAM1);
928                    //set attitude in degrees
929                    String strAN = Double.toString(AN.get(0));
930                    tg.set_AN(strAN);
931                }
932                /*
933                if(AM.size()>2)
934                {
935                    String strH1 = Double.toString(AM.get(2));     //buffer size
936                    tg.set_H1(strH1);
937                }
938                 */
939            }
940        } catch (Exception exc) {
941            ErrorLogger.LogException("clsRenderer", "createTGLightfromMilStdSymbol",
942                    new RendererException("Failed to build multipoint TG for " + milStd.getSymbolID(), exc));
943        }
944        return tg;
945    }
946
947    private static String createAltitudeLabel(double distance, DistanceUnit altitudeUnit, String altitudeLabel){
948        double conversionFactor;
949
950        // if using "FL" (Flight Level) for altitudeLabel, override conversion factor to avoid potential user error with altitudeUnit
951        if (altitudeLabel.equals("FL")) {
952            conversionFactor = DistanceUnit.FLIGHT_LEVEL.conversionFactor;
953        } else {
954            conversionFactor = altitudeUnit.conversionFactor;
955        }
956
957        // Truncate the result
958        double result = distance * conversionFactor;
959        result *= 10.0;
960        result = Math.round(result);
961        int tempResult = (int) result;
962        int truncatedResult = tempResult / 10;
963        // MIL-STD-2525D says altitude/depth must be an integer
964
965        // Simplifies labels of "0 units AGL" to "GL" (Ground Level) and "0 units AMSL/BMSL" to "MSL" (Mean Sea Level)
966        // as permitted by MIL-STD-2525D 5.3.7.5.1.
967        // Also works for "0 units GL" and "0 units MSL", which are improperly labeled but can be understood to mean the same thing.
968        if (truncatedResult == 0) {
969            if (altitudeLabel.equals("AGL") || altitudeLabel.equals("GL")) {
970                return "GL";
971            }
972            if (altitudeLabel.equals("AMSL") || altitudeLabel.equals("BMSL") || altitudeLabel.equals("MSL")) {
973                return "MSL";
974            }
975        }
976
977        // Flight level is a special altitude displayed as "FL ###" where ### are 3 digits representing hundreds of feet.
978        if (altitudeLabel.equals("FL")) {
979            return "FL " + String.format("%03d", truncatedResult);
980        }
981
982        return truncatedResult + " " + altitudeUnit.label + " " + altitudeLabel;
983    }
984
985    /**
986     * @deprecated @param milStd
987     * @param converter
988     * @param computeChannelPt
989     * @return
990     */
991    public static TGLight createTGLightFromMilStdSymbol(MilStdSymbol milStd,
992            IPointConversion converter, Boolean computeChannelPt) {
993        TGLight tg = new TGLight();
994        try {
995            String symbolId = milStd.getSymbolID();
996            tg.set_SymbolId(symbolId);
997            String status = tg.get_Status();
998            if (status != null && status.equals("A")) {
999                //lineStyle=GraphicProperties.LINE_TYPE_DASHED;
1000                tg.set_LineStyle(1);
1001            }
1002            tg.set_VisibleModifiers(true);
1003            //set tg latlongs and pixels
1004            setClientCoords(milStd, tg);
1005            //build tg.Pixels
1006            tg.Pixels = clsUtility.LatLongToPixels(tg.LatLongs, converter);
1007            tg.set_Font(new Font("Arial", Font.PLAIN, 12));
1008            tg.set_FillColor(milStd.getFillColor());
1009            tg.set_LineColor(milStd.getLineColor());
1010            tg.set_LineThickness(milStd.getLineWidth());
1011            tg.set_TexturePaint(milStd.getFillStyle());
1012            tg.set_patternScale(milStd.getPatternScale());
1013            tg.set_FontBackColor(Color.WHITE);
1014            tg.set_TextColor(milStd.getTextColor());
1015
1016//            tg.set_DTG(milStd.getModifier(Modifiers.W_DTG_1));
1017//            tg.set_DTG1(milStd.getModifier(Modifiers.W1_DTG_2));
1018//            tg.set_H(milStd.getModifier(Modifiers.H_ADDITIONAL_INFO_1));
1019//            tg.set_H1(milStd.getModifier(Modifiers.H1_ADDITIONAL_INFO_2));
1020//            tg.set_H2(milStd.getModifier(Modifiers.H2_ADDITIONAL_INFO_3));
1021//            tg.set_Name(milStd.getModifier(Modifiers.T_UNIQUE_DESIGNATION_1));
1022//            tg.set_T1(milStd.getModifier(Modifiers.T1_UNIQUE_DESIGNATION_2));
1023//            tg.set_Location(milStd.getModifier(Modifiers.Y_LOCATION));
1024//            tg.set_N(Modifiers.N_HOSTILE);
1025            if (milStd.getModifier(Modifiers.W_DTG_1) != null) {
1026                tg.set_DTG(milStd.getModifier(Modifiers.W_DTG_1));
1027            }
1028            if (milStd.getModifier(Modifiers.W1_DTG_2) != null) {
1029                tg.set_DTG1(milStd.getModifier(Modifiers.W1_DTG_2));
1030            }
1031            if (milStd.getModifier(Modifiers.H_ADDITIONAL_INFO_1) != null) {
1032                tg.set_H(milStd.getModifier(Modifiers.H_ADDITIONAL_INFO_1));
1033            }
1034            if (milStd.getModifier(Modifiers.H1_ADDITIONAL_INFO_2) != null) {
1035                tg.set_H1(milStd.getModifier(Modifiers.H1_ADDITIONAL_INFO_2));
1036            }
1037            if (milStd.getModifier(Modifiers.H2_ADDITIONAL_INFO_3) != null) {
1038                tg.set_H2(milStd.getModifier(Modifiers.H2_ADDITIONAL_INFO_3));
1039            }
1040            if (milStd.getModifier(Modifiers.T_UNIQUE_DESIGNATION_1) != null) {
1041                tg.set_Name(milStd.getModifier(Modifiers.T_UNIQUE_DESIGNATION_1));
1042            }
1043            if (milStd.getModifier(Modifiers.T1_UNIQUE_DESIGNATION_2) != null) {
1044                tg.set_T1(milStd.getModifier(Modifiers.T1_UNIQUE_DESIGNATION_2));
1045            }
1046            if (milStd.getModifier(Modifiers.V_EQUIP_TYPE) != null) {
1047                tg.set_V(milStd.getModifier(Modifiers.V_EQUIP_TYPE));
1048            }
1049            if (milStd.getModifier(Modifiers.AS_COUNTRY) != null) {
1050                tg.set_AS(milStd.getModifier(Modifiers.AS_COUNTRY));
1051            }
1052            if (milStd.getModifier(Modifiers.AP_TARGET_NUMBER) != null) {
1053                tg.set_AP(milStd.getModifier(Modifiers.AP_TARGET_NUMBER));
1054            }
1055            if (milStd.getModifier(Modifiers.Y_LOCATION) != null) {
1056                tg.set_Location(milStd.getModifier(Modifiers.Y_LOCATION));
1057            }
1058            if (milStd.getModifier(Modifiers.N_HOSTILE) != null) {
1059                tg.set_N(milStd.getModifier(Modifiers.N_HOSTILE));
1060            }
1061
1062            //int lineType=CELineArray.CGetLinetypeFromString(tg.get_SymbolId());
1063            int lineType = armyc2.c5isr.JavaTacticalRenderer.clsUtility.GetLinetypeFromString(symbolId);
1064            boolean isClosedArea = armyc2.c5isr.JavaTacticalRenderer.clsUtility.isClosedPolygon(lineType);
1065
1066            if (isClosedArea) {
1067                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(tg.Pixels);
1068                armyc2.c5isr.JavaTacticalRenderer.clsUtility.ClosePolygon(tg.LatLongs);
1069            }
1070
1071            //these channels need a channel point added
1072            if (computeChannelPt) {
1073                switch (lineType) {
1074                    case TacticalLines.CATK:
1075                    case TacticalLines.CATKBYFIRE:
1076                    case TacticalLines.AAAAA:
1077                    case TacticalLines.AIRAOA:
1078                    case TacticalLines.MAIN:
1079                    case TacticalLines.SPT:
1080                    case TacticalLines.FRONTAL_ATTACK:
1081                    case TacticalLines.TURNING_MOVEMENT:
1082                    case TacticalLines.MOVEMENT_TO_CONTACT:
1083                        POINT2 ptPixels = armyc2.c5isr.JavaTacticalRenderer.clsUtility.ComputeLastPoint(tg.Pixels);
1084                        tg.Pixels.add(ptPixels);
1085                        //Point pt = clsUtility.POINT2ToPoint(ptPixels);
1086                        Point2D pt = new Point2D.Double(ptPixels.x, ptPixels.y);
1087                        //in case it needs the corresponding geo point
1088                        Point2D ptGeo2d = converter.PixelsToGeo(pt);
1089                        POINT2 ptGeo = clsUtility.Point2DToPOINT2(ptGeo2d);
1090                        tg.LatLongs.add(ptGeo);
1091                        //}
1092                        break;
1093                    default:
1094                        break;
1095                }
1096            }
1097        } catch (Exception exc) {
1098            ErrorLogger.LogException("clsRenderer", "createTGLightfromMilStdSymbol",
1099                    new RendererException("Failed to build multipoint TG for " + milStd.getSymbolID(), exc));
1100        }
1101        return tg;
1102    }
1103
1104    private static void Shape2ToShapeInfo(ArrayList<ShapeInfo> shapeInfos, ArrayList<Shape2> shapes) {
1105        try {
1106            int j = 0;
1107            Shape2 shape = null;
1108            if (shapes == null || shapeInfos == null || shapes.size() == 0) {
1109                return;
1110            }
1111
1112            for (j = 0; j < shapes.size(); j++) {
1113                shape = shapes.get(j);
1114                shapeInfos.add((ShapeInfo) shape);
1115            }
1116        } catch (Exception exc) {
1117            ErrorLogger.LogException("clsRenderer", "Shape2ToShapeInfo",
1118                    new RendererException("Failed to build ShapeInfo ArrayList", exc));
1119        }
1120    }
1121
1122    /**
1123     * Added function to handle when coords or display area spans IDL but not
1124     * both, it prevents the symbol from rendering if the bounding rectangles
1125     * don't intersect.
1126     *
1127     * @param tg
1128     * @param converter
1129     * @param clipArea
1130     * @return
1131     */
1132    public static boolean intersectsClipArea(TGLight tg, IPointConversion converter, Object clipArea)
1133    {
1134        boolean result=false;
1135        try
1136        {
1137            if (clipArea==null || tg.LatLongs.size() < 2)
1138                return true;
1139            Rectangle2D clipBounds = null;
1140            ArrayList<Point2D> clipPoints = null;
1141            
1142//            if (clipArea != null) {
1143//                if (clipArea.getClass().isAssignableFrom(Rectangle2D.Double.class)) {
1144//                    clipBounds = (Rectangle2D.Double) clipArea;
1145//                } else if (clipArea.getClass().isAssignableFrom(Rectangle.class)) {
1146//                    clipBounds = (Rectangle2D) clipArea;
1147//                } else if (clipArea.getClass().isAssignableFrom(ArrayList.class)) {
1148//                    clipPoints = (ArrayList<Point2D>) clipArea;
1149//                }
1150//            }
1151            if (clipArea != null) {
1152                if (clipArea.getClass().isAssignableFrom(Rectangle2D.Double.class)) {
1153                    clipBounds = (Rectangle2D.Double) clipArea;
1154                } else if (clipArea.getClass().isAssignableFrom(Rectangle.class)) {
1155                    Rectangle rectx = (Rectangle) clipArea;
1156                    clipBounds = new Rectangle2D.Double(rectx.x, rectx.y, rectx.width, rectx.height);
1157                } else if (clipArea.getClass().isAssignableFrom(ArrayList.class)) {
1158                    clipPoints = (ArrayList<Point2D>) clipArea;                    
1159                    //double x0=clipPoints.get(0).getX(),y0=clipPoints.get(0).getY();
1160                    //double w=clipPoints.get(1).getX()-x0,h=clipPoints.get(3).getY()-y0;
1161                    //clipBounds = new Rectangle2D.Double(x0, y0, w, h);                    
1162                    clipBounds=clsUtility.getMBR(clipPoints);
1163                }
1164            }
1165            //assumes we are using clipBounds
1166            int j = 0;
1167            double x = clipBounds.getMinX();
1168            double y = clipBounds.getMinY();
1169            double width = clipBounds.getWidth();
1170            double height = clipBounds.getHeight();
1171            POINT2 tl = new POINT2(x, y);
1172            POINT2 br = new POINT2(x + width, y + height);
1173            tl = clsUtility.PointPixelsToLatLong(tl, converter);
1174            br = clsUtility.PointPixelsToLatLong(br, converter);
1175            //the latitude range
1176            //boolean ptInside = false, ptAbove = false, ptBelow = false;
1177            double coordsLeft = tg.LatLongs.get(0).x;
1178            double coordsRight = coordsLeft;
1179            double coordsTop=tg.LatLongs.get(0).y;
1180            double coordsBottom=coordsTop;
1181            boolean intersects=false;
1182            double minx=tg.LatLongs.get(0).x,maxx=minx,maxNegX=0;
1183            for (j = 0; j < tg.LatLongs.size(); j++)
1184            {                
1185                POINT2 pt=tg.LatLongs.get(j);
1186                if (pt.x < minx)
1187                    minx = pt.x;
1188                if (pt.x > maxx)
1189                    maxx = pt.x;
1190                if(maxNegX==0 && pt.x<0)
1191                    maxNegX=pt.x;
1192                if(maxNegX<0 && pt.x<0 && pt.x>maxNegX)
1193                    maxNegX=pt.x;
1194                if (pt.y < coordsBottom)
1195                    coordsBottom = pt.y;
1196                if (pt.y > coordsTop)
1197                    coordsTop = pt.y;                
1198            }
1199            boolean coordSpanIDL = false;
1200            if(maxx==180 || minx==-180)
1201                coordSpanIDL=true;
1202            if(maxx-minx>=180)
1203            {
1204                coordSpanIDL=true;
1205                coordsLeft=maxx;
1206                coordsRight=maxNegX;
1207            }else
1208            {
1209                coordsLeft=minx;
1210                coordsRight=maxx;
1211            }
1212            //if(canClipPoints)
1213            //{                
1214                if(br.y<=coordsBottom && coordsBottom <= tl.y)
1215                    intersects=true;
1216                else if(coordsBottom<=br.y && br.y <=coordsTop)
1217                    intersects=true;
1218                else
1219                    return false;
1220            //}
1221            //if it gets this far then the latitude ranges intersect
1222            //re-initialize intersects for the longitude ranges
1223            intersects=false;
1224            //the longitude range
1225            //the min and max coords longitude
1226            boolean boxSpanIDL = false;
1227            //boolean coordSpanIDL = false;
1228            if(tl.x==180 || tl.x==-180 || br.x==180 || br.x==-180)
1229                boxSpanIDL=true;
1230            else if (Math.abs(br.x - tl.x) > 180)
1231                boxSpanIDL = true;
1232            
1233//            if (coordsRight - coordsLeft > 180)
1234//            {
1235//                double temp = coordsLeft;
1236//                coordsLeft = coordsRight;
1237//                coordsRight = temp;
1238//                coordSpanIDL=true;
1239//            }
1240            //boolean intersects=false;
1241            if(coordSpanIDL && boxSpanIDL)
1242                intersects=true;
1243            else if(!coordSpanIDL && !boxSpanIDL)   //was && canclipPoints
1244            {
1245                if(coordsLeft<=tl.x && tl.x<=coordsRight)
1246                    intersects=true;
1247                if(coordsLeft<=br.x && br.x<=coordsRight)
1248                    intersects=true;
1249                if(tl.x<=coordsLeft && coordsLeft<=br.x)
1250                    intersects=true;
1251                if(tl.x<=coordsRight && coordsRight<=br.x)
1252                    intersects=true;
1253            }
1254            else if(!coordSpanIDL && boxSpanIDL)    //box spans IDL and coords do not
1255            {   
1256                if(tl.x<coordsRight && coordsRight<180)
1257                    intersects=true;
1258                if(-180<coordsLeft && coordsLeft<br.x)
1259                    intersects=true;
1260            }
1261            else if(coordSpanIDL && !boxSpanIDL)    //coords span IDL and box does not
1262            {   
1263                if(coordsLeft<br.x && br.x<180)
1264                    intersects=true;
1265                if(-180<tl.x && tl.x<coordsRight)
1266                    intersects=true;
1267            }
1268            return intersects;
1269            
1270        }
1271        catch (Exception exc) {
1272            ErrorLogger.LogException("clsRenderer", "intersectsClipArea",
1273                    new RendererException("Failed inside intersectsClipArea", exc));
1274        }    
1275        return result;
1276    }
1277
1278    /**
1279     * Adds Feint, decoy, or dummy indicator to shapes. Does not check if tactical graphic should have indicator
1280     */
1281    private static void addFDI(TGLight tg, ArrayList<Shape2> shapes) {
1282        try {
1283            MSInfo msi = MSLookup.getInstance().getMSLInfo(tg.get_SymbolId());
1284            final int drawRule = msi != null ? msi.getDrawRule() : -1;
1285            final int lineType = tg.get_LineType();
1286
1287            if (lineType == TacticalLines.MAIN) {
1288                // Only Axis of Advance with arrowhead in a different location
1289                ArrayList<POINT2> points = shapes.get(1).getPoints();
1290                POINT2 ptA = new POINT2(points.get(points.size() - 3));
1291                POINT2 ptB = new POINT2(points.get(points.size() - 8));
1292                POINT2 ptC = new POINT2(points.get(points.size() - 7));
1293                shapes.add(DISMSupport.getFDIShape(tg, ptA, ptB, ptC));
1294            } else if (drawRule == DrawRules.AXIS1 || drawRule == DrawRules.AXIS2) {
1295                // Axis of Advance symbols
1296                ArrayList<POINT2> points = shapes.get(0).getPoints();
1297                POINT2 ptA = new POINT2(points.get(points.size() / 2 - 1));
1298                POINT2 ptB = new POINT2(points.get(points.size() / 2));
1299                POINT2 ptC = new POINT2(points.get(points.size() / 2 + 1));
1300                shapes.add(DISMSupport.getFDIShape(tg, ptA, ptB, ptC));
1301            }
1302            // Direction of attack symbols
1303            else if (lineType == TacticalLines.DIRATKAIR) {
1304                ArrayList<POINT2> points = shapes.get(2).getPoints();
1305                POINT2 ptA = new POINT2(points.get(0));
1306                POINT2 ptB = new POINT2(points.get(1));
1307                POINT2 ptC = new POINT2(points.get(2));
1308                shapes.add(DISMSupport.getFDIShape(tg, ptA, ptB, ptC));
1309            } else if (lineType == TacticalLines.DIRATKGND) {
1310                ArrayList<POINT2> points = shapes.get(1).getPoints();
1311                POINT2 ptA = new POINT2(points.get(7));
1312                POINT2 ptB = new POINT2(points.get(4));
1313                POINT2 ptC = new POINT2(points.get(9));
1314                shapes.add(DISMSupport.getFDIShape(tg, ptA, ptB, ptC));
1315            } else if (lineType == TacticalLines.DIRATKSPT || lineType == TacticalLines.INFILTRATION) {
1316                ArrayList<POINT2> points = shapes.get(1).getPoints();
1317                POINT2 ptA = new POINT2(points.get(0));
1318                POINT2 ptB = new POINT2(points.get(1));
1319                POINT2 ptC = new POINT2(points.get(2));
1320                shapes.add(DISMSupport.getFDIShape(tg, ptA, ptB, ptC));
1321            }
1322            else if (lineType == TacticalLines.EXPLOIT) {
1323                ArrayList<POINT2> points = shapes.get(1).getPoints();
1324                POINT2 ptA = new POINT2(points.get(0));
1325                POINT2 ptB = new POINT2(points.get(1));
1326                POINT2 ptC = new POINT2(points.get(2));
1327                shapes.add(DISMSupport.getFDIShape(tg, ptA, ptB, ptC));
1328            }
1329            else {
1330                // Shape has no arrow. Put on top of shape
1331                POINT2 firstPoint = shapes.get(0).getPoints().get(0);
1332                POINT2 ptUl = new POINT2(firstPoint);
1333                POINT2 ptUr = new POINT2(firstPoint);
1334                POINT2 ptLr = new POINT2(firstPoint);
1335                POINT2 ptLl = new POINT2(firstPoint);
1336                clsUtility.GetMBR(shapes, ptUl, ptUr, ptLr, ptLl);
1337                shapes.add(DISMSupport.getFDIShape(tg, ptUl, ptUr));
1338            }
1339        } catch (Exception exc) {
1340            ErrorLogger.LogException(_className, "addFDI", new RendererException("failed inside addFDI", exc));
1341        }
1342    }
1343
1344    /**
1345     * @param mss
1346     * @param converter
1347     * @param clipArea
1348     * @param context
1349     * @deprecated context not used
1350     */
1351    public static void renderWithPolylines(MilStdSymbol mss,
1352            IPointConversion converter,
1353            Object clipArea,
1354            Context context) {
1355        try {
1356            TGLight tg = clsRenderer.createTGLightFromMilStdSymbol(mss, converter);
1357            ArrayList<ShapeInfo> shapeInfos = new ArrayList();
1358            ArrayList<ShapeInfo> modifierShapeInfos = new ArrayList();
1359            if (intersectsClipArea(tg, converter, clipArea)) {
1360                render_GE(tg, shapeInfos, modifierShapeInfos, converter, clipArea, context);
1361            }
1362            mss.setSymbolShapes(shapeInfos);
1363            mss.setModifierShapes(modifierShapeInfos);
1364        } catch (Exception exc) {
1365            ErrorLogger.LogException("clsRenderer", "renderWithPolylines",
1366                    new RendererException("Failed inside renderWithPolylines", exc));
1367        }
1368    }
1369
1370    /**
1371     * GoogleEarth renderer uses polylines for rendering
1372     *
1373     * @param mss MilStdSymbol object
1374     * @param converter the geographic to pixels coordinate converter
1375     * @param clipArea the clip bounds
1376     */
1377    public static void renderWithPolylines(MilStdSymbol mss,
1378            IPointConversion converter,
1379            Object clipArea) {
1380        try {
1381            TGLight tg = clsRenderer.createTGLightFromMilStdSymbol(mss, converter);
1382            ArrayList<ShapeInfo> shapeInfos = new ArrayList();
1383            ArrayList<ShapeInfo> modifierShapeInfos = new ArrayList();
1384            if (intersectsClipArea(tg, converter, clipArea)) {
1385                render_GE(tg, shapeInfos, modifierShapeInfos, converter, clipArea);
1386            }
1387            mss.setSymbolShapes(shapeInfos);
1388            mss.setModifierShapes(modifierShapeInfos);
1389            mss.set_WasClipped(tg.get_WasClipped());
1390        } catch (Exception exc) {
1391            ErrorLogger.LogException("clsRenderer", "renderWithPolylines",
1392                    new RendererException("Failed inside renderWithPolylines", exc));
1393        }
1394    }
1395
1396    /**
1397     * See render_GE below for comments
1398     *
1399     * @param tg
1400     * @param shapeInfos
1401     * @param modifierShapeInfos
1402     * @param converter
1403     * @param clipArea
1404     * @param context test android-gradle
1405     * @deprecated context not used
1406     */
1407    public static void render_GE(TGLight tg,
1408            ArrayList<ShapeInfo> shapeInfos,
1409            ArrayList<ShapeInfo> modifierShapeInfos,
1410            IPointConversion converter,
1411            Object clipArea,
1412            Context context) //was Rectangle2D
1413    {
1414        render_GE(tg, shapeInfos, modifierShapeInfos, converter, clipArea);
1415    }
1416
1417    /**
1418     * Google Earth renderer: Called by mapfragment-demo This is the public
1419     * interface for Google Earth renderer assumes tg.Pixels is filled assumes
1420     * the caller instantiated the ShapeInfo arrays
1421     *
1422     * @param tg tactical graphic
1423     * @param shapeInfos symbol ShapeInfo array
1424     * @param modifierShapeInfos modifier ShapeInfo array
1425     * @param converter geographic to pixels coordinate converter
1426     * @param clipArea clipping bounds in pixels
1427     */
1428    public static void render_GE(TGLight tg,
1429            ArrayList<ShapeInfo> shapeInfos,
1430            ArrayList<ShapeInfo> modifierShapeInfos,
1431            IPointConversion converter,
1432            Object clipArea) {
1433        try {
1434            reversePointsRevD(tg);
1435
1436            Rectangle2D clipBounds = null;
1437            CELineArray.setClient("ge");
1438//            ArrayList<POINT2> origPixels = null;
1439//            ArrayList<POINT2> origLatLongs = null;
1440//            if (clsUtilityGE.segmentColorsSet(tg)) {
1441//                origPixels=lineutility.getDeepCopy(tg.Pixels);
1442//                origLatLongs=lineutility.getDeepCopy(tg.LatLongs);
1443//            }
1444            ArrayList<POINT2> origFillPixels = lineutility.getDeepCopy(tg.Pixels);
1445
1446            if (tg.get_LineType() == TacticalLines.LC)
1447                armyc2.c5isr.JavaTacticalRenderer.clsUtility.SegmentLCPoints(tg, converter);
1448
1449//            boolean shiftLines = Channels.getShiftLines();
1450//            if (shiftLines) {
1451//                String affiliation = tg.get_Affiliation();
1452//                Channels.setAffiliation(affiliation);
1453//            }
1454            //CELineArray.setMinLength(2.5);    //2-27-2013
1455            ArrayList<Point2D> clipPoints = null;
1456            if (clipArea != null) {
1457                if (clipArea.getClass().isAssignableFrom(Rectangle2D.Double.class)) {
1458                    clipBounds = (Rectangle2D.Double) clipArea;
1459                } else if (clipArea.getClass().isAssignableFrom(Rectangle.class)) {
1460                    Rectangle rectx = (Rectangle) clipArea;
1461                    clipBounds = new Rectangle2D.Double(rectx.x, rectx.y, rectx.width, rectx.height);
1462                } else if (clipArea.getClass().isAssignableFrom(ArrayList.class)) {
1463                    clipPoints = (ArrayList<Point2D>) clipArea;
1464                }
1465            }
1466            double zoomFactor = clsUtilityGE.getZoomFactor(clipBounds, clipPoints, tg.Pixels);
1467            //add sub-section to test clipArea if client passes the rectangle
1468            boolean useClipPoints = false;    //currently not used
1469            if (useClipPoints == true && clipBounds != null) {
1470                double x = clipBounds.getMinX();
1471                double y = clipBounds.getMinY();
1472                double width = clipBounds.getWidth();
1473                double height = clipBounds.getHeight();
1474                clipPoints = new ArrayList();
1475                clipPoints.add(new Point2D.Double(x, y));
1476                clipPoints.add(new Point2D.Double(x + width, y));
1477                clipPoints.add(new Point2D.Double(x + width, y + height));
1478                clipPoints.add(new Point2D.Double(x, y + height));
1479                clipPoints.add(new Point2D.Double(x, y));
1480                clipBounds = null;
1481            }
1482            //end section
1483
1484            if (tg.get_Client() == null || tg.get_Client().isEmpty()) {
1485                tg.set_client("ge");
1486            }
1487
1488            clsUtility.RemoveDuplicatePoints(tg);
1489
1490            int linetype = tg.get_LineType();
1491            if (linetype < 0) {
1492                linetype = armyc2.c5isr.JavaTacticalRenderer.clsUtility.GetLinetypeFromString(tg.get_SymbolId());
1493                //clsUtilityCPOF.SegmentGeoPoints(tg, converter);
1494                tg.set_LineType(linetype);
1495            }
1496
1497            Boolean isTextFlipped = false;
1498            ArrayList<Shape2> shapes = null;   //use this to collect all the shapes
1499            clsUtilityGE.setSplineLinetype(tg);
1500
1501            clsUtilityCPOF.SegmentGeoPoints(tg, converter, zoomFactor);
1502            if (clipBounds != null || clipPoints != null) {
1503                if (clsUtilityCPOF.canClipPoints(tg)) {
1504                    //check assignment
1505                    if (clipBounds != null) {
1506                        clsClipPolygon2.ClipPolygon(tg, clipBounds);
1507                    } else if (clipPoints != null) {
1508                        clsClipQuad.ClipPolygon(tg, clipPoints);
1509                    }
1510
1511                    clsUtilityGE.removeTrailingPoints(tg, clipArea);
1512                    tg.LatLongs = clsUtility.PixelsToLatLong(tg.Pixels, converter);
1513                }
1514            }
1515
1516            //if MSR segment data set use original pixels unless tg.Pixels is empty from clipping
1517//            if (origPixels != null) {
1518//                if (tg.Pixels.isEmpty()) {
1519//                    return;
1520//                } else {
1521//                    tg.Pixels = origPixels;
1522//                    tg.LatLongs = origLatLongs;
1523//                    clipArea = null;
1524//                }
1525//            }
1526            armyc2.c5isr.JavaTacticalRenderer.clsUtility.InterpolatePixels(tg);
1527
1528            tg.modifiers = new ArrayList();
1529            BufferedImage bi = new BufferedImage(8, 8, BufferedImage.TYPE_INT_ARGB);
1530            Graphics2D g2d = bi.createGraphics();
1531            g2d.setFont(tg.get_Font());
1532            Modifier2.AddModifiersGeo(tg, g2d, clipArea, converter);
1533
1534            clsUtilityCPOF.FilterPoints2(tg, converter);
1535            armyc2.c5isr.JavaTacticalRenderer.clsUtility.FilterVerticalSegments(tg);
1536            clsUtility.FilterAXADPoints(tg, converter);
1537            clsUtilityCPOF.ClearPixelsStyle(tg);
1538
1539            ArrayList<Shape2> linesWithFillShapes = null;
1540
1541            ArrayList<POINT2> savePixels = tg.Pixels;
1542            tg.Pixels = origFillPixels;
1543
1544            //check assignment
1545            if (clipBounds != null) {
1546                linesWithFillShapes = clsClipPolygon2.LinesWithFill(tg, clipBounds);
1547            } else if (clipPoints != null) {
1548                linesWithFillShapes = clsClipQuad.LinesWithFill(tg, clipPoints);
1549            } else if (clipArea == null) {
1550                linesWithFillShapes = clsClipPolygon2.LinesWithFill(tg, null);
1551            }
1552
1553            tg.Pixels = savePixels;
1554
1555            ArrayList<Shape2> rangeFanFillShapes = null;
1556            //do not fill the original shapes for circular range fans
1557            int savefillStyle = tg.get_FillStyle();
1558            if (linetype == TacticalLines.RANGE_FAN) {
1559                tg.set_Fillstyle(0);
1560            }
1561
1562            //check assignment (pass which clip object is not null)
1563            if (clipBounds != null) {
1564                shapes = clsRenderer2.GetLineArray(tg, converter, isTextFlipped, clipBounds); //takes clip object           
1565            } else if (clipPoints != null) {
1566                shapes = clsRenderer2.GetLineArray(tg, converter, isTextFlipped, clipPoints);
1567            } else if (clipArea == null) {
1568                shapes = clsRenderer2.GetLineArray(tg, converter, isTextFlipped, null);
1569            }
1570
1571            // Add Feint, decoy, or dummy indicator
1572            if (shapes != null
1573                    && SymbolID.getSymbolSet(tg.get_SymbolId()) == SymbolID.SymbolSet_ControlMeasure
1574                    && SymbolUtilities.hasFDI(tg.get_SymbolId())) {
1575                addFDI(tg, shapes);
1576            }
1577
1578            switch (linetype) {
1579                case TacticalLines.RANGE_FAN:
1580                case TacticalLines.RANGE_FAN_SECTOR:
1581                case TacticalLines.RADAR_SEARCH:
1582                    if (tg.get_FillColor() == null || tg.get_FillColor().getAlpha() < 2) {
1583                        break;
1584                    }
1585                    TGLight tg1 = clsUtilityCPOF.GetCircularRangeFanFillTG(tg);
1586                    tg1.set_Fillstyle(savefillStyle);
1587                    tg1.set_SymbolId(tg.get_SymbolId());
1588                    //check assignment (pass which clip object is not null)
1589                    if (clipBounds != null) {
1590                        rangeFanFillShapes = clsRenderer2.GetLineArray(tg1, converter, isTextFlipped, clipBounds);
1591                    } else if (clipPoints != null) {
1592                        rangeFanFillShapes = clsRenderer2.GetLineArray(tg1, converter, isTextFlipped, clipPoints);
1593                    } else if (clipArea == null) {
1594                        rangeFanFillShapes = clsRenderer2.GetLineArray(tg1, converter, isTextFlipped, null);
1595                    }
1596
1597                    if (rangeFanFillShapes != null) {
1598                        if (shapes == null) {
1599                            System.out.println("shapes is null");
1600                            break;
1601                        } else {
1602                            shapes.addAll(0, rangeFanFillShapes);
1603                        }
1604
1605                    }
1606                    break;
1607                default:
1608                    clsRenderer2.getAutoshapeFillShape(tg, shapes);
1609                    break;
1610            }
1611            //end section
1612
1613            //undo any fillcolor for lines with fill
1614            clsUtilityCPOF.LinesWithSeparateFill(tg.get_LineType(), shapes);
1615            clsClipPolygon2.addAbatisFill(tg, shapes);
1616
1617            //if this line is commented then the extra line in testbed goes away
1618            if (shapes != null && linesWithFillShapes != null && linesWithFillShapes.size() > 0) {
1619                shapes.addAll(0, linesWithFillShapes);
1620            }
1621
1622            if (clsUtilityCPOF.canClipPoints(tg) == false && clipBounds != null) {
1623                shapes = clsUtilityCPOF.postClipShapes(tg, shapes, clipBounds);
1624            } else if (clsUtilityCPOF.canClipPoints(tg) == false && clipPoints != null) {
1625                shapes = clsUtilityCPOF.postClipShapes(tg, shapes, clipPoints);
1626            }
1627            resolvePostClippedShapes(tg,shapes);
1628            //returns early if textSpecs are null
1629            //currently the client is ignoring these
1630            if (modifierShapeInfos != null) {
1631                ArrayList<Shape2> textSpecs = new ArrayList();
1632                Modifier2.DisplayModifiers2(tg, g2d, textSpecs, isTextFlipped, converter);
1633                Shape2ToShapeInfo(modifierShapeInfos, textSpecs);
1634            }
1635            Shape2ToShapeInfo(shapeInfos, shapes);
1636            clsUtility.addHatchFills(tg, shapeInfos);
1637
1638            //check assignment (pass which clip object is not null)
1639            if (clipBounds != null) {
1640                clsUtilityGE.SetShapeInfosPolylines(tg, shapeInfos, clipBounds);//takes a clip object            
1641            } else if (clipPoints != null) {
1642                clsUtilityGE.SetShapeInfosPolylines(tg, shapeInfos, clipPoints);
1643            } else if (clipArea == null) {
1644                clsUtilityGE.SetShapeInfosPolylines(tg, shapeInfos, null);
1645            }
1646        } catch (Exception exc) {
1647            ErrorLogger.LogException(_className, "render_GE",
1648                    new RendererException("Failed inside render_GE", exc));
1649
1650        }
1651    }
1652    /**
1653     * creates a shape for known symbols. The intent is to use client points for
1654     * the shape and is intended for use with ellipse. If hatch &gt; 1 it creates 2 shapes
1655     * one for the hatch pattern, the second one is for the outline.
1656     *
1657     * @param milStd
1658     * @param ipc
1659     * @param clipArea
1660     * @param shapeType
1661     * @param lineColor
1662     * @param fillColor
1663     * @param hatch
1664     */
1665    public static void render_Shape(MilStdSymbol milStd,
1666            IPointConversion ipc,
1667            Object clipArea,
1668            int shapeType,
1669            Color lineColor,
1670            Color fillColor,
1671            int hatch) {
1672        try {
1673            Rectangle2D clipBounds = null;
1674            //CELineArray.setClient("ge");
1675            ArrayList<Point2D> clipPoints = null;
1676
1677            if (clipArea != null) {
1678                if (clipArea.getClass().isAssignableFrom(Rectangle2D.Double.class)) {
1679                    clipBounds = (Rectangle2D.Double) clipArea;
1680                } else if (clipArea.getClass().isAssignableFrom(Rectangle.class)) {
1681                    clipBounds = (Rectangle2D) clipArea;
1682                } else if (clipArea.getClass().isAssignableFrom(ArrayList.class)) {
1683                    clipPoints = (ArrayList<Point2D>) clipArea;
1684                }
1685            }
1686            
1687            //can't use following line because it resets the pixels
1688            //TGLight tg = createTGLightFromMilStdSymbol(milStd, ipc);
1689            TGLight tg = new TGLight();
1690            tg.set_SymbolId(milStd.getSymbolID());
1691            //tg.set_VisibleModifiers(true);
1692            //set tg latlongs and pixels
1693            setClientCoords(milStd, tg);
1694            //build tg.Pixels
1695            tg.Pixels = clsUtility.LatLongToPixels(tg.LatLongs, ipc);            
1696            
1697            //int fillStyle = milStd.getPatternFillType();
1698            Shape2 shape = new Shape2(shapeType);
1699            shape.setFillColor(fillColor);
1700            if (lineColor != null) {
1701                shape.setLineColor(lineColor);
1702                shape.setStroke(new BasicStroke(milStd.getLineWidth()));
1703            }
1704            //the client has already set the coordinates for the shape
1705            POINT2 pt;
1706            for (int j = 0; j < tg.Pixels.size(); j++) {
1707                pt = tg.Pixels.get(j);
1708                if (j == 0) {
1709                    shape.moveTo(pt);
1710                } else {
1711                    shape.lineTo(pt);
1712                }
1713            }
1714
1715            //post clip the shape and set the polylines
1716            ArrayList<Shape2> shapes = new ArrayList();
1717            shapes.add(shape);
1718            //post-clip the shape
1719            if (clsUtilityCPOF.canClipPoints(tg) == false && clipBounds != null) {
1720                shapes = clsUtilityCPOF.postClipShapes(tg, shapes, clipBounds);
1721            } else if (clsUtilityCPOF.canClipPoints(tg) == false && clipPoints != null) {
1722                shapes = clsUtilityCPOF.postClipShapes(tg, shapes, clipPoints);
1723            }
1724            shape=shapes.get(0);
1725            if (hatch > 1) 
1726            {
1727                shape = clsUtility.buildHatchArea(tg, shape, hatch, 20);
1728                shape.setLineColor(lineColor);
1729                shape.setStroke(new BasicStroke(1));
1730                //shapes.clear();
1731                shapes.add(shape);
1732            }
1733            ArrayList<ShapeInfo> shapeInfos = new ArrayList();
1734            Shape2ToShapeInfo(shapeInfos, shapes);
1735            //set the shapeInfo polylines
1736            if (clipBounds != null) {
1737                clsUtilityGE.SetShapeInfosPolylines(tg, shapeInfos, clipBounds);
1738            } else if (clipPoints != null) {
1739                clsUtilityGE.SetShapeInfosPolylines(tg, shapeInfos, clipPoints);
1740            } else if (clipArea == null) {
1741                clsUtilityGE.SetShapeInfosPolylines(tg, shapeInfos, null);
1742            }
1743            //set milStd symbol shapes
1744            if (milStd.getSymbolShapes() == null) {
1745                milStd.setSymbolShapes(shapeInfos);
1746            } else {
1747                milStd.getSymbolShapes().addAll(shapeInfos);
1748            }
1749            return;
1750        } catch (Exception exc) {
1751            ErrorLogger.LogException(_className, "render_Shape",
1752                    new RendererException("Failed inside render_Shape", exc));
1753
1754        }
1755    }
1756
1757    private static void resolvePostClippedShapes(TGLight tg, ArrayList<Shape2> shapes) {
1758        try {
1759            //resolve the PBS and BBS shape properties after the post clip, regardless whether they were clipped
1760            switch (tg.get_LineType()) {
1761                case TacticalLines.BBS_RECTANGLE:
1762                case TacticalLines.BBS_POINT:
1763                case TacticalLines.BBS_LINE:
1764                case TacticalLines.BBS_AREA:
1765                case TacticalLines.PBS_RECTANGLE:
1766                case TacticalLines.PBS_SQUARE:
1767                    break;
1768                default:
1769                    return;
1770            }
1771            Color fillColor = tg.get_FillColor();
1772            shapes.get(0).setFillColor(fillColor);
1773            shapes.get(1).setFillColor(null);
1774            int fillStyle = tg.get_FillStyle();
1775            shapes.get(0).set_Fillstyle(0);
1776            shapes.get(1).set_Fillstyle(fillStyle);
1777            return;
1778
1779        } catch (Exception exc) {
1780            ErrorLogger.LogException(_className, "resolvePostClippedShapes",
1781                    new RendererException("Failed inside resolvePostClippedShapes", exc));
1782
1783        }
1784    }
1785
1786    /**
1787     * set the clip rectangle as an arraylist or a Rectangle2D depending on the
1788     * object
1789     *
1790     * @param clipBounds
1791     * @param clipRect
1792     * @param clipArray
1793     * @return
1794     */
1795    private static boolean setClip(Object clipBounds, Rectangle2D clipRect, ArrayList<Point2D> clipArray) {
1796        try {
1797            if (clipBounds == null) {
1798                return false;
1799            } else if (clipBounds.getClass().isAssignableFrom(Rectangle2D.Double.class)) {
1800                clipRect.setRect((Rectangle2D) clipBounds);
1801            } else if (clipBounds.getClass().isAssignableFrom(Rectangle2D.class)) {
1802                clipRect.setRect((Rectangle2D) clipBounds);
1803            } else if (clipBounds.getClass().isAssignableFrom(Rectangle.class)) {
1804                //clipRect.setRect((Rectangle2D)clipBounds);
1805                Rectangle rectx = (Rectangle) clipBounds;
1806                //clipBounds=new Rectangle2D.Double(rectx.x,rectx.y,rectx.width,rectx.height);
1807                clipRect.setRect(rectx.x, rectx.y, rectx.width, rectx.height);
1808            } else if (clipBounds.getClass().isAssignableFrom(ArrayList.class)) {
1809                clipArray.addAll((ArrayList) clipBounds);
1810            }
1811        } catch (Exception exc) {
1812            ErrorLogger.LogException(_className, "setClip",
1813                    new RendererException("Failed inside setClip", exc));
1814
1815        }
1816        return true;
1817    }
1818
1819    /**
1820     * public render function transferred from JavaLineArrayCPOF project. Use
1821     * this function to replicate CPOF renderer functionality.
1822     *
1823     * @param mss the milStdSymbol object
1824     * @param converter the geographic to pixels coordinate converter
1825     * @param clipBounds the pixels based clip bounds
1826     */
1827    public static void render(MilStdSymbol mss,
1828            IPointConversion converter,
1829            Object clipBounds) {
1830        try {
1831            ArrayList<ShapeInfo> shapeInfos = new ArrayList();
1832            ArrayList<ShapeInfo> modifierShapeInfos = new ArrayList();
1833            render(mss, converter, shapeInfos, modifierShapeInfos, clipBounds);
1834        } catch (Exception exc) {
1835            ErrorLogger.LogException(_className, "render",
1836                    new RendererException("render", exc));
1837
1838        }
1839    }
1840
1841    /**
1842     * Generic tester button says Tiger or use JavaRendererSample. Generic
1843     * renderer testers: called by JavaRendererSample and TestJavaLineArray
1844     * public render function transferred from JavaLineArrayCPOF project. Use
1845     * this function to replicate CPOF renderer functionality.
1846     *
1847     * @param mss
1848     * @param converter geographic to pixels converter
1849     * @param shapeInfos ShapeInfo array
1850     * @param modifierShapeInfos modifier ShapeInfo array
1851     * @param clipBounds clip bounds
1852     */
1853    public static void render(MilStdSymbol mss,
1854            IPointConversion converter,
1855            ArrayList<ShapeInfo> shapeInfos,
1856            ArrayList<ShapeInfo> modifierShapeInfos,
1857            Object clipBounds) {
1858        try {
1859            //boolean shiftLines = Channels.getShiftLines();
1860            //end section
1861
1862            Rectangle2D clipRect = new Rectangle2D.Double();
1863            ArrayList<Point2D> clipArray = new ArrayList();
1864            setClip(clipBounds, clipRect, clipArray);
1865
1866            TGLight tg = createTGLightFromMilStdSymbol(mss, converter);
1867            reversePointsRevD(tg);
1868            CELineArray.setClient("generic");
1869//            if (shiftLines) {
1870//                String affiliation = tg.get_Affiliation();
1871//                Channels.setAffiliation(affiliation);
1872//            }
1873            //CELineArray.setMinLength(2.5);    //2-27-2013
1874
1875            int linetype = tg.get_LineType();
1876            //replace calls to MovePixels
1877            clsUtility.RemoveDuplicatePoints(tg);
1878
1879            BufferedImage bi = new BufferedImage(8, 8, BufferedImage.TYPE_INT_ARGB);
1880            Graphics2D g2d = bi.createGraphics();
1881            g2d.setFont(tg.get_Font());
1882
1883            clsUtilityCPOF.SegmentGeoPoints(tg, converter, 1);
1884            clsUtility.FilterAXADPoints(tg, converter);
1885
1886            //prevent vertical segments for oneway, twoway, alt
1887            armyc2.c5isr.JavaTacticalRenderer.clsUtility.FilterVerticalSegments(tg);
1888            boolean isChange1Area = armyc2.c5isr.JavaTacticalRenderer.clsUtility.IsChange1Area(linetype);
1889            boolean isTextFlipped = false;
1890            //for 3d change 1 symbols we do not transform the points
1891
1892            //if it is world view then we want to flip the far points about
1893            //the left and right sides to get two symbols
1894            ArrayList<POINT2> farLeftPixels = new ArrayList();
1895            ArrayList<POINT2> farRightPixels = new ArrayList();
1896            if (isChange1Area == false) {
1897                clsUtilityCPOF.GetFarPixels(tg, converter, farLeftPixels, farRightPixels);
1898            }
1899
1900            ArrayList<Shape2> shapesLeft = new ArrayList();
1901            ArrayList<Shape2> shapesRight = new ArrayList();
1902            ArrayList<Shape2> shapes = null;   //use this to collect all the shapes
1903
1904            //CPOF 6.0 diagnostic
1905            ArrayList<Shape2> textSpecsLeft = null;
1906            ArrayList<Shape2> textSpecsRight = null;
1907            //Note: DisplayModifiers3 returns early if textSpecs are null
1908            textSpecsLeft = new ArrayList();
1909            textSpecsRight = new ArrayList();
1910
1911            if (farLeftPixels.size() > 0) {
1912                tg.Pixels = farLeftPixels;
1913                shapesLeft = clsRenderer2.GetLineArray(tg, converter, isTextFlipped, clipBounds);
1914                //CPOF 6.0
1915                //returns early if textSpecs are null
1916                Modifier2.DisplayModifiers2(tg, g2d, textSpecsLeft, isTextFlipped, null);
1917            }
1918            if (farRightPixels.size() > 0) {
1919                tg.Pixels = farRightPixels;
1920                shapesRight = clsRenderer2.GetLineArray(tg, converter, isTextFlipped, clipBounds);
1921                //CPOF 6.0
1922                //returns early if textSpecs are null
1923                Modifier2.DisplayModifiers2(tg, g2d, textSpecsRight, isTextFlipped, null);
1924            }
1925
1926            //CPOF 6.0 diagnostic
1927            ArrayList<Shape2> textSpecs = new ArrayList();
1928
1929            if (shapesLeft.isEmpty() || shapesRight.isEmpty()) {
1930                ArrayList<Shape2> linesWithFillShapes = null;
1931                if (clipArray != null && !clipArray.isEmpty()) {
1932                    linesWithFillShapes = clsClipQuad.LinesWithFill(tg, clipArray);
1933                } else if (clipRect != null && clipRect.getWidth() != 0) {
1934                    linesWithFillShapes = clsClipPolygon2.LinesWithFill(tg, clipRect);
1935                } else {
1936                    linesWithFillShapes = clsClipPolygon2.LinesWithFill(tg, null);
1937                }
1938
1939                //diagnostic: comment two lines if using the WW tester
1940                if (clsUtilityCPOF.canClipPoints(tg) && clipBounds != null) {
1941                    if (clipArray != null && !clipArray.isEmpty()) {
1942                        clsClipQuad.ClipPolygon(tg, clipArray);
1943                    } else if (clipRect != null && clipRect.getWidth() != 0) {
1944                        clsClipPolygon2.ClipPolygon(tg, clipRect);
1945                    }
1946
1947                    tg.LatLongs = clsUtility.PixelsToLatLong(tg.Pixels, converter);
1948                }
1949
1950                //diagnostic 1-28-13
1951                armyc2.c5isr.JavaTacticalRenderer.clsUtility.InterpolatePixels(tg);
1952
1953                tg.modifiers = new ArrayList();
1954                Modifier2.AddModifiersGeo(tg, g2d, clipBounds, converter);
1955
1956                clsUtilityCPOF.FilterPoints2(tg, converter);
1957                clsUtilityCPOF.ClearPixelsStyle(tg);
1958                //add section to replace preceding line M. Deutch 11-4-2011
1959                ArrayList rangeFanFillShapes = null;
1960                //do not fill the original shapes for circular range fans
1961                int savefillStyle = tg.get_FillStyle();
1962                if (linetype == TacticalLines.RANGE_FAN) {
1963                    tg.set_Fillstyle(0);
1964                }
1965
1966                shapes = clsRenderer2.GetLineArray(tg, converter, isTextFlipped, clipBounds);
1967
1968                // Add Feint, decoy, or dummy indicator
1969                if (shapes != null
1970                        && SymbolID.getSymbolSet(tg.get_SymbolId()) == SymbolID.SymbolSet_ControlMeasure
1971                        && SymbolUtilities.hasFDI(tg.get_SymbolId())) {
1972                    addFDI(tg, shapes);
1973                }
1974
1975                switch (linetype) {
1976                    case TacticalLines.RANGE_FAN:
1977                    case TacticalLines.RANGE_FAN_SECTOR:
1978                    case TacticalLines.RADAR_SEARCH:
1979                        if (tg.get_FillColor() == null || tg.get_FillColor().getAlpha() < 2) {
1980                            break;
1981                        }
1982                        TGLight tg1 = clsUtilityCPOF.GetCircularRangeFanFillTG(tg);
1983                        tg1.set_Fillstyle(savefillStyle);
1984                        tg1.set_SymbolId(tg.get_SymbolId());
1985                        rangeFanFillShapes = clsRenderer2.GetLineArray(tg1, converter, isTextFlipped, clipBounds);
1986
1987                        if (rangeFanFillShapes != null) {
1988                            shapes.addAll(0, rangeFanFillShapes);
1989                        }
1990                        break;
1991                    default:
1992                        break;
1993                }
1994
1995                //undo any fillcolor for lines with fill
1996                clsUtilityCPOF.LinesWithSeparateFill(tg.get_LineType(), shapes);
1997                clsClipPolygon2.addAbatisFill(tg, shapes);
1998
1999                //if this line is commented then the extra line in testbed goes away
2000                if (shapes != null && linesWithFillShapes != null && linesWithFillShapes.size() > 0) {
2001                    shapes.addAll(0, linesWithFillShapes);
2002                }
2003
2004                if (shapes != null && shapes.size() > 0) {
2005                    Modifier2.DisplayModifiers2(tg, g2d, textSpecs, isTextFlipped, null);
2006                    Shape2ToShapeInfo(modifierShapeInfos, textSpecs);
2007                    mss.setModifierShapes(modifierShapeInfos);
2008                }
2009            } else //symbol was more than 180 degrees wide, use left and right symbols
2010            {
2011                shapes = shapesLeft;
2012                shapes.addAll(shapesRight);
2013
2014                if (textSpecs != null) {
2015                    textSpecs.addAll(textSpecsLeft);
2016                    textSpecs.addAll(textSpecsRight);
2017                }
2018            }
2019            //post-clip the points if the tg could not be pre-clipped
2020            if (clsUtilityCPOF.canClipPoints(tg) == false && clipBounds != null) {
2021                shapes = clsUtilityCPOF.postClipShapes(tg, shapes, clipBounds);
2022            }
2023
2024            Shape2ToShapeInfo(shapeInfos, shapes);
2025            clsUtility.addHatchFills(tg, shapeInfos);
2026            mss.setSymbolShapes(shapeInfos);
2027        } catch (Exception exc) {
2028            ErrorLogger.LogException(_className, "render",
2029                    new RendererException("Failed inside render", exc));
2030
2031        }
2032    }
2033
2034    public static int getCMLineType(int version, int entityCode) {
2035        // Check if line type is specific to a version
2036        if (version >= SymbolID.Version_2525Ech1) {
2037            switch (entityCode) {
2038                // Added in 2525Ech1
2039                case 152600:
2040                    return TacticalLines.AREA_DEFENSE;
2041                case 152700:
2042                    return TacticalLines.FRONTAL_ATTACK;
2043                case 152900:
2044                    return TacticalLines.TURNING_MOVEMENT;
2045                case 152800:
2046                    return TacticalLines.MOBILE_DEFENSE;
2047                case 242800:
2048                    return TacticalLines.KILL_ZONE;
2049                case 342900:
2050                    return TacticalLines.MOVEMENT_TO_CONTACT;
2051                case 343100:
2052                    return TacticalLines.EXPLOIT;
2053                case 343300:
2054                    return TacticalLines.DEMONSTRATE;
2055                case 343500:
2056                    return TacticalLines.ENVELOPMENT;
2057                case 343800:
2058                    return TacticalLines.INFILTRATION;
2059                case 344000:
2060                    return TacticalLines.PURSUIT;
2061                case 344400:
2062                    return TacticalLines.DISENGAGE;
2063                case 344500:
2064                    return TacticalLines.EVACUATE;
2065                case 344700:
2066                    return TacticalLines.TURN;
2067                // Updated in 2525Ech1
2068                case 172000:
2069                    return TacticalLines.WFZ;
2070                // Removed in 2525Ech1
2071                case 240804:
2072                    return -1;
2073            }
2074        }
2075        if (version >= SymbolID.Version_2525E) {
2076            switch (entityCode) {
2077                // Added in 2525E
2078                case 110400:
2079                    return TacticalLines.GENERIC_LINE;
2080                case 120700:
2081                    return TacticalLines.GENERIC_AREA;
2082                case 141800:
2083                    return TacticalLines.HOL;
2084                case 141900:
2085                    return TacticalLines.BHL;
2086                case 310800:
2087                    return TacticalLines.CSA;
2088                case 330500:
2089                    return TacticalLines.ROUTE;
2090                case 330501:
2091                    return TacticalLines.ROUTE_ONEWAY;
2092                case 330502:
2093                    return TacticalLines.ROUTE_ALT;
2094                case 344100:
2095                    return TacticalLines.FPOL;
2096                case 344200:
2097                    return TacticalLines.RPOL;
2098                // Updated in 2525E
2099                case 120500:
2100                    return TacticalLines.BASE_CAMP;
2101                case 120600:
2102                    return TacticalLines.GUERILLA_BASE;
2103                case 151000:
2104                    return TacticalLines.FORT;
2105                case 260400:
2106                    return TacticalLines.BCL;
2107                case 310100:
2108                    return TacticalLines.DHA;
2109                // Updated in 2525Ech1
2110                case 172000:
2111                    return TacticalLines.WFZ_REVD;
2112            }
2113        } else { // 2525Dchange 1 and older
2114            switch (entityCode) {
2115                // Updated in 2525E
2116                case 120500:
2117                    return TacticalLines.BASE_CAMP_REVD;
2118                case 120600:
2119                    return TacticalLines.GUERILLA_BASE_REVD;
2120                case 151000:
2121                    return TacticalLines.FORT_REVD;
2122                case 260400:
2123                    return TacticalLines.BCL_REVD;
2124                case 310100:
2125                    return TacticalLines.DHA_REVD;
2126                // Removed in 2525E
2127                case 150300:
2128                    return TacticalLines.ASSY;
2129                case 241601:
2130                    return TacticalLines.SENSOR;
2131                case 241602:
2132                    return TacticalLines.SENSOR_RECTANGULAR;
2133                case 241603:
2134                    return TacticalLines.SENSOR_CIRCULAR;
2135                // Updated in 2525Ech1
2136                case 172000:
2137                    return TacticalLines.WFZ_REVD;
2138            }
2139        }
2140        // Line type isn't specific to a version or doesn't exist
2141        switch (entityCode) {
2142            case 200101:
2143                return TacticalLines.LAUNCH_AREA;
2144            case 200201:
2145                return TacticalLines.DEFENDED_AREA_CIRCULAR;
2146            case 200202:
2147                return TacticalLines.DEFENDED_AREA_RECTANGULAR;
2148            case 120100:
2149                return TacticalLines.AO;
2150            case 120200:
2151                return TacticalLines.NAI;
2152            case 120300:
2153                return TacticalLines.TAI;
2154            case 120400:
2155                return TacticalLines.AIRFIELD;
2156            case 151401:
2157                return TacticalLines.AIRAOA;
2158            case 151402:
2159                return TacticalLines.AAAAA;
2160            case 151403:
2161                return TacticalLines.MAIN;
2162            case 151404:
2163                return TacticalLines.SPT;
2164            case 110100:
2165                return TacticalLines.BOUNDARY;
2166            case 110200:
2167                return TacticalLines.LL;
2168            case 110300:
2169                return TacticalLines.EWL;
2170            case 140100:
2171                return TacticalLines.FLOT;
2172            case 140200:
2173                return TacticalLines.LC;
2174            case 140300:
2175                return TacticalLines.PL;
2176            case 140400:
2177                return TacticalLines.FEBA;
2178            case 140500:
2179                return TacticalLines.PDF;
2180            case 140601:
2181                return TacticalLines.DIRATKAIR;
2182            case 140602:
2183                return TacticalLines.DIRATKGND;
2184            case 140603:
2185                return TacticalLines.DIRATKSPT;
2186            case 140700:
2187                return TacticalLines.FCL;
2188            case 140800:
2189                return TacticalLines.IL;
2190            case 140900:
2191                return TacticalLines.LOA;
2192            case 141000:
2193                return TacticalLines.LOD;
2194            case 141100:
2195                return TacticalLines.LDLC;
2196            case 141200:
2197                return TacticalLines.PLD;
2198            case 150200:
2199                return TacticalLines.ASSY;
2200            case 150100:
2201                return TacticalLines.GENERAL;
2202            case 150501:
2203                return TacticalLines.JTAA;
2204            case 150502:
2205                return TacticalLines.SAA;
2206            case 150503:
2207                return TacticalLines.SGAA;
2208            case 150600:    //dz no eny
2209                return TacticalLines.DZ;
2210            case 150700:    //ez no eny
2211                return TacticalLines.EZ;
2212            case 150800:    //lz no eny
2213                return TacticalLines.LZ;
2214            case 150900:    //pz no eny
2215                return TacticalLines.PZ;
2216            case 151100:
2217                return TacticalLines.LAA;
2218            case 151200:
2219                return TacticalLines.BATTLE;
2220            case 151202:
2221                return TacticalLines.PNO;
2222            case 151204:
2223                return TacticalLines.CONTAIN;
2224            case 151205:
2225                return TacticalLines.RETAIN;
2226            case 151300:
2227                return TacticalLines.EA;
2228            case 151203:
2229                return TacticalLines.STRONG;
2230            case 151500:
2231                return TacticalLines.ASSAULT;
2232            case 151600:
2233                return TacticalLines.ATKPOS;
2234            case 151700:
2235                return TacticalLines.OBJ;
2236            case 151800:
2237                return TacticalLines.ENCIRCLE;
2238            case 151900:
2239                return TacticalLines.PEN;
2240            case 152000:
2241                return TacticalLines.ATKBYFIRE;
2242            case 152100:
2243                return TacticalLines.SPTBYFIRE;
2244            case 152200:
2245                return TacticalLines.SARA;
2246            case 141300:
2247                return TacticalLines.AIRHEAD;
2248            case 141400:
2249                return TacticalLines.BRDGHD;
2250            case 141500:
2251                return TacticalLines.HOLD;
2252            case 141600:
2253                return TacticalLines.RELEASE;
2254            case 141700:
2255                return TacticalLines.AMBUSH;
2256            case 170100:
2257                return TacticalLines.AC;
2258            case 170200:
2259                return TacticalLines.LLTR;
2260            case 170300:
2261                return TacticalLines.MRR;
2262            case 170400:
2263                return TacticalLines.SL;
2264            case 170500:
2265                return TacticalLines.SAAFR;
2266            case 170600:
2267                return TacticalLines.TC;
2268            case 170700:
2269                return TacticalLines.SC;
2270            case 170800:
2271                return TacticalLines.BDZ;
2272            case 170900:
2273                return TacticalLines.HIDACZ;
2274            case 171000:
2275                return TacticalLines.ROZ;
2276            case 171100:
2277                return TacticalLines.AARROZ;
2278            case 171200:
2279                return TacticalLines.UAROZ;
2280            case 171300:
2281                return TacticalLines.WEZ;
2282            case 171400:
2283                return TacticalLines.FEZ;
2284            case 171500:
2285                return TacticalLines.JEZ;
2286            case 171600:
2287                return TacticalLines.MEZ;
2288            case 171700:
2289                return TacticalLines.LOMEZ;
2290            case 171800:
2291                return TacticalLines.HIMEZ;
2292            case 171900:
2293                return TacticalLines.FAADZ;
2294            case 200401:
2295                return TacticalLines.SHIP_AOI_CIRCULAR;
2296            case 240804:
2297                return TacticalLines.RECTANGULAR_TARGET;
2298            case 220100:
2299                return TacticalLines.BEARING;
2300            case 220101:
2301                return TacticalLines.ELECTRO;
2302            case 220102:    //EW                //new label
2303                return TacticalLines.BEARING_EW;
2304            case 220103:
2305                return TacticalLines.ACOUSTIC;
2306            case 220104:
2307                return TacticalLines.ACOUSTIC_AMB;
2308            case 220105:
2309                return TacticalLines.TORPEDO;
2310            case 220106:
2311                return TacticalLines.OPTICAL;
2312            case 218400:
2313                return TacticalLines.NAVIGATION;
2314            case 220107:    //Jammer                //new label
2315                return TacticalLines.BEARING_J;
2316            case 220108:    //RDF                   //new label
2317                return TacticalLines.BEARING_RDF;
2318            case 240101:
2319                return TacticalLines.ACA;
2320            case 240102:
2321                return TacticalLines.ACA_RECTANGULAR;
2322            case 240103:
2323                return TacticalLines.ACA_CIRCULAR;
2324
2325            case 240201:
2326                return TacticalLines.FFA;
2327            case 240202:
2328                return TacticalLines.FFA_RECTANGULAR;
2329            case 240203:
2330                return TacticalLines.FFA_CIRCULAR;
2331
2332            case 240301:
2333                return TacticalLines.NFA;
2334            case 240302:
2335                return TacticalLines.NFA_RECTANGULAR;
2336            case 240303:
2337                return TacticalLines.NFA_CIRCULAR;
2338
2339            case 240401:
2340                return TacticalLines.RFA;
2341            case 240402:
2342                return TacticalLines.RFA_RECTANGULAR;
2343            case 240403:
2344                return TacticalLines.RFA_CIRCULAR;
2345            case 240503:
2346                return TacticalLines.PAA;
2347            case 240501:
2348                return TacticalLines.PAA_RECTANGULAR;
2349            case 240502:
2350                return TacticalLines.PAA_CIRCULAR;
2351            case 260100:
2352                return TacticalLines.FSCL;
2353            case 300100:
2354                return TacticalLines.ICL;
2355            case 190100:
2356                return TacticalLines.IFF_OFF;
2357            case 190200:
2358                return TacticalLines.IFF_ON;
2359            case 260200:
2360                return TacticalLines.CFL;
2361            case 260300:
2362                return TacticalLines.NFL;
2363            case 260500:
2364                return TacticalLines.RFL;
2365            case 260600:
2366                return TacticalLines.MFP;
2367            case 240701:
2368                return TacticalLines.LINTGT;
2369            case 240702:
2370                return TacticalLines.LINTGTS;
2371            case 240703:
2372                return TacticalLines.FPF;
2373            case 240801:
2374                return TacticalLines.AT;
2375            case 240802:
2376                return TacticalLines.RECTANGULAR;
2377            case 240803:
2378                return TacticalLines.CIRCULAR;
2379            case 240805:
2380                return TacticalLines.SERIES;
2381            case 240806:
2382                return TacticalLines.SMOKE;
2383            case 240808:
2384                return TacticalLines.BOMB;
2385            case 241001:
2386                return TacticalLines.FSA;
2387            case 241002:
2388                return TacticalLines.FSA_RECTANGULAR;
2389            case 200402:
2390                return TacticalLines.SHIP_AOI_RECTANGULAR;
2391            case 200600:
2392                return TacticalLines.CUED_ACQUISITION;
2393            case 200700:
2394                return TacticalLines.RADAR_SEARCH;
2395            case 241003:
2396                return TacticalLines.FSA_CIRCULAR;
2397            case 200300:
2398                return TacticalLines.NOTACK;
2399            case 241101:
2400                return TacticalLines.ATI;
2401            case 241102:
2402                return TacticalLines.ATI_RECTANGULAR;
2403            case 241103:
2404                return TacticalLines.ATI_CIRCULAR;
2405            case 241201:
2406                return TacticalLines.CFFZ;
2407            case 241202:
2408                return TacticalLines.CFFZ_RECTANGULAR;
2409            case 241203:
2410                return TacticalLines.CFFZ_CIRCULAR;
2411            case 241301:
2412                return TacticalLines.CENSOR;
2413            case 241302:
2414                return TacticalLines.CENSOR_RECTANGULAR;
2415            case 241303:
2416                return TacticalLines.CENSOR_CIRCULAR;
2417            case 241401:
2418                return TacticalLines.CFZ;
2419            case 241402:
2420                return TacticalLines.CFZ_RECTANGULAR;
2421            case 241403:
2422                return TacticalLines.CFZ_CIRCULAR;
2423            case 241501:
2424                return TacticalLines.DA;
2425            case 241502:
2426                return TacticalLines.DA_RECTANGULAR;
2427            case 241503:
2428                return TacticalLines.DA_CIRCULAR;
2429            case 241701:
2430                return TacticalLines.TBA;
2431            case 241702:
2432                return TacticalLines.TBA_RECTANGULAR;
2433            case 241703:
2434                return TacticalLines.TBA_CIRCULAR;
2435            case 241801:
2436                return TacticalLines.TVAR;
2437            case 241802:
2438                return TacticalLines.TVAR_RECTANGULAR;
2439            case 241803:
2440                return TacticalLines.TVAR_CIRCULAR;
2441            case 241901:
2442                return TacticalLines.ZOR;
2443            case 241902:
2444                return TacticalLines.ZOR_RECTANGULAR;
2445            case 241903:
2446                return TacticalLines.ZOR_CIRCULAR;
2447            case 242000:
2448                return TacticalLines.TGMF;
2449            case 242100:
2450                return TacticalLines.RANGE_FAN;
2451            case 242200:
2452                return TacticalLines.RANGE_FAN_SECTOR;
2453            case 242301:
2454                return TacticalLines.KILLBOXBLUE;
2455            case 242302:
2456                return TacticalLines.KILLBOXBLUE_RECTANGULAR;
2457            case 242303:
2458                return TacticalLines.KILLBOXBLUE_CIRCULAR;
2459            case 242304:
2460                return TacticalLines.KILLBOXPURPLE;
2461            case 242305:
2462                return TacticalLines.KILLBOXPURPLE_RECTANGULAR;
2463            case 242306:
2464                return TacticalLines.KILLBOXPURPLE_CIRCULAR;
2465            case 270100:
2466            case 270200:
2467                return TacticalLines.ZONE;
2468            case 270300:
2469                return TacticalLines.OBSFAREA;
2470            case 270400:
2471                return TacticalLines.OBSAREA;
2472            case 270501:
2473                return TacticalLines.MNFLDBLK;
2474            case 270502:
2475                return TacticalLines.MNFLDDIS;
2476            case 270503:
2477                return TacticalLines.MNFLDFIX;
2478            case 270504:
2479                return TacticalLines.TURN_REVD;
2480            case 270601:
2481                return TacticalLines.EASY;
2482            case 270602:
2483                return TacticalLines.BYDIF;
2484            case 270603:
2485                return TacticalLines.BYIMP;
2486            case 271100:
2487                return TacticalLines.GAP;
2488            case 271201:
2489                return TacticalLines.PLANNED;
2490            case 271202:
2491                return TacticalLines.ESR1;
2492            case 271203:
2493                return TacticalLines.ESR2;
2494            case 271204:
2495                return TacticalLines.ROADBLK;
2496            case 280100:
2497                return TacticalLines.ABATIS;
2498            case 290100:
2499                return TacticalLines.LINE;
2500            case 290201:
2501                return TacticalLines.ATDITCH;
2502            case 290202:
2503                return TacticalLines.ATDITCHC;
2504            case 290203:
2505                return TacticalLines.ATDITCHM;
2506            case 290204:
2507                return TacticalLines.ATWALL;
2508            case 290301:
2509                return TacticalLines.UNSP;
2510            case 290302:
2511                return TacticalLines.SFENCE;
2512            case 290303:
2513                return TacticalLines.DFENCE;
2514            case 290304:
2515                return TacticalLines.DOUBLEA;
2516            case 290305:
2517                return TacticalLines.LWFENCE;
2518            case 290306:
2519                return TacticalLines.HWFENCE;
2520            case 290307:
2521                return TacticalLines.SINGLEC;
2522            case 290308:
2523                return TacticalLines.DOUBLEC;
2524            case 290309:
2525                return TacticalLines.TRIPLE;
2526            case 290600:
2527                return TacticalLines.MFLANE;
2528            case 270707:
2529                return TacticalLines.DEPICT;
2530            case 270800:
2531                return TacticalLines.MINED;
2532            case 270801:
2533                return TacticalLines.FENCED;
2534            case 290101:
2535                return TacticalLines.MINE_LINE;
2536            case 271000:
2537                return TacticalLines.UXO;
2538            case 271700:
2539                return TacticalLines.BIO;
2540            case 271800:
2541                return TacticalLines.CHEM;
2542            case 271900:
2543                return TacticalLines.NUC;
2544            case 272000:
2545                return TacticalLines.RAD;
2546            case 290400:
2547                return TacticalLines.CLUSTER;
2548            case 290500:
2549                return TacticalLines.TRIP;
2550            case 282003:
2551                return TacticalLines.OVERHEAD_WIRE;
2552            case 271300:
2553                return TacticalLines.ASLTXING;
2554            case 271500:
2555                return TacticalLines.FORDSITE;
2556            case 271600:
2557                return TacticalLines.FORDIF;
2558            case 290700:
2559                return TacticalLines.FERRY;
2560            case 290800:
2561                return TacticalLines.RAFT;
2562            case 290900:
2563                return TacticalLines.FORTL;
2564            case 291000:
2565                return TacticalLines.FOXHOLE;
2566            case 272100:
2567                return TacticalLines.MSDZ;
2568            case 272200:
2569                return TacticalLines.DRCL;
2570
2571            case 310200:
2572                return TacticalLines.EPW;
2573            case 310300:
2574                return TacticalLines.FARP;
2575            case 310400:
2576                return TacticalLines.RHA;
2577            case 310500:
2578                return TacticalLines.RSA;
2579            case 310600:
2580                return TacticalLines.BSA;
2581            case 310700:
2582                return TacticalLines.DSA;
2583            case 330100:
2584                return TacticalLines.CONVOY;
2585            case 330200:
2586                return TacticalLines.HCONVOY;
2587            case 330300:
2588                return TacticalLines.MSR;
2589            case 330301:
2590                return TacticalLines.MSR_ONEWAY;
2591            case 330401:
2592                return TacticalLines.ASR_ONEWAY;
2593            case 330302:
2594                return TacticalLines.MSR_TWOWAY;
2595            case 330402:
2596                return TacticalLines.ASR_TWOWAY;
2597            case 330303:
2598                return TacticalLines.MSR_ALT;
2599            case 330403:
2600                return TacticalLines.ASR_ALT;
2601
2602            case 330400:
2603                return TacticalLines.ASR;
2604
2605            case 340100:
2606                return TacticalLines.BLOCK;
2607            case 340200:
2608                return TacticalLines.BREACH;
2609            case 340300:
2610                return TacticalLines.BYPASS;
2611            case 340400:
2612                return TacticalLines.CANALIZE;
2613            case 340500:
2614                return TacticalLines.CLEAR;
2615            case 340600:
2616                return TacticalLines.CATK;
2617            case 340700:
2618                return TacticalLines.CATKBYFIRE;
2619
2620            case 340800:
2621                return TacticalLines.DELAY;
2622            case 341000:
2623                return TacticalLines.DISRUPT;
2624            case 341100:
2625                return TacticalLines.FIX;
2626            case 341200:
2627                return TacticalLines.FOLLA;
2628            case 341300:
2629                return TacticalLines.FOLSP;
2630            case 341500:
2631                return TacticalLines.ISOLATE;
2632            case 341700:
2633                return TacticalLines.OCCUPY;
2634            case 341800:
2635                return TacticalLines.PENETRATE;
2636            case 341900:
2637                return TacticalLines.RIP;
2638            case 342000:
2639                return TacticalLines.RETIRE;
2640            case 342100:
2641                return TacticalLines.SECURE;
2642            case 342201:
2643                return TacticalLines.COVER;
2644            case 342202:
2645                return TacticalLines.GUARD;
2646            case 342203:
2647                return TacticalLines.SCREEN;
2648            case 342300:
2649                return TacticalLines.SEIZE;
2650            case 342400:
2651                return TacticalLines.WITHDRAW;
2652            case 342500:
2653                return TacticalLines.WDRAWUP;
2654            case 342600:
2655                return TacticalLines.CORDONKNOCK;
2656            case 342700:
2657                return TacticalLines.CORDONSEARCH;
2658            case 272101:
2659                return TacticalLines.STRIKWARN;
2660            default:
2661                break;
2662        }
2663        return -1;
2664    }
2665
2666    /**
2667     * Some symbol's points are reversed when moving from 2525C to 2525D. This method should be called at the start of each render.
2668     *
2669     * It's a simpler fix to reverse the points order at start than to reverse order when rendering.
2670     *
2671     * Note: Make sure to only call once to not reverse reversed points
2672     * @param tg
2673     */
2674    private static void reversePointsRevD(TGLight tg) {
2675        try {
2676            if (tg.get_SymbolId().length() < 20 || SymbolID.getSymbolSet(tg.get_SymbolId()) != 25) {
2677                return;
2678            }
2679            switch (tg.get_LineType()) {
2680                case TacticalLines.UNSP:
2681                case TacticalLines.LWFENCE:
2682                case TacticalLines.HWFENCE:
2683                case TacticalLines.SINGLEC:
2684                case TacticalLines.DOUBLEC:
2685                case TacticalLines.TRIPLE:
2686                case TacticalLines.LINE:
2687                    if (tg.Pixels != null) {
2688                        Collections.reverse(tg.Pixels);
2689                    }
2690                    if (tg.LatLongs != null) {
2691                        Collections.reverse(tg.LatLongs);
2692                    }
2693                    break;
2694                case TacticalLines.CLUSTER:
2695                    if (SymbolID.getVersion(tg.get_SymbolId()) < SymbolID.Version_2525E) {
2696                        if (tg.Pixels != null) {
2697                            Collections.reverse(tg.Pixels);
2698                        }
2699                        if (tg.LatLongs != null) {
2700                            Collections.reverse(tg.LatLongs);
2701                        }
2702                    }
2703                    break;
2704                default:
2705                    break;
2706            }
2707        } catch (Exception exc) {
2708            ErrorLogger.LogException("clsRenderer", "reversePointsRevD",
2709                    new RendererException("Failed inside reversePointsRevD", exc));
2710        }
2711    }
2712}