001/*
002 * To change this template, choose Tools | Templates
003 * and open the template in the editor.
004 */
005package armyc2.c5isr.RenderMultipoints;
006
007import java.util.ArrayList;
008import java.util.Arrays;
009import java.util.Collections;
010import java.util.HashMap;
011import java.util.Map;
012
013import armyc2.c5isr.JavaLineArray.POINT2;
014import armyc2.c5isr.JavaLineArray.Shape2;
015import armyc2.c5isr.JavaLineArray.TacticalLines;
016import armyc2.c5isr.JavaLineArray.arraysupport;
017import armyc2.c5isr.JavaLineArray.lineutility;
018import armyc2.c5isr.JavaLineArray.ref;
019import armyc2.c5isr.JavaTacticalRenderer.TGLight;
020import armyc2.c5isr.JavaTacticalRenderer.clsMETOC;
021import armyc2.c5isr.JavaTacticalRenderer.clsUtility;
022import armyc2.c5isr.JavaTacticalRenderer.mdlGeodesic;
023import armyc2.c5isr.graphics2d.GeneralPath;
024import armyc2.c5isr.graphics2d.PathIterator;
025import armyc2.c5isr.graphics2d.Point2D;
026import armyc2.c5isr.graphics2d.Rectangle;
027import armyc2.c5isr.graphics2d.Rectangle2D;
028import armyc2.c5isr.graphics2d.Shape;
029import armyc2.c5isr.renderer.utilities.ErrorLogger;
030import armyc2.c5isr.renderer.utilities.IPointConversion;
031import armyc2.c5isr.renderer.utilities.RendererException;
032import armyc2.c5isr.renderer.utilities.ShapeInfo;
033import armyc2.c5isr.renderer.utilities.SymbolUtilities;
034
035/**
036 * CPOF utility functions taken from JavaLineArrayCPOF
037 *
038*
039 */
040public final class clsUtilityCPOF {
041
042    private static final String _className = "clsUtilityCPOF";
043
044    /**
045     *
046     * @param ptLatLong
047     * @param converter
048     * @return
049     */
050    private static POINT2 PointLatLongToPixels(POINT2 ptLatLong,
051            IPointConversion converter) {
052        POINT2 pt = new POINT2();
053        try {
054            double x = ptLatLong.x;
055            double y = ptLatLong.y;
056            Point2D ptPixels = converter.GeoToPixels(new Point2D.Double(x, y));
057            pt.x = ptPixels.getX();
058            pt.y = ptPixels.getY();
059            pt.style = ptLatLong.style;
060
061        } catch (Exception exc) {
062            ErrorLogger.LogException(_className, "PointLatLongToPixels",
063                    new RendererException("Failed inside PointLatLongToPixels", exc));
064        }
065        return pt;
066    }
067
068    /**
069     * for the change 1 fire support areas
070     *
071     * @param tg
072     * @param lineType
073     * @param radius
074     * @param width
075     * @param length
076     * @param attitude
077     */
078    private static void GetNumericFields(TGLight tg,
079            int lineType,
080            ref<double[]> radius,
081            ref<double[]> width,
082            ref<double[]> length,
083            ref<double[]> attitude) {
084        try {
085            if (lineType == TacticalLines.RANGE_FAN_FILL) {
086                return;
087            }
088            double dist = 0;
089            ref<double[]> a12 = new ref(), a21 = new ref();
090            POINT2 pt0 = new POINT2(0, 0);
091            POINT2 pt1 = new POINT2(0, 0);
092            radius.value = new double[1];
093            width.value = new double[1];
094            attitude.value = new double[2];
095            length.value = new double[1];
096            switch (lineType) {
097                case TacticalLines.CIRCULAR:
098                case TacticalLines.PBS_CIRCLE:
099                case TacticalLines.BDZ:
100                case TacticalLines.BBS_POINT:
101                case TacticalLines.FSA_CIRCULAR:
102                case TacticalLines.NOTACK:
103                case TacticalLines.FFA_CIRCULAR:
104                case TacticalLines.NFA_CIRCULAR:
105                case TacticalLines.RFA_CIRCULAR:
106                case TacticalLines.PAA_CIRCULAR:
107                case TacticalLines.ATI_CIRCULAR:
108                case TacticalLines.CFFZ_CIRCULAR:
109                case TacticalLines.SENSOR_CIRCULAR:
110                case TacticalLines.CENSOR_CIRCULAR:
111                case TacticalLines.DA_CIRCULAR:
112                case TacticalLines.CFZ_CIRCULAR:
113                case TacticalLines.ZOR_CIRCULAR:
114                case TacticalLines.TBA_CIRCULAR:
115                case TacticalLines.TVAR_CIRCULAR:
116                case TacticalLines.ACA_CIRCULAR:
117                case TacticalLines.KILLBOXBLUE_CIRCULAR:
118                case TacticalLines.KILLBOXPURPLE_CIRCULAR:
119                    if (SymbolUtilities.isNumber(tg.get_AM())) {
120                        radius.value[0] = Double.parseDouble(tg.get_AM());
121                    }
122                    break;
123                case TacticalLines.LAUNCH_AREA:
124                case TacticalLines.DEFENDED_AREA_CIRCULAR:
125                case TacticalLines.SHIP_AOI_CIRCULAR:
126                case TacticalLines.PBS_ELLIPSE:
127                    //minor radius in meters
128                    if (SymbolUtilities.isNumber(tg.get_AM1())) {
129                        length.value[0] = Double.parseDouble(tg.get_AM1());
130                    }
131                    //major radius in meters
132                    if (SymbolUtilities.isNumber(tg.get_AM())) {
133                        width.value[0] = Double.parseDouble(tg.get_AM());
134                    }
135                    //rotation angle in degrees
136                    if (SymbolUtilities.isNumber(tg.get_AN())) {
137                        attitude.value[0] = Double.parseDouble(tg.get_AN());
138                    }
139
140                    break;
141                case TacticalLines.RECTANGULAR:
142                    if (SymbolUtilities.isNumber(tg.get_AM1())) {
143                        length.value[0] = Double.parseDouble(tg.get_AM1());
144                    }
145                    if (SymbolUtilities.isNumber(tg.get_AM())) {
146                        width.value[0] = Double.parseDouble(tg.get_AM());
147                    }
148                    //assume that attitude was passed in mils
149                    //so we must multiply by 360/6400 to convert to degrees                    
150                    if (SymbolUtilities.isNumber(tg.get_AN())) {
151                        attitude.value[0] = Double.parseDouble(tg.get_AN()) * (360d / 6400d);
152                    }
153                    break;
154                case TacticalLines.PBS_RECTANGLE:
155                case TacticalLines.PBS_SQUARE:
156                    if (SymbolUtilities.isNumber(tg.get_AM1())) {
157                        length.value[0] = Double.parseDouble(tg.get_AM1());
158                    }
159                    if (SymbolUtilities.isNumber(tg.get_AM())) {
160                        width.value[0] = Double.parseDouble(tg.get_AM());
161                    }
162                    //assume that attitude was passed in mils
163                    //so we must multiply by 360/6400 to convert to degrees                    
164                    if (SymbolUtilities.isNumber(tg.get_AN())) {
165                        attitude.value[0] = Double.parseDouble(tg.get_AN());
166                    }
167                    break;
168                case TacticalLines.CUED_ACQUISITION:
169                    if (SymbolUtilities.isNumber(tg.get_AM())) {
170                        length.value[0] = Double.parseDouble(tg.get_AM());
171                    }
172                    if (SymbolUtilities.isNumber(tg.get_AM1())) {
173                        width.value[0] = Double.parseDouble(tg.get_AM1());
174                    }
175                    if (SymbolUtilities.isNumber(tg.get_AN())) {
176                        // Make 0 degrees point north instead of East
177                        attitude.value[0] = Double.parseDouble(tg.get_AN()) + 270;
178                    }
179                    break;
180                case TacticalLines.PAA_RECTANGULAR:
181                case TacticalLines.FSA_RECTANGULAR:
182                case TacticalLines.SHIP_AOI_RECTANGULAR:
183                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
184                case TacticalLines.FFA_RECTANGULAR:
185                case TacticalLines.ACA_RECTANGULAR:
186                case TacticalLines.NFA_RECTANGULAR:
187                case TacticalLines.RFA_RECTANGULAR:
188                case TacticalLines.ATI_RECTANGULAR:
189                case TacticalLines.CFFZ_RECTANGULAR:
190                case TacticalLines.SENSOR_RECTANGULAR:
191                case TacticalLines.CENSOR_RECTANGULAR:
192                case TacticalLines.DA_RECTANGULAR:
193                case TacticalLines.CFZ_RECTANGULAR:
194                case TacticalLines.ZOR_RECTANGULAR:
195                case TacticalLines.TBA_RECTANGULAR:
196                case TacticalLines.TVAR_RECTANGULAR:
197                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
198                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
199                case TacticalLines.RECTANGULAR_TARGET:
200                case TacticalLines.BS_ORBIT:
201                    if (tg.LatLongs.size() >= 2) {
202                        //get the length and the attitude in mils
203                        pt0 = tg.LatLongs.get(0);
204                        pt1 = tg.LatLongs.get(1);
205                        dist = mdlGeodesic.geodesic_distance(pt0, pt1, a12, a21);
206                        attitude.value[0] = a12.value[0];
207                    }
208                    if (SymbolUtilities.isNumber(tg.get_AM())) {
209                        width.value[0] = Double.parseDouble(tg.get_AM());
210                    }
211                    break;
212                case TacticalLines.BS_POLYARC:
213                    if (SymbolUtilities.isNumber(tg.get_AM())) {
214                        length.value[0] = Double.parseDouble(tg.get_AM());
215                    }
216                    String[] an = tg.get_AN().split(",");
217                    attitude.value[0] = Double.parseDouble(an[0]);
218                    attitude.value[1] = Double.parseDouble(an[1]);
219                    break;
220                default:
221                    break;
222            }
223        } catch (Exception exc) {
224            ErrorLogger.LogException(_className, "GetNumericFields",
225                    new RendererException("Failed inside GetNumericFields", exc));
226        }
227    }
228
229    /**
230     * Do a 360 degree horizontal shift for points on either side of the
231     * midpoint of the display, if the MBR for the pixels is greater than 180
232     * degrees wide. Builds pixels for two symbols to draw a symbol flipped
233     * about the left edge and also a symbol flipped about the right edge. This
234     * function is typically used at world view. Caller must instantiate last
235     * two parameters.
236     *
237     * @param tg
238     * @param converter
239     * @param farLeftPixels - OUT - the resultant pixels for left shift symbol
240     * @param farRightPixels - OUT - the result pixels for the right shift
241     * symbol
242     */
243    protected static void GetFarPixels(TGLight tg,
244            IPointConversion converter,
245            ArrayList farLeftPixels,
246            ArrayList farRightPixels) {
247        try {
248            if (farLeftPixels == null || farRightPixels == null) {
249                return;
250            }
251            //Cannot use tg.LatLon to get width in degrees because it shifts +/-180 at IDL.
252            //Get degrees per pixel longitude, will use it for determining width in degrees
253            Point2D ptPixels50 = converter.GeoToPixels(new Point2D.Double(50, 30));
254            Point2D ptPixels60 = converter.GeoToPixels(new Point2D.Double(60, 30));
255            double degLonPerPixel = 10 / Math.abs(ptPixels60.getX() - ptPixels50.getX());
256            int j = 0;
257            double minX = Double.MAX_VALUE, maxX = -Double.MAX_VALUE;
258            int n = tg.Pixels.size();
259            //for(j=0;j<tg.Pixels.size();j++)
260            for (j = 0; j < n; j++) {
261                if (tg.Pixels.get(j).x < minX) {
262                    minX = tg.Pixels.get(j).x;
263                }
264                if (tg.Pixels.get(j).x > maxX) {
265                    maxX = tg.Pixels.get(j).x;
266                }
267            }
268            double degWidth = (maxX - minX) * degLonPerPixel;
269            if (Math.abs(degWidth) < 180) {
270                return;
271            }
272
273            //if it did not return then we must shift the pixels left and right
274            //first get the midpoint X value to use for partitioning the points
275            double midX = Math.abs(180 / degLonPerPixel);
276            double x = 0, y = 0;
277            //do a shift about the left hand side
278            //for(j=0;j<tg.Pixels.size();j++)
279            for (j = 0; j < n; j++) {
280                x = tg.Pixels.get(j).x;
281                y = tg.Pixels.get(j).y;
282                if (x > midX) {
283                    //shift x left by 360 degrees in pixels
284                    x -= 2 * midX;
285                }
286                //else do not shift the point
287                //add the shifted (or not) point to the new arraylist
288                farLeftPixels.add(new POINT2(x, y));
289            }
290            //do a shift about the right hand side
291            //for(j=0;j<tg.Pixels.size();j++)
292            for (j = 0; j < n; j++) {
293                x = tg.Pixels.get(j).x;
294                y = tg.Pixels.get(j).y;
295                if (x < midX) {
296                    //shift x right by 360 degrees in pixels
297                    x += 2 * midX;
298                }
299                //else do not shift the point
300                //add the shifted (or not) point to the new arraylist
301                farRightPixels.add(new POINT2(x, y));
302            }
303        } catch (Exception exc) {
304            ErrorLogger.LogException(_className, "GetFarPixels",
305                    new RendererException("Failed inside GetFarPixels", exc));
306        }
307    }
308
309    /**
310     *
311     * @param tg
312     * @param lineType
313     * @param converter
314     * @param shapes
315     * @return
316     */
317    protected static boolean Change1TacticalAreas(TGLight tg,
318            int lineType, IPointConversion converter, ArrayList<Shape2> shapes) {
319        try {
320            ref<double[]> width = new ref(), length = new ref(), attitude = new ref(), radius = new ref();
321            int j = 0;
322            POINT2 pt0 = tg.LatLongs.get(0);
323            POINT2 pt1 = null;
324            POINT2 ptTemp = new POINT2();
325            POINT2 pt00 = new POINT2();
326            if (tg.LatLongs.size() > 1) {
327                pt1 = tg.LatLongs.get(1);
328            } else {
329                pt1 = tg.LatLongs.get(0);
330            }
331            POINT2[] pPoints = null;
332            POINT2 ptCenter = PointLatLongToPixels(pt0, converter);
333
334            GetNumericFields(tg, lineType, radius, width, length, attitude);
335            switch (lineType) {
336                case TacticalLines.LAUNCH_AREA:
337                case TacticalLines.DEFENDED_AREA_CIRCULAR:
338                case TacticalLines.SHIP_AOI_CIRCULAR:
339                case TacticalLines.PBS_ELLIPSE:
340                    POINT2[] ellipsePts = mdlGeodesic.getGeoEllipse(pt0, width.value[0], length.value[0], attitude.value[0]);
341                    for (j = 0; j < ellipsePts.length; j++) //was 103
342                    {
343                        pt0 = ellipsePts[j];
344                        pt1 = PointLatLongToPixels(pt0, converter);
345                        tg.Pixels.add(pt1);
346                    }
347                    break;
348                case TacticalLines.PAA_RECTANGULAR:
349                case TacticalLines.FSA_RECTANGULAR:
350                case TacticalLines.SHIP_AOI_RECTANGULAR:
351                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
352                case TacticalLines.FFA_RECTANGULAR:
353                case TacticalLines.ACA_RECTANGULAR:
354                case TacticalLines.NFA_RECTANGULAR:
355                case TacticalLines.RFA_RECTANGULAR:
356                case TacticalLines.ATI_RECTANGULAR:
357                case TacticalLines.CFFZ_RECTANGULAR:
358                case TacticalLines.SENSOR_RECTANGULAR:
359                case TacticalLines.CENSOR_RECTANGULAR:
360                case TacticalLines.DA_RECTANGULAR:
361                case TacticalLines.CFZ_RECTANGULAR:
362                case TacticalLines.ZOR_RECTANGULAR:
363                case TacticalLines.TBA_RECTANGULAR:
364                case TacticalLines.TVAR_RECTANGULAR:
365                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
366                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
367                    //get the upper left corner                    
368                    pt00 = mdlGeodesic.geodesic_coordinate(pt0, width.value[0] / 2, attitude.value[0] - 90);
369                    pt00 = PointLatLongToPixels(pt00, converter);
370
371                    pt00.style = 0;
372                    tg.Pixels.add(pt00);
373
374                    //second corner (clockwise from center)
375                    ptTemp = mdlGeodesic.geodesic_coordinate(pt0, width.value[0] / 2, attitude.value[0] + 90);
376                    ptTemp = PointLatLongToPixels(ptTemp, converter);
377                    ptTemp.style = 0;
378                    tg.Pixels.add(ptTemp);
379
380                    //third corner (clockwise from center)
381                    ptTemp = mdlGeodesic.geodesic_coordinate(pt1, width.value[0] / 2, attitude.value[0] + 90);
382                    ptTemp = PointLatLongToPixels(ptTemp, converter);
383                    ptTemp.style = 0;
384                    tg.Pixels.add(ptTemp);
385
386                    //fourth corner (clockwise from center)
387                    ptTemp = mdlGeodesic.geodesic_coordinate(pt1, width.value[0] / 2, attitude.value[0] - 90);
388                    ptTemp = PointLatLongToPixels(ptTemp, converter);
389                    ptTemp.style = 0;
390                    tg.Pixels.add(ptTemp);
391
392                    tg.Pixels.add(pt00);
393                    break;
394                case TacticalLines.BS_ORBIT:
395                    ptTemp = mdlGeodesic.geodesic_coordinate(pt0, width.value[0] / 2, attitude.value[0] - 90);
396                    ptTemp = clsUtilityCPOF.PointLatLongToPixels(ptTemp, converter);
397                    ptTemp.style = 0;
398                    tg.Pixels.add(ptTemp);
399
400                    ptTemp = mdlGeodesic.geodesic_coordinate(pt1, width.value[0] / 2, attitude.value[0] - 90);
401                    pPoints = new POINT2[3];
402                    pPoints[0] = new POINT2(pt1);
403                    pPoints[1] = new POINT2(ptTemp);
404                    pPoints[2] = new POINT2(ptTemp);
405                    ArrayList<POINT2> pPoints2 = mdlGeodesic.GetGeodesicArc(pPoints);
406                    for (j = 0; j < pPoints2.size() / 2; j++) {
407                        ptTemp = clsUtilityCPOF.PointLatLongToPixels(pPoints2.get(j), converter);
408                        ptTemp.style = 0;
409                        tg.Pixels.add(ptTemp);
410                    }
411
412                    ptTemp = mdlGeodesic.geodesic_coordinate(pt0, width.value[0] / 2, attitude.value[0] + 90);
413                    pPoints[0] = new POINT2(pt0);
414                    pPoints[1] = new POINT2(ptTemp);
415                    pPoints[2] = new POINT2(ptTemp);
416                    pPoints2 = mdlGeodesic.GetGeodesicArc(pPoints);
417                    for (j = 0; j < pPoints2.size() / 2; j++) {
418                        ptTemp = clsUtilityCPOF.PointLatLongToPixels(pPoints2.get(j), converter);
419                        ptTemp.style = 0;
420                        tg.Pixels.add(ptTemp);
421                    }
422                    break;
423                case TacticalLines.BS_ROUTE:
424                    ArrayList<String> am = new ArrayList<>(Arrays.asList(tg.get_AM().split(",")));
425                    while (am.size() < tg.LatLongs.size() - 1) {
426                        am.add(am.get(am.size() - 1));
427                    }
428                    for (int i = 0; i < tg.LatLongs.size() - 1; i++) {
429                        ref<double[]> a12  = new ref();
430                        ref<double[]> a21 = new ref();
431                        pt0 = tg.LatLongs.get(i);
432                        pt1 = tg.LatLongs.get(i + 1);
433                        double widthVal = 0;
434                        double attitudeVal = 0;
435
436                        mdlGeodesic.geodesic_distance(pt0, pt1, a12, a21);
437                        attitudeVal = a12.value[0];
438
439                        if (SymbolUtilities.isNumber(am.get(i))) {
440                            widthVal = Double.parseDouble(am.get(i));
441                        }
442
443                        //get the upper left corner
444                        pt00 = mdlGeodesic.geodesic_coordinate(pt0, widthVal / 2, attitudeVal - 90);
445                        pt00 = clsUtilityCPOF.PointLatLongToPixels(pt00, converter);
446
447                        pt00.style = 0;
448                        tg.Pixels.add(pt00);
449
450                        //second corner (clockwise from center)
451                        ptTemp = mdlGeodesic.geodesic_coordinate(pt0, widthVal / 2, attitudeVal + 90);
452                        ptTemp = clsUtilityCPOF.PointLatLongToPixels(ptTemp, converter);
453                        ptTemp.style = 0;
454                        tg.Pixels.add(ptTemp);
455
456                        //third corner (clockwise from center)
457                        ptTemp = mdlGeodesic.geodesic_coordinate(pt1, widthVal / 2, attitudeVal + 90);
458                        ptTemp = clsUtilityCPOF.PointLatLongToPixels(ptTemp, converter);
459                        ptTemp.style = 0;
460                        tg.Pixels.add(ptTemp);
461
462                        //fourth corner (clockwise from center)
463                        ptTemp = mdlGeodesic.geodesic_coordinate(pt1, widthVal / 2, attitudeVal - 90);
464                        ptTemp = clsUtilityCPOF.PointLatLongToPixels(ptTemp, converter);
465                        ptTemp.style = 0;
466                        tg.Pixels.add(ptTemp);
467
468                        pt00 = new POINT2(pt00);
469                        pt00.style = 5;
470                        tg.Pixels.add(pt00);
471                    }
472                    break;
473                case TacticalLines.BS_TRACK:
474                    am = new ArrayList<>(Arrays.asList(tg.get_AM().split(",")));
475                    while (am.size() < tg.LatLongs.size() - 1) {
476                        am.add(am.get(am.size() - 1));
477                    }
478                    for (int i = 0; i < tg.LatLongs.size() - 1; i++) {
479                        ref<double[]> a12  = new ref();
480                        ref<double[]> a21 = new ref();
481                        pt0 = tg.LatLongs.get(i);
482                        pt1 = tg.LatLongs.get(i + 1);
483                        double leftWidth = 0;
484                        double rightWidth = 0;
485                        double attitudeVal = 0;
486
487                        mdlGeodesic.geodesic_distance(pt0, pt1, a12, a21);
488                        attitudeVal = a12.value[0];
489
490                        if (SymbolUtilities.isNumber(am.get(2 * i))) {
491                            leftWidth = Double.parseDouble(am.get(2 * i));
492                        }
493                        if (SymbolUtilities.isNumber(am.get(2 * i + 1))) {
494                            rightWidth = Double.parseDouble(am.get(2 * i + 1));
495                        }
496
497                        //get the upper left corner
498                        pt00 = mdlGeodesic.geodesic_coordinate(pt0, leftWidth, attitudeVal - 90);
499                        pt00 = clsUtilityCPOF.PointLatLongToPixels(pt00, converter);
500
501                        pt00.style = 0;
502                        tg.Pixels.add(pt00);
503
504                        //second corner (clockwise from center)
505                        ptTemp = mdlGeodesic.geodesic_coordinate(pt0, rightWidth, attitudeVal + 90);
506                        ptTemp = clsUtilityCPOF.PointLatLongToPixels(ptTemp, converter);
507                        ptTemp.style = 0;
508                        tg.Pixels.add(ptTemp);
509
510                        //third corner (clockwise from center)
511                        ptTemp = mdlGeodesic.geodesic_coordinate(pt1, rightWidth, attitudeVal + 90);
512                        ptTemp = clsUtilityCPOF.PointLatLongToPixels(ptTemp, converter);
513                        ptTemp.style = 0;
514                        tg.Pixels.add(ptTemp);
515
516                        //fourth corner (clockwise from center)
517                        ptTemp = mdlGeodesic.geodesic_coordinate(pt1, leftWidth, attitudeVal - 90);
518                        ptTemp = clsUtilityCPOF.PointLatLongToPixels(ptTemp, converter);
519                        ptTemp.style = 0;
520                        tg.Pixels.add(ptTemp);
521
522                        pt00 = new POINT2(pt00);
523                        pt00.style = 5;
524                        tg.Pixels.add(pt00);
525                    }
526                    break;
527                case TacticalLines.RECTANGULAR_TARGET:
528                    POINT2[] pts = new POINT2[4]; // 4 Corners
529
530                    // get the upper left corner
531                    pts[0] = mdlGeodesic.geodesic_coordinate(pt0, width.value[0] / 2, attitude.value[0] - 90);
532                    ptTemp = PointLatLongToPixels(pts[0], converter);
533                    ptTemp.style = 0;
534                    tg.Pixels.add(ptTemp);
535
536                    // second corner (clockwise from center)
537                    pts[1] = mdlGeodesic.geodesic_coordinate(pt0, width.value[0] / 2, attitude.value[0] + 90);
538                    ptTemp = PointLatLongToPixels(pts[1], converter);
539                    ptTemp.style = 0;
540                    tg.Pixels.add(ptTemp);
541
542                    // third corner (clockwise from center)
543                    pts[2] = mdlGeodesic.geodesic_coordinate(pt1, width.value[0] / 2, attitude.value[0] + 90);
544                    ptTemp = PointLatLongToPixels(pts[2], converter);
545                    ptTemp.style = 0;
546                    tg.Pixels.add(ptTemp);
547
548                    // fourth corner (clockwise from center)
549                    pts[3] = mdlGeodesic.geodesic_coordinate(pt1, width.value[0] / 2, attitude.value[0] - 90);
550                    ptTemp = PointLatLongToPixels(pts[3], converter);
551                    ptTemp.style = 0;
552                    tg.Pixels.add(ptTemp);
553
554                    // Close shape
555                    ptTemp = PointLatLongToPixels(pts[0], converter);
556                    ptTemp.style = 5;
557                    tg.Pixels.add(ptTemp);
558
559                    double heightD = mdlGeodesic.geodesic_distance(pts[0], pts[1], null, null);
560                    double widthD = mdlGeodesic.geodesic_distance(pts[1], pts[2], null, null);
561                    double crossLength = Math.min(heightD, widthD) * .4; // Length from center
562
563                    POINT2 centerPt = lineutility.CalcCenterPointDouble2(pts, 4);
564
565                    ptTemp = mdlGeodesic.geodesic_coordinate(centerPt, crossLength, 0);
566                    ptTemp = PointLatLongToPixels(ptTemp, converter);
567                    ptTemp.style = 0;
568                    tg.Pixels.add(ptTemp);
569
570                    ptTemp = mdlGeodesic.geodesic_coordinate(centerPt, crossLength, 180);
571                    ptTemp = PointLatLongToPixels(ptTemp, converter);
572                    ptTemp.style = 5;
573                    tg.Pixels.add(ptTemp);
574
575                    ptTemp = mdlGeodesic.geodesic_coordinate(centerPt, crossLength, -90);
576                    ptTemp = PointLatLongToPixels(ptTemp, converter);
577                    ptTemp.style = 0;
578                    tg.Pixels.add(ptTemp);
579
580                    ptTemp = mdlGeodesic.geodesic_coordinate(centerPt, crossLength, 90);
581                    ptTemp = PointLatLongToPixels(ptTemp, converter);
582                    ptTemp.style = 0;
583                    tg.Pixels.add(ptTemp);
584                    break;
585                case TacticalLines.RECTANGULAR:
586                case TacticalLines.PBS_RECTANGLE:
587                case TacticalLines.PBS_SQUARE:
588                case TacticalLines.CUED_ACQUISITION:
589                    //AFATDS swap length and width
590                    //comment next three lines to render per Mil-Std-2525
591                    //double temp=width.value[0];
592                    //width.value[0]=length.value[0];
593                    //length.value[0]=temp;
594
595                    //get the upper left corner
596                    ptTemp = mdlGeodesic.geodesic_coordinate(pt0, length.value[0] / 2, attitude.value[0] - 90);//was length was -90
597                    ptTemp = mdlGeodesic.geodesic_coordinate(ptTemp, width.value[0] / 2, attitude.value[0] + 0);//was width was 0
598
599                    ptTemp = PointLatLongToPixels(ptTemp, converter);
600                    tg.Pixels.add(ptTemp);
601                    //second corner (clockwise from center)
602                    ptTemp = mdlGeodesic.geodesic_coordinate(pt0, length.value[0] / 2, attitude.value[0] + 90);  //was length was +90
603                    ptTemp = mdlGeodesic.geodesic_coordinate(ptTemp, width.value[0] / 2, attitude.value[0] + 0);   //was width was 0
604
605                    ptTemp = PointLatLongToPixels(ptTemp, converter);
606
607                    tg.Pixels.add(ptTemp);
608
609                    //third corner (clockwise from center)
610                    ptTemp = mdlGeodesic.geodesic_coordinate(pt0, length.value[0] / 2, attitude.value[0] + 90);//was length was +90
611                    ptTemp = mdlGeodesic.geodesic_coordinate(ptTemp, width.value[0] / 2, attitude.value[0] + 180);//was width was +180
612
613                    ptTemp = PointLatLongToPixels(ptTemp, converter);
614
615                    tg.Pixels.add(ptTemp);
616
617                    //fouth corner (clockwise from center)
618                    ptTemp = mdlGeodesic.geodesic_coordinate(pt0, length.value[0] / 2, attitude.value[0] - 90);//was length was -90
619                    ptTemp = mdlGeodesic.geodesic_coordinate(ptTemp, width.value[0] / 2, attitude.value[0] + 180);//was width was +180
620
621                    ptTemp = PointLatLongToPixels(ptTemp, converter);
622                    tg.Pixels.add(ptTemp);
623                    tg.Pixels.add(new POINT2(tg.Pixels.get(0).x, tg.Pixels.get(0).y));
624                    break;
625                case TacticalLines.CIRCULAR:
626                case TacticalLines.PBS_CIRCLE:
627                case TacticalLines.BDZ:
628                case TacticalLines.BBS_POINT:
629                case TacticalLines.FSA_CIRCULAR:
630                case TacticalLines.NOTACK:
631                case TacticalLines.ACA_CIRCULAR:
632                case TacticalLines.FFA_CIRCULAR:
633                case TacticalLines.NFA_CIRCULAR:
634                case TacticalLines.RFA_CIRCULAR:
635                case TacticalLines.PAA_CIRCULAR:
636                case TacticalLines.ATI_CIRCULAR:
637                case TacticalLines.CFFZ_CIRCULAR:
638                case TacticalLines.SENSOR_CIRCULAR:
639                case TacticalLines.CENSOR_CIRCULAR:
640                case TacticalLines.DA_CIRCULAR:
641                case TacticalLines.CFZ_CIRCULAR:
642                case TacticalLines.ZOR_CIRCULAR:
643                case TacticalLines.TBA_CIRCULAR:
644                case TacticalLines.TVAR_CIRCULAR:
645                case TacticalLines.KILLBOXBLUE_CIRCULAR:
646                case TacticalLines.KILLBOXPURPLE_CIRCULAR:
647                    //get a horizontal point on the radius
648                    pt0 = tg.LatLongs.get(0);
649
650                    ptTemp = mdlGeodesic.geodesic_coordinate(pt0, radius.value[0], 90);
651
652                    pPoints = new POINT2[3];
653                    pPoints[0] = new POINT2(pt0);
654                    pPoints[1] = new POINT2(ptTemp);
655                    pPoints[2] = new POINT2(ptTemp);
656
657                    pPoints2 = mdlGeodesic.GetGeodesicArc(pPoints);
658                    POINT2 ptTemp2 = null;
659                    //fill pixels and latlongs
660                    for (j = 0; j < pPoints2.size(); j++) //was 103
661                    {
662                        pt0 = pPoints2.get(j);
663                        ptTemp2 = new POINT2();
664                        ptTemp2 = PointLatLongToPixels(pt0, converter);
665
666                        tg.Pixels.add(ptTemp2);
667                    }
668                    break;
669                case TacticalLines.RANGE_FAN:
670                    //get the concentric circles
671                    GetConcentricCircles(tg, lineType, converter);
672                    //Mil-Std-2525 Rev C does not have the orientation arrow
673                    //assume we are using Rev C if there is only 1 anchor point
674                    if (tg.LatLongs.size() > 1) {
675                        RangeFanOrientation(tg, lineType, converter);
676                    }
677                    break;
678                case TacticalLines.RANGE_FAN_SECTOR:
679                    GetSectorRangeFan(tg, converter);
680                    RangeFanOrientation(tg, lineType, converter);
681                    break;
682                case TacticalLines.RADAR_SEARCH:
683                case TacticalLines.BS_RADARC:
684                case TacticalLines.BS_CAKE:
685                    GetSectorRangeFan(tg, converter);
686                    break;
687                case TacticalLines.RANGE_FAN_FILL:  //circular range fan calls Change1TacticalAreas twice
688                    GetSectorRangeFan(tg, converter);
689                    break;
690                case TacticalLines.BS_POLYARC:
691                    // Polyarc points should be counterclockwise
692                    if (clsUtilityCPOF.CalculateSignedAreaOfPolygon(tg.LatLongs) < 0) {
693                        ptTemp = tg.LatLongs.remove(0);
694                        Collections.reverse(tg.LatLongs);
695                        tg.LatLongs.add(0, ptTemp);
696                    }
697
698                    ArrayList<POINT2> pPointsArc = new ArrayList<POINT2>();
699                    pPoints2 = new ArrayList<POINT2>();
700                    pPoints2.add(pt0);
701                    pPoints2.add(mdlGeodesic.geodesic_coordinate(pt0, length.value[0], attitude.value[0]));
702                    pPoints2.add(mdlGeodesic.geodesic_coordinate(pt0, length.value[0], attitude.value[1]));
703                    mdlGeodesic.GetGeodesicArc2(pPoints2, pPointsArc);
704
705                    for (int i = 0; i < pPointsArc.size(); i++) {
706                        ptTemp = new POINT2(pPointsArc.get(i));
707                        ptTemp = clsUtilityCPOF.PointLatLongToPixels(ptTemp, converter);
708                        tg.Pixels.add(ptTemp);
709                    }
710
711                    for (int i = 1; i < tg.LatLongs.size(); i++) {
712                        ptTemp = new POINT2(tg.LatLongs.get(i));
713                        ptTemp = clsUtilityCPOF.PointLatLongToPixels(ptTemp, converter);
714                        tg.Pixels.add(ptTemp);
715                    }
716
717                    tg.Pixels.add(tg.Pixels.get(0));
718                    break;
719                default:
720                    return false;
721            }
722
723            //the shapes
724            ArrayList<POINT2> farLeftPixels = new ArrayList();
725            ArrayList<POINT2> farRightPixels = new ArrayList();
726            clsUtilityCPOF.GetFarPixels(tg, converter, farLeftPixels, farRightPixels);
727            ArrayList<Shape2> shapesLeft = new ArrayList();
728            ArrayList<Shape2> shapesRight = new ArrayList();
729            //ArrayList<Shape2>shapes=null;   //use this to collect all the shapes
730
731            if (farLeftPixels.isEmpty() || farRightPixels.isEmpty()) {
732                //diagnostic
733                //Change1PixelsToShapes(tg,shapes);
734                ArrayList<POINT2> tempPixels = new ArrayList();
735                tempPixels.addAll((ArrayList) tg.Pixels);
736                clsUtilityCPOF.postSegmentFSA(tg, converter);
737                Change1PixelsToShapes(tg, shapes, false);
738                //reuse the original pixels for the subsequent call to AddModifier2
739                tg.Pixels = tempPixels;
740                //end section
741            } else //symbol was more than 180 degrees wide, use left and right symbols
742            {
743                //set tg.Pixels to the left shapes for the call to Change1PixelsToShapes
744                tg.Pixels = farLeftPixels;
745                Change1PixelsToShapes(tg, shapesLeft, false);
746                //set tg.Pixels to the right shapes for the call to Change1PixelsToShapes
747                tg.Pixels = farRightPixels;
748                Change1PixelsToShapes(tg, shapesRight, false);
749                //load left and right shapes into shapes
750                shapes.addAll(shapesLeft);
751                shapes.addAll(shapesRight);
752            }
753            if (lineType == TacticalLines.BBS_POINT) {
754                Shape2 shape = new Shape2(Shape2.SHAPE_TYPE_POLYLINE);
755                shape.moveTo(ptCenter);
756                //ptCenter.x+=1;
757                ptCenter.y += 1;
758                shape.lineTo(ptCenter);
759                shapes.add(shape);
760            }
761            if (lineType == TacticalLines.PBS_RECTANGLE || lineType == TacticalLines.PBS_SQUARE)
762            {
763                double dist = radius.value[0];//Double.parseDouble(strH1);
764                pt0 = new POINT2(tg.LatLongs.get(0));
765                pt1 = mdlGeodesic.geodesic_coordinate(pt0, dist, 45);//45 is arbitrary
766                Point2D pt02d = new Point2D.Double(pt0.x, pt0.y);
767                Point2D pt12d = new Point2D.Double(pt1.x, pt1.y);
768                pt02d = converter.GeoToPixels(pt02d);
769                pt12d = converter.GeoToPixels(pt12d);
770                pt0.x = pt02d.getX();
771                pt0.y = pt02d.getY();
772                pt1.x = pt12d.getX();
773                pt1.y = pt12d.getY();
774                dist = lineutility.CalcDistanceDouble(pt0, pt1);    //pixels distance
775                //tg.Pixels.get(0).style=(int)dist;
776                ArrayList<POINT2> tempPixels = new ArrayList();
777                tempPixels.addAll((ArrayList) tg.Pixels);
778                POINT2[]pts=tempPixels.toArray(new POINT2[tempPixels.size()]);
779                pts[0].style=(int)dist;
780                lineutility.getExteriorPoints(pts, pts.length, lineType, false);
781                tg.Pixels.clear();
782                for(j=0;j<pts.length;j++)
783                    tg.Pixels.add(new POINT2(pts[j].x,pts[j].y));
784
785                Change1PixelsToShapes(tg, shapes, true);
786                //reuse the original pixels for the subsequent call to AddModifier2
787                tg.Pixels = tempPixels;
788            }
789            return true;
790        } catch (Exception exc) {
791            ErrorLogger.LogException(_className, "Change1TacticalAreas",
792                    new RendererException("Failed inside Change1TacticalAreas", exc));
793        }
794        return false;
795    }
796
797    /**
798     * build shapes arraylist from tg.Pixels for the Change 1 symbols
799     *
800     * @param tg
801     * @param shapes - OUT - caller instantiates the arraylist
802     */
803    private static void Change1PixelsToShapes(TGLight tg, ArrayList<Shape2> shapes, boolean fill) {
804        Shape2 shape = null;
805        boolean beginLine = true;
806        POINT2 currentPt = null, lastPt = null;
807        int k = 0;
808        int linetype = tg.get_LineType();
809        int n = tg.Pixels.size();
810            //a loop for the outline shapes            
811        //for (k = 0; k < tg.Pixels.size(); k++)
812        for (k = 0; k < n; k++) {
813            //use shapes instead of pixels
814            if (shape == null) {
815                //shape = new Shape2(Shape2.SHAPE_TYPE_POLYLINE);
816                if(!fill)
817                    shape = new Shape2(Shape2.SHAPE_TYPE_POLYLINE);
818                else if(fill)
819                    shape = new Shape2(Shape2.SHAPE_TYPE_FILL);
820            }
821
822            currentPt = tg.Pixels.get(k);
823            if (k > 0) {
824                lastPt = tg.Pixels.get(k - 1);
825            }
826
827            if (beginLine) {
828                if (k == 0) {
829                    shape.set_Style(currentPt.style);
830                }
831
832                if (k > 0) //doubled points with linestyle=5
833                {
834                    if (currentPt.style == 5 && lastPt.style == 5) {
835                        shape.lineTo(currentPt);
836                    }
837                }
838
839                shape.moveTo(currentPt);
840                beginLine = false;
841            } else {
842                shape.lineTo(currentPt);
843                if (currentPt.style == 5 || currentPt.style == 10) {
844                    beginLine = true;
845                    //unless there are doubled points with style=5
846                    if ((linetype == TacticalLines.RANGE_FAN_FILL || linetype == TacticalLines.BS_ROUTE || linetype == TacticalLines.BS_TRACK || linetype == TacticalLines.BS_CAKE) && k < tg.Pixels.size() - 1) {
847                        shapes.add(shape);
848                        shape = new Shape2(Shape2.SHAPE_TYPE_POLYLINE);
849                    }
850                }
851            }
852            if (k == tg.Pixels.size() - 1) //PBS shapes have 2 shapes, other non-LC symbols have 1 shape
853            {
854                //shapes.add(shape);
855                if(shape.getShapeType()==ShapeInfo.SHAPE_TYPE_FILL)
856                    shapes.add(0,shape);
857                else
858                    shapes.add(shape);
859            }
860        }   //end for
861
862    }
863
864    private static void GetConcentricCircles(TGLight tg, int lineType, IPointConversion converter) {
865        try {
866            int j = 0, l = 0;
867            double radius = 0;
868
869            POINT2 pt = new POINT2();
870            ArrayList<POINT2> pts = new ArrayList();
871            double[] radii = null; // AM
872            String strAM = tg.get_AM();
873            if (tg.LatLongs.size() == 1 && strAM != null)
874            {
875                String[] strs = strAM.split(",");
876                radii = new double[strs.length];
877                for (j = 0; j < strs.length; j++) {
878                    radii[j] = Double.parseDouble(strs[j]);
879                }
880            }
881
882            int n = radii.length;
883
884            //loop thru the circles
885            POINT2[] pPoints = null;
886            for (l = 0; l < n; l++) {
887                radius = radii[l];
888                if (radius == 0) {
889                    continue;
890                }
891
892                pPoints = new POINT2[3];
893                pt = tg.LatLongs.get(0);
894                pPoints[0] = new POINT2(pt);
895                //radius, 90, ref lon2c, ref lat2c);
896                pt = mdlGeodesic.geodesic_coordinate(pt, radius, 90);
897                pPoints[1] = new POINT2(pt);
898                pPoints[2] = new POINT2(pt);
899
900                pts = mdlGeodesic.GetGeodesicArc(pPoints);
901
902                POINT2 ptTemp2 = null;
903                //fill pixels and latlongs
904                int t = pts.size();
905                //for (j = 0; j < pts.size(); j++)//was 103
906                for (j = 0; j < t; j++)//was 103
907                {
908                    ptTemp2 = new POINT2();
909                    ptTemp2 = PointLatLongToPixels(pts.get(j), converter);
910                    ptTemp2.style = 0;
911                    if (j == pts.size() - 1) {
912                        ptTemp2.style = 5;
913                    }
914
915                    tg.Pixels.add(ptTemp2);
916                }
917            }
918            int length = tg.Pixels.size();
919            tg.Pixels.get(length - 1).style = 5;
920
921        } catch (Exception exc) {
922            ErrorLogger.LogException(_className, "GetConcentricCircles",
923                    new RendererException("Failed inside GetConcentricCircles", exc));
924        }
925    }
926
927    /**
928     * if tg.H2 is filled then the max range sector is used to determine the
929     * orientation
930     *
931     * @param tg
932     * @return left,right,min,max
933     */
934    private static String GetMaxSector(TGLight tg) {
935        String strLeftRightMinMax = null;
936        try {
937            double max = 0, maxx = -Double.MAX_VALUE;
938            //get the number of sectors
939            strLeftRightMinMax = tg.get_LRMM();
940            String[] leftRightMinMax = strLeftRightMinMax.split(",");
941            int numSectors = leftRightMinMax.length / 4;
942            int k = 0, maxIndex = -1;
943            //there must be at least one sector
944            if (numSectors < 1) {
945                return null;
946            }
947
948            if (numSectors * 4 != leftRightMinMax.length) {
949                return null;
950            }
951            //get the max index
952            try {
953                for (k = 0; k < numSectors; k++) {
954                    //left = Double.parseDouble(leftRightMinMax[4 * k]);
955                    //right = Double.parseDouble(leftRightMinMax[4 * k + 1]);
956                    //min = Double.parseDouble(leftRightMinMax[4 * k + 2]);
957                    max = Double.parseDouble(leftRightMinMax[4 * k + 3]);
958                    if (max > maxx) {
959                        maxx = max;
960                        maxIndex = k;
961                    }
962                }
963            } catch (NumberFormatException e) {
964                return null;
965            }
966            String strLeft = leftRightMinMax[4 * maxIndex];
967            String strRight = leftRightMinMax[4 * maxIndex + 1];
968            String strMin = leftRightMinMax[4 * maxIndex + 2];
969            String strMax = leftRightMinMax[4 * maxIndex + 3];
970            strLeftRightMinMax = strLeft + "," + strRight + "," + strMin + "," + strMax;
971        } catch (Exception exc) {
972            ErrorLogger.LogException(_className, "GetMaxSector",
973                    new RendererException("Failed inside GetMaxSector", exc));
974        }
975        return strLeftRightMinMax;
976    }
977
978    /**
979     * Create a tg with a new line type to used for circular range fan fill
980     *
981     * @param tg
982     * @return
983     */
984    protected static TGLight GetCircularRangeFanFillTG(TGLight tg) {
985        TGLight tg1 = null;
986        try {
987            //instantiate a dummy tg which will be used to call GetSectorRangeFan
988            tg1 = new TGLight();
989            tg1.set_VisibleModifiers(true);
990            tg1.set_LineThickness(0);
991            tg1.set_FillColor(tg.get_FillColor());
992            tg1.set_Fillstyle(tg.get_FillStyle());
993            tg1.LatLongs = new ArrayList<POINT2>();
994            tg1.Pixels = new ArrayList<POINT2>();
995            //we only want the 0th point
996            tg1.LatLongs.add(tg.LatLongs.get(0));
997            tg1.Pixels.add(tg.Pixels.get(0));
998            tg1.Pixels.add(tg.Pixels.get(1));
999            tg1.set_LineType(TacticalLines.RANGE_FAN_FILL);
1000
1001            if (tg.get_LineType() == TacticalLines.RANGE_FAN_SECTOR || tg.get_LineType() == TacticalLines.RADAR_SEARCH) {
1002                tg1.set_LRMM(tg.get_LRMM());
1003                return tg1;
1004            } else if (tg.get_LineType() == TacticalLines.RANGE_FAN) {
1005                String[] radii = tg.get_AM().split(",");
1006                String strLeftRightMinMax = "";
1007                for (int j = 0; j < radii.length - 1; j++) {
1008                    if (j > 0) {
1009                        strLeftRightMinMax += ",";
1010                    }
1011
1012                    strLeftRightMinMax += "0,0," + radii[j] + "," + radii[j + 1];
1013                }
1014                tg1.set_LRMM(strLeftRightMinMax);
1015            }
1016        } catch (Exception exc) {
1017            ErrorLogger.LogException(_className, "GetCircularRangeFanFillTG",
1018                    new RendererException("Failed inside GetCircularRangeFanFillTG", exc));
1019        }
1020        return tg1;
1021    }
1022
1023    /**
1024     *
1025     * @param tg
1026     * @param converter
1027     * @return
1028     */
1029    private static boolean GetSectorRangeFan(TGLight tg, IPointConversion converter) {
1030        boolean circle = false;
1031        try {
1032            POINT2 ptCenter = tg.LatLongs.get(0);
1033            int k = 0, l = 0;
1034            int numSectors = 0;
1035            clsUtility.GetSectorRadiiFromPoints(tg);
1036
1037            //use pPoints to get each geodesic arc
1038            ArrayList<POINT2> pPoints = new ArrayList();
1039            ArrayList<POINT2> pPointsInnerArc = new ArrayList();
1040            ArrayList<POINT2> pPointsOuterArc = new ArrayList();
1041            ArrayList<POINT2> sectorPoints = new ArrayList();
1042            ArrayList<POINT2> allPoints = new ArrayList();
1043
1044            //use these and the center to define each sector
1045            POINT2 pt1 = new POINT2(), pt2 = new POINT2();
1046
1047            //get the number of sectors
1048            String strLeftRightMinMax = tg.get_LRMM();
1049            String[] leftRightMinMax = strLeftRightMinMax.split(",");
1050
1051            //sanity checks
1052            double left = 0, right = 0, min = 0, max = 0;
1053            numSectors = leftRightMinMax.length / 4;
1054
1055            //there must be at least one sector
1056            if (numSectors < 1) {
1057                return false;
1058            }
1059
1060            if (numSectors * 4 != leftRightMinMax.length) {
1061                return false;
1062            }
1063
1064            //left must be  less than right,
1065            //min must be less than max, each sector
1066            try {
1067                for (k = 0; k < numSectors; k++) {
1068                    left = Double.parseDouble(leftRightMinMax[4 * k]);
1069                    right = Double.parseDouble(leftRightMinMax[4 * k + 1]);
1070                    min = Double.parseDouble(leftRightMinMax[4 * k + 2]);
1071                    max = Double.parseDouble(leftRightMinMax[4 * k + 3]);
1072                }
1073            } catch (NumberFormatException e) {
1074                return false;
1075            }
1076
1077            for (k = 0; k < numSectors; k++) //was k=0
1078            {
1079                //empty any points that were there from the last sector
1080                sectorPoints.clear();
1081                pPointsOuterArc.clear();
1082                pPointsInnerArc.clear();
1083
1084                left = Double.parseDouble(leftRightMinMax[4 * k]);
1085                right = Double.parseDouble(leftRightMinMax[4 * k + 1]);
1086                min = Double.parseDouble(leftRightMinMax[4 * k + 2]);
1087                max = Double.parseDouble(leftRightMinMax[4 * k + 3]);
1088
1089                //get the first point of the sector inner arc
1090                pt1 = mdlGeodesic.geodesic_coordinate(ptCenter, min, left);
1091
1092                //get the last point of the sector inner arc
1093                pt2 = mdlGeodesic.geodesic_coordinate(ptCenter, min, right);
1094
1095                pPoints.clear();
1096
1097                pPoints.add(ptCenter);
1098                pPoints.add(pt1);
1099                pPoints.add(pt2);
1100
1101                circle = mdlGeodesic.GetGeodesicArc2(pPoints, pPointsInnerArc);
1102
1103                pPoints.clear();
1104                circle = false;
1105
1106                pt1 = mdlGeodesic.geodesic_coordinate(ptCenter, max, left);
1107                pt2 = mdlGeodesic.geodesic_coordinate(ptCenter, max, right);
1108
1109                pPoints.add(ptCenter);
1110                pPoints.add(pt1);
1111                pPoints.add(pt2);
1112
1113                //get the geodesic min arc from left to right
1114                circle = mdlGeodesic.GetGeodesicArc2(pPoints, pPointsOuterArc);
1115
1116                //we now have all the points and can add them to the polygon to return
1117                //we will have to reverse the order of points in the outer arc
1118                int n = pPointsInnerArc.size();
1119                for (l = 0; l < n; l++) {
1120                    pt1 = new POINT2(pPointsInnerArc.get(l));
1121                    sectorPoints.add(pt1);
1122                }
1123                n = pPointsOuterArc.size();
1124                //for (l = pPointsOuterArc.size() - 1; l >= 0; l--)
1125                for (l = n - 1; l >= 0; l--) {
1126                    pt1 = new POINT2(pPointsOuterArc.get(l));
1127                    sectorPoints.add(pt1);
1128                }
1129
1130                //close the polygon
1131                pt1 = new POINT2(pPointsInnerArc.get(0));
1132                pt1.style = 5;
1133                sectorPoints.add(pt1);
1134                n = sectorPoints.size();
1135                //for (l = 0; l < sectorPoints.size(); l++)
1136                for (l = 0; l < n; l++) {
1137                    allPoints.add(sectorPoints.get(l));
1138                }
1139            }
1140
1141            //cleanup what we can
1142            pPointsInnerArc = null;
1143            pPointsOuterArc = null;
1144            ptCenter = null;
1145
1146            POINT2 ptTemp = null;
1147            int n = allPoints.size();
1148            //for (l = 0; l < allPoints.size(); l++)
1149            for (l = 0; l < n; l++) {
1150                pt1 = new POINT2();
1151                pt1 = PointLatLongToPixels(allPoints.get(l), converter);
1152                //do not add duplicates
1153                if (ptTemp != null && pt1.x == ptTemp.x && pt1.y == ptTemp.y) {
1154                    continue;
1155                }
1156                tg.Pixels.add(new POINT2(pt1));
1157                ptTemp = new POINT2(pt1);
1158            }
1159
1160            return true;
1161        } catch (Exception exc) {
1162            ErrorLogger.LogException(_className, "GetSectorRangeFan",
1163                    new RendererException("Failed inside GetSectorRangeFan", exc));
1164        }
1165        return circle;
1166    }
1167
1168    private static void RangeFanOrientation(TGLight tg, int lineType, IPointConversion converter) {
1169        try {
1170            POINT2 pt0 = tg.LatLongs.get(0);
1171            double dist = 0;
1172            double orientation = 0;
1173            double radius = 0;
1174            //double[] radii = clsUtility.GetRadii(tg,lineType);
1175            int j = 0;
1176            POINT2 pt1 = new POINT2();
1177            //if tg.PointCollection has more than one point
1178            //we use pts[1] to stuff tg.H with the orientation
1179            ref<double[]> a12 = new ref(), a21 = new ref();
1180            if (tg.LatLongs.size() > 1) //rev B can use points
1181            {
1182                pt1 = tg.LatLongs.get(1);
1183                dist = mdlGeodesic.geodesic_distance(pt0, pt1, a12, a21);
1184                orientation = a12.value[0];
1185            } else //rev C uses H2
1186            {
1187                String strLeftRightMinMax = GetMaxSector(tg);
1188                String[] sector = strLeftRightMinMax.split(",");
1189                double left = Double.parseDouble(sector[0]);
1190                double right = Double.parseDouble(sector[1]);
1191                double min = Double.parseDouble(sector[2]);
1192                double max = Double.parseDouble(sector[3]);
1193                //we want the range to be 0 to 360
1194                while (left > 360) {
1195                    left -= 360;
1196                }
1197                while (right > 360) {
1198                    right -= 360;
1199                }
1200                while (left < 0) {
1201                    left += 360;
1202                }
1203                while (right < 0) {
1204                    right += 360;
1205                }
1206
1207                if (left > right) {
1208                    orientation = (left - 360 + right) / 2;
1209                } else {
1210                    orientation = (left + right) / 2;
1211                }
1212
1213                dist = max;
1214            }
1215            radius = dist * 1.1;
1216            POINT2 pt0F = new POINT2();
1217            POINT2 pt1F = new POINT2();
1218            POINT2 ptBaseF = new POINT2();
1219            POINT2 ptLeftF = new POINT2();
1220            POINT2 ptRightF = new POINT2();
1221            POINT2 ptTipF = new POINT2();
1222
1223            pt0 = tg.LatLongs.get(0);
1224
1225            pt0F = PointLatLongToPixels(pt0, converter);
1226
1227            pt1 = mdlGeodesic.geodesic_coordinate(pt0, radius, orientation);
1228
1229            pt1F = PointLatLongToPixels(pt1, converter);
1230            dist = lineutility.CalcDistanceDouble(pt0F, pt1F);
1231            double base = 10;
1232            if (dist < 100) {
1233                base = dist / 10;
1234            }
1235            if (base < 5) {
1236                base = 5;
1237            }
1238            double basex2 = 2 * base;
1239            ptBaseF = lineutility.ExtendAlongLineDouble(pt0F, pt1F, dist + base);   //was 10
1240            ptTipF = lineutility.ExtendAlongLineDouble(pt0F, pt1F, dist + basex2);  //was 20
1241
1242            ptLeftF = lineutility.ExtendDirectedLine(pt0F, ptBaseF, ptBaseF, 0, base);    //was 10
1243            ptRightF = lineutility.ExtendDirectedLine(pt0F, ptBaseF, ptBaseF, 1, base);   //was 10
1244            //length1 = tg.Pixels.size();
1245
1246            tg.Pixels.add(pt0F);
1247            ptTipF.style = 5;
1248            tg.Pixels.add(ptTipF);
1249            tg.Pixels.add(ptLeftF);
1250            ptTipF.style = 0;
1251            tg.Pixels.add(ptTipF);
1252            tg.Pixels.add(ptRightF);
1253        } catch (Exception exc) {
1254            ErrorLogger.LogException(_className, "RangeFanOrientation",
1255                    new RendererException("Failed inside RangeFanOrientation", exc));
1256        }
1257    }
1258
1259    /**
1260     * after filtering pixels it needs to reinitialize the style to 0 or it
1261     * causes CELineArraydotNet to build wrong shapes
1262     *
1263     * @param tg
1264     */
1265    protected static void ClearPixelsStyle(TGLight tg) {
1266        try {
1267            //do not clear pixel style for the air corridors because
1268            //arraysupport is using linestyle for these to set the segment width         
1269            switch (tg.get_LineType()) {
1270                case TacticalLines.BBS_AREA:
1271                case TacticalLines.BBS_LINE:
1272                case TacticalLines.BBS_RECTANGLE:
1273                case TacticalLines.SC:
1274                case TacticalLines.MRR:
1275                case TacticalLines.SL:
1276                case TacticalLines.TC:
1277                case TacticalLines.LLTR:
1278                case TacticalLines.AC:
1279                case TacticalLines.SAAFR:
1280                case TacticalLines.BS_ELLIPSE:
1281                    return;
1282                default:
1283                    break;
1284
1285            }
1286            int n = tg.Pixels.size();
1287            //for(int j=0;j<tg.Pixels.size();j++)            
1288            for (int j = 0; j < n; j++) {
1289                tg.Pixels.get(j).style = 0;
1290            }
1291
1292        } catch (Exception exc) {
1293            ErrorLogger.LogException(_className, "ClearPixelsStyle",
1294                    new RendererException("Failed inside ClearPixelsStyle", exc));
1295
1296        }
1297    }
1298
1299    /**
1300     * Filters too close points after segmenting and clipping
1301     *
1302     * @param tg
1303     * @param converter
1304     */
1305    protected static void FilterPoints2(TGLight tg, IPointConversion converter) {
1306        try {
1307            int lineType = tg.get_LineType();
1308            double minSpikeDistance = 0;
1309            boolean segmented = true;
1310            if (tg.Pixels.size() < 3) {
1311                return;
1312            }
1313
1314            switch (lineType) {
1315                case TacticalLines.PL:
1316                case TacticalLines.DECISION_LINE:
1317                case TacticalLines.FEBA:
1318                case TacticalLines.LOA:
1319                case TacticalLines.LL:
1320                case TacticalLines.EWL:
1321                case TacticalLines.FCL:
1322                case TacticalLines.LOD:
1323                case TacticalLines.LDLC:
1324                case TacticalLines.PLD:
1325                case TacticalLines.HOLD:
1326                case TacticalLines.HOLD_GE:
1327                case TacticalLines.RELEASE:
1328                case TacticalLines.HOL:
1329                case TacticalLines.BHL:
1330                case TacticalLines.BRDGHD:
1331                case TacticalLines.BRDGHD_GE:
1332                case TacticalLines.NFL:
1333                    minSpikeDistance = arraysupport.getScaledSize(5, tg.get_LineThickness(), tg.get_patternScale());
1334                    segmented = false;
1335                    break;
1336                case TacticalLines.ATDITCH:
1337                case TacticalLines.ATDITCHC:
1338                case TacticalLines.ATDITCHM:
1339                case TacticalLines.FLOT:
1340                case TacticalLines.FORT_REVD:
1341                case TacticalLines.FORT:
1342                case TacticalLines.FORTL:
1343                case TacticalLines.STRONG:
1344                    minSpikeDistance = arraysupport.getScaledSize(25, tg.get_LineThickness(), tg.get_patternScale());
1345                    break;
1346                case TacticalLines.LC:
1347                case TacticalLines.OBSAREA:
1348                case TacticalLines.OBSFAREA:
1349                case TacticalLines.ENCIRCLE:
1350                case TacticalLines.ZONE:
1351                case TacticalLines.LINE:
1352                case TacticalLines.ATWALL:
1353                //case TacticalLines.ATWALL3D:
1354                case TacticalLines.UNSP:
1355                case TacticalLines.SFENCE:
1356                case TacticalLines.DFENCE:
1357                case TacticalLines.DOUBLEA:
1358                case TacticalLines.LWFENCE:
1359                case TacticalLines.HWFENCE:
1360                case TacticalLines.SINGLEC:
1361                case TacticalLines.DOUBLEC:
1362                case TacticalLines.TRIPLE:
1363                    minSpikeDistance = arraysupport.getScaledSize(35, tg.get_LineThickness(), tg.get_patternScale());
1364                    break;
1365                case TacticalLines.ICE_EDGE_RADAR:  //METOCs
1366                case TacticalLines.ICE_OPENINGS_FROZEN:
1367                case TacticalLines.CRACKS_SPECIFIC_LOCATION:
1368                    minSpikeDistance = arraysupport.getScaledSize(35, tg.get_LineThickness(), tg.get_patternScale());
1369                    break;
1370                default:
1371                    return;
1372            }
1373            double dist = 0;
1374
1375            ArrayList<POINT2> pts = new ArrayList();
1376
1377            //stuff pts with tg.Pixels
1378            //loop through pts to remove any points which are too close
1379            //then reset tg.Pixels with the new array with boundary points removed,            
1380            int j = 0;
1381            POINT2 pt = null, pt0 = null, pt1 = null;
1382            int n = tg.Pixels.size();
1383            //for(j=0;j<tg.Pixels.size();j++)
1384            for (j = 0; j < n; j++) {
1385                pt = tg.Pixels.get(j);
1386                pt.style = tg.Pixels.get(j).style;
1387                pts.add(pt);
1388            }
1389
1390            boolean removedPt = true;
1391            //order of priority is: keep anchor points, then boundary points, then segmented points
1392            outer:
1393            while (removedPt == true) {
1394                removedPt = false;
1395                //n=pts.size();
1396                for (j = 0; j < pts.size() - 1; j++) {
1397                    pt0 = pts.get(j);
1398                    pt1 = pts.get(j + 1);
1399                    dist = lineutility.CalcDistanceDouble(pts.get(j), pts.get(j + 1));
1400                    if (dist < minSpikeDistance) {
1401                        if (segmented == false) {
1402                            if (j + 1 == pts.size() - 1) {
1403                                pts.remove(j);
1404                            } else {
1405                                pts.remove(j + 1);
1406                            }
1407
1408                            removedPt = true;
1409                            break outer;
1410                        } else if (pt0.style == 0 && pt1.style == -1)//-1 are clipped boundary points
1411                        {
1412                            pts.remove(j + 1);
1413                            removedPt = true;
1414                            break outer;
1415                        } else if (pt0.style == 0 && pt1.style == -2)//-2 are segmented points, this should never happen
1416                        {
1417                            pts.remove(j + 1);
1418                            removedPt = true;
1419                            break outer;
1420                        } else if (pt0.style == -1 && pt1.style == 0) {
1421                            pts.remove(j);
1422                            removedPt = true;
1423                            break outer;
1424                        } else if (pt0.style == -1 && pt1.style == -1) {
1425                            pts.remove(j + 1);
1426                            removedPt = true;
1427                            break outer;
1428                        } else if (pt0.style == -1 && pt1.style == -2) {
1429                            pts.remove(j + 1);
1430                            removedPt = true;
1431                            break outer;
1432                        } else if (pt0.style == -2 && pt1.style == 0)//this should never happen
1433                        {
1434                            pts.remove(j);
1435                            removedPt = true;
1436                            break outer;
1437                        } else if (pt0.style == -2 && pt1.style == -1) {
1438                            pts.remove(j);
1439                            removedPt = true;
1440                            break outer;
1441                        } else if (pt0.style == -2 && pt1.style == -2) {
1442                            pts.remove(j + 1);
1443                            removedPt = true;
1444                            break outer;
1445                        }
1446                    }
1447                    //n=pts.size();
1448                }
1449            }
1450            tg.Pixels = pts;
1451            tg.LatLongs = armyc2.c5isr.RenderMultipoints.clsUtility.PixelsToLatLong(pts, converter);
1452
1453        } catch (Exception exc) {
1454            ErrorLogger.LogException(_className, "FilterPoints2",
1455                    new RendererException("Failed inside FilterPoints2", exc));
1456
1457        }
1458    }
1459
1460    /**
1461     * returns true if the line type can be clipped before calculating the
1462     * shapes
1463     *
1464     * @param tg tactical graphic
1465     * @return true if can pre-clip points
1466     */
1467    public static Boolean canClipPoints(TGLight tg) {
1468        try {
1469            String symbolId = tg.get_SymbolId();
1470            if (clsMETOC.IsWeather(symbolId) > 0) {
1471                return true;
1472            }
1473
1474            int linetype = tg.get_LineType();
1475            switch (linetype) {
1476                case TacticalLines.ABATIS:
1477//                case TacticalLines.BOUNDARY:
1478                case TacticalLines.FLOT:
1479                case TacticalLines.LC:
1480                case TacticalLines.PL:
1481                case TacticalLines.DECISION_LINE:
1482                case TacticalLines.FEBA:
1483                case TacticalLines.LL:
1484                case TacticalLines.EWL:
1485                case TacticalLines.GENERAL:
1486                case TacticalLines.JTAA:
1487                case TacticalLines.SAA:
1488                case TacticalLines.SGAA:
1489                case TacticalLines.BS_AREA:
1490                case TacticalLines.BS_LINE:
1491                case TacticalLines.ASSY:
1492                case TacticalLines.EA:
1493                case TacticalLines.FORT_REVD:
1494                case TacticalLines.FORT:
1495                case TacticalLines.DZ:
1496                case TacticalLines.EZ:
1497                case TacticalLines.LZ:
1498                case TacticalLines.PZ:
1499                case TacticalLines.LAA:
1500                case TacticalLines.ROZ:
1501                case TacticalLines.AARROZ:
1502                case TacticalLines.UAROZ:
1503                case TacticalLines.WEZ:
1504                case TacticalLines.FEZ:
1505                case TacticalLines.JEZ:
1506                case TacticalLines.FAADZ:
1507                case TacticalLines.HIDACZ:
1508                case TacticalLines.MEZ:
1509                case TacticalLines.LOMEZ:
1510                case TacticalLines.HIMEZ:
1511                case TacticalLines.WFZ_REVD:
1512                case TacticalLines.WFZ:
1513                case TacticalLines.AIRFIELD:
1514                case TacticalLines.BATTLE:
1515                case TacticalLines.PNO:
1516                case TacticalLines.DIRATKAIR:
1517                case TacticalLines.DIRATKGND:
1518                case TacticalLines.DIRATKSPT:
1519                case TacticalLines.INFILTRATION:
1520                case TacticalLines.FCL:
1521                case TacticalLines.HOLD:
1522                case TacticalLines.BRDGHD:
1523                case TacticalLines.HOLD_GE:
1524                case TacticalLines.BRDGHD_GE:
1525                case TacticalLines.LOA:
1526                case TacticalLines.LOD:
1527                case TacticalLines.LDLC:
1528                case TacticalLines.PLD:
1529                case TacticalLines.ASSAULT:
1530                case TacticalLines.ATKPOS:
1531                case TacticalLines.OBJ:
1532                case TacticalLines.PEN:
1533                case TacticalLines.RELEASE:
1534                case TacticalLines.HOL:
1535                case TacticalLines.BHL:
1536                case TacticalLines.AO:
1537                case TacticalLines.AIRHEAD:
1538                case TacticalLines.ENCIRCLE:
1539                case TacticalLines.NAI:
1540                case TacticalLines.TAI:
1541                case TacticalLines.BASE_CAMP_REVD:
1542                case TacticalLines.BASE_CAMP:
1543                case TacticalLines.GUERILLA_BASE_REVD:
1544                case TacticalLines.GUERILLA_BASE:
1545                case TacticalLines.GENERIC_AREA:
1546                case TacticalLines.LINE:
1547                case TacticalLines.ZONE:
1548                case TacticalLines.OBSAREA:
1549                case TacticalLines.OBSFAREA:
1550                case TacticalLines.ATDITCH:
1551                case TacticalLines.ATDITCHC:
1552                case TacticalLines.ATDITCHM:
1553                case TacticalLines.ATWALL:
1554                case TacticalLines.DEPICT:
1555                case TacticalLines.MINED:
1556                case TacticalLines.FENCED:
1557                case TacticalLines.UXO:
1558                case TacticalLines.UNSP:
1559                case TacticalLines.SFENCE:
1560                case TacticalLines.DFENCE:
1561                case TacticalLines.DOUBLEA:
1562                case TacticalLines.LWFENCE:
1563                case TacticalLines.HWFENCE:
1564                case TacticalLines.SINGLEC:
1565                case TacticalLines.DOUBLEC:
1566                case TacticalLines.TRIPLE:
1567                case TacticalLines.FORTL:
1568                case TacticalLines.STRONG:
1569                case TacticalLines.RAD:
1570                case TacticalLines.RADT:
1571                case TacticalLines.BIO:
1572                case TacticalLines.BIOT:
1573                case TacticalLines.NUC:
1574                case TacticalLines.CHEM:
1575                case TacticalLines.CHEMT:
1576                case TacticalLines.DRCL:
1577                case TacticalLines.LINTGT:
1578                case TacticalLines.LINTGTS:
1579                case TacticalLines.FPF:
1580                case TacticalLines.FSCL:
1581                case TacticalLines.BCL_REVD:
1582                case TacticalLines.BCL:
1583                case TacticalLines.ICL:
1584                case TacticalLines.IFF_OFF:
1585                case TacticalLines.IFF_ON:
1586                case TacticalLines.GENERIC_LINE:
1587                case TacticalLines.CFL:
1588                case TacticalLines.TRIP:
1589                case TacticalLines.OVERHEAD_WIRE:
1590                case TacticalLines.NFL:
1591                case TacticalLines.MFP:
1592                case TacticalLines.RFL:
1593                case TacticalLines.AT:
1594                case TacticalLines.SERIES:
1595                case TacticalLines.STRIKWARN:
1596                case TacticalLines.SMOKE:
1597                case TacticalLines.BOMB:
1598                case TacticalLines.FSA:
1599                case TacticalLines.ACA:
1600                case TacticalLines.FFA:
1601                case TacticalLines.NFA:
1602                case TacticalLines.RFA:
1603                case TacticalLines.PAA:
1604                case TacticalLines.ATI:
1605                case TacticalLines.CFFZ:
1606                case TacticalLines.CFZ:
1607                case TacticalLines.SENSOR:
1608                case TacticalLines.CENSOR:
1609                case TacticalLines.DA:
1610                case TacticalLines.ZOR:
1611                case TacticalLines.TBA:
1612                case TacticalLines.TVAR:
1613                case TacticalLines.KILLBOXBLUE:
1614                case TacticalLines.KILLBOXPURPLE:
1615//                case TacticalLines.MSR:
1616//                case TacticalLines.ASR:
1617                case TacticalLines.MSR_ONEWAY:
1618                case TacticalLines.MSR_TWOWAY:
1619                case TacticalLines.MSR_ALT:
1620                case TacticalLines.ASR_ONEWAY:
1621                case TacticalLines.ASR_TWOWAY:
1622                case TacticalLines.ASR_ALT:
1623                case TacticalLines.TRAFFIC_ROUTE_ONEWAY:
1624                case TacticalLines.TRAFFIC_ROUTE_ALT:
1625                case TacticalLines.DHA_REVD:
1626                case TacticalLines.DHA:
1627                case TacticalLines.KILL_ZONE:
1628                case TacticalLines.EPW:
1629                case TacticalLines.FARP:
1630                case TacticalLines.RHA:
1631                case TacticalLines.BSA:
1632                case TacticalLines.DSA:
1633                case TacticalLines.CSA:
1634                case TacticalLines.RSA:
1635                case TacticalLines.TGMF:
1636                    return true;
1637                case TacticalLines.MSR: //post clip these so there are identical points regardless whether segment data is set 10-5-16
1638                case TacticalLines.ASR:
1639                case TacticalLines.TRAFFIC_ROUTE:
1640                case TacticalLines.BOUNDARY:
1641                    return false;
1642                default:
1643                    return false;
1644            }
1645        } catch (Exception exc) {
1646            ErrorLogger.LogException(_className, "canClipPoints",
1647                    new RendererException("Failed inside canClipPoints", exc));
1648        }
1649        return false;
1650    }
1651
1652    /**
1653     * These get clipped so the fill must be treated as a separate shape.
1654     * Normally lines with fill do not have a separate shape for the fill.
1655     *
1656     * @param linetype
1657     * @return
1658     */
1659    protected static boolean LinesWithSeparateFill(int linetype, ArrayList<Shape2> shapes) {
1660        if (shapes == null) {
1661            return false;
1662        }
1663
1664        switch (linetype) {
1665            case TacticalLines.MSDZ:
1666                return true;
1667            //treat these as lines: because of the feint they need an extra shape for the fill
1668            case TacticalLines.OBSFAREA:
1669            case TacticalLines.OBSAREA:
1670            case TacticalLines.STRONG:
1671            case TacticalLines.ZONE:
1672            case TacticalLines.FORT_REVD:
1673            case TacticalLines.FORT:
1674            case TacticalLines.ENCIRCLE:
1675            //return true;
1676            case TacticalLines.FIX:
1677            case TacticalLines.BOUNDARY:
1678            case TacticalLines.FLOT:
1679            case TacticalLines.LC:
1680            case TacticalLines.PL:
1681            case TacticalLines.DECISION_LINE:
1682            case TacticalLines.FEBA:
1683            case TacticalLines.LL:
1684            case TacticalLines.EWL:
1685            case TacticalLines.AC:
1686            case TacticalLines.MRR:
1687            case TacticalLines.SL:
1688            case TacticalLines.TC:
1689            case TacticalLines.SAAFR:
1690            case TacticalLines.SC:
1691            case TacticalLines.LLTR:
1692            case TacticalLines.DIRATKAIR:
1693            case TacticalLines.DIRATKGND:
1694            case TacticalLines.DIRATKSPT:
1695            case TacticalLines.INFILTRATION:
1696            case TacticalLines.FCL:
1697            case TacticalLines.HOLD:
1698            case TacticalLines.BRDGHD:
1699            case TacticalLines.HOLD_GE:
1700            case TacticalLines.BRDGHD_GE:
1701            case TacticalLines.LOA:
1702            case TacticalLines.LOD:
1703            case TacticalLines.LDLC:
1704            case TacticalLines.PLD:
1705            case TacticalLines.RELEASE:
1706            case TacticalLines.HOL:
1707            case TacticalLines.BHL:
1708            case TacticalLines.LINE:
1709            case TacticalLines.ABATIS:
1710            case TacticalLines.ATDITCH:
1711            case TacticalLines.ATDITCHC:
1712            case TacticalLines.ATDITCHM:
1713            case TacticalLines.ATWALL:
1714            case TacticalLines.MNFLDFIX:
1715            case TacticalLines.UNSP:
1716            case TacticalLines.SFENCE:
1717            case TacticalLines.DFENCE:
1718            case TacticalLines.DOUBLEA:
1719            case TacticalLines.LWFENCE:
1720            case TacticalLines.HWFENCE:
1721            case TacticalLines.SINGLEC:
1722            case TacticalLines.DOUBLEC:
1723            case TacticalLines.TRIPLE:
1724            case TacticalLines.FORTL:
1725            case TacticalLines.LINTGT:
1726            case TacticalLines.LINTGTS:
1727            case TacticalLines.FSCL:
1728            case TacticalLines.BCL_REVD:
1729            case TacticalLines.BCL:
1730            case TacticalLines.ICL:
1731            case TacticalLines.IFF_OFF:
1732            case TacticalLines.IFF_ON:
1733            case TacticalLines.GENERIC_LINE:
1734            case TacticalLines.CFL:
1735            case TacticalLines.TRIP:
1736            case TacticalLines.NFL:
1737            case TacticalLines.MFP:
1738            case TacticalLines.RFL:
1739            case TacticalLines.MSR:
1740            case TacticalLines.MSR_ONEWAY:
1741            case TacticalLines.MSR_TWOWAY:
1742            case TacticalLines.MSR_ALT:
1743            case TacticalLines.ASR:
1744            case TacticalLines.ASR_ONEWAY:
1745            case TacticalLines.ASR_TWOWAY:
1746            case TacticalLines.ASR_ALT:
1747            case TacticalLines.TRAFFIC_ROUTE:
1748            case TacticalLines.TRAFFIC_ROUTE_ONEWAY:
1749            case TacticalLines.TRAFFIC_ROUTE_ALT:
1750                //undo any fill
1751                Shape2 shape = null;
1752                if (shapes != null && shapes.size() > 0) {
1753                    int n = shapes.size();
1754                    //for(int j=0;j<shapes.size();j++)
1755                    for (int j = 0; j < n; j++) {
1756                        shape = shapes.get(j);
1757                        if (shape.getShapeType() == Shape2.SHAPE_TYPE_POLYLINE) {
1758                            shapes.get(j).setFillColor(null);
1759                        }
1760                    }
1761                }
1762                return true;
1763            default:
1764                return false;
1765
1766        }
1767    }
1768
1769    /**
1770     * uses a hash map to set the POINT2 style when creating tg.Pixels from
1771     * Point2D ArrayList
1772     *
1773     * @param pts2d
1774     * @param hashMap
1775     * @return
1776     */
1777    protected static ArrayList<POINT2> Point2DtoPOINT2Mapped(ArrayList<Point2D> pts2d, Map<String, Object> hashMap) {
1778        ArrayList<POINT2> pts = new ArrayList();
1779        try {
1780            Point2D pt2d;
1781            int style = 0;
1782            int n = pts2d.size();
1783            //for(int j=0;j<pts2d.size();j++)
1784            for (int j = 0; j < n; j++) {
1785                pt2d = pts2d.get(j);
1786                //the hash map contains the original tg.Pixels before clipping
1787                if (hashMap.containsValue(pt2d)) {
1788                    style = 0;
1789                } else {
1790                    style = -1;   //style set to -1 identifies it as a clip bounds point
1791                }
1792                pts.add(new POINT2(pts2d.get(j).getX(), pts2d.get(j).getY(), style));
1793            }
1794        } catch (Exception exc) {
1795            ErrorLogger.LogException(_className, "Point2DToPOINT2Mapped",
1796                    new RendererException("Failed inside Point2DToPOINT2Mapped", exc));
1797        }
1798        return pts;
1799    }
1800
1801    protected static ArrayList<POINT2> Point2DtoPOINT2(ArrayList<Point2D> pts2d) {
1802        ArrayList<POINT2> pts = new ArrayList();
1803        try {
1804            int n = pts2d.size();
1805            //for(int j=0;j<pts2d.size();j++)
1806            for (int j = 0; j < n; j++) {
1807                pts.add(new POINT2(pts2d.get(j).getX(), pts2d.get(j).getY()));
1808            }
1809        } catch (Exception exc) {
1810            ErrorLogger.LogException(_className, "Point2DToPOINT2",
1811                    new RendererException("Failed inside Point2DToPOINT2", exc));
1812        }
1813        return pts;
1814    }
1815
1816    protected static ArrayList<Point2D> POINT2toPoint2D(ArrayList<POINT2> pts) {
1817        ArrayList<Point2D> pts2d = new ArrayList();
1818        try {
1819            int n = pts.size();
1820            //for(int j=0;j<pts.size();j++)
1821            for (int j = 0; j < n; j++) {
1822                pts2d.add(new Point2D.Double(pts.get(j).x, pts.get(j).y));
1823            }
1824
1825        } catch (Exception exc) {
1826            ErrorLogger.LogException(_className, "POINT2toPoint2D",
1827                    new RendererException("Failed inside POINT2toPoint2D", exc));
1828        }
1829        return pts2d;
1830    }
1831
1832    /**
1833     * Builds a single shape from a point array. Currently we assume the array
1834     * represents a moveTo followed by a series of lineTo operations
1835     *
1836     * @param pts2d
1837     * @return
1838     */
1839    private static Shape BuildShapeFromPoints(ArrayList<Point2D> pts2d) {
1840        GeneralPath shape = new GeneralPath();
1841        try {
1842            shape.moveTo(pts2d.get(0).getX(), pts2d.get(0).getY());
1843            int n = pts2d.size();
1844            //for(int j=1;j<pts2d.size();j++)
1845            for (int j = 1; j < n; j++) {
1846                shape.lineTo(pts2d.get(j).getX(), pts2d.get(j).getY());
1847            }
1848        } catch (Exception exc) {
1849            ErrorLogger.LogException(_className, "buildShapeFromPoints",
1850                    new RendererException("Failed inside buildShapeFromPoints", exc));
1851
1852        }
1853        return shape;
1854    }
1855
1856    /**
1857     * Clips a ShapeSpec. Assumes we are not post clipping splines, therefore
1858     * all the operations are moveTo, lineTo. Each ShapeSpec is assumed to be:
1859     * moveTo, lineTo ... lineTo, followed by another moveTo, lineTo, ...
1860     * lineTo, followed by ...
1861     *
1862     * @param shapeSpec
1863     * @param pts
1864     * @param clipArea
1865     * @return a single clipped shapeSpec
1866     */
1867    protected static ArrayList<Shape2> buildShapeSpecFromPoints(TGLight tg0,
1868            Shape2 shapeSpec, //the original ShapeSpec
1869            ArrayList<POINT2> pts,
1870            Object clipArea) {
1871        ArrayList<Shape2> shapeSpecs2 = null;
1872        Shape2 shapeSpec2;
1873        try {
1874            //create a tg to use for the clip
1875            shapeSpecs2 = new ArrayList();
1876            int j = 0, n = 0;
1877            //return null if it is outside the bounds
1878            Rectangle rect = shapeSpec.getBounds();
1879            int h = shapeSpec.getBounds().height;
1880            int w = shapeSpec.getBounds().width;
1881            int x = shapeSpec.getBounds().x;
1882            int y = shapeSpec.getBounds().y;
1883//            if(h==0 && w==0)
1884//                return shapeSpecs2;
1885
1886            if (h == 0) {
1887                h = 1;
1888            }
1889            if (w == 0) {
1890                w = 1;
1891            }
1892
1893            Rectangle2D clipBounds = null;
1894            ArrayList<Point2D> clipPoints = null;
1895            if (clipArea != null && clipArea.getClass().isAssignableFrom(Rectangle2D.Double.class)) {
1896                clipBounds = (Rectangle2D) clipArea;
1897            } else if (clipArea != null && clipArea.getClass().isAssignableFrom(Rectangle.class)) {
1898                //clipBounds=(Rectangle2D)clipArea;
1899                Rectangle rectx = (Rectangle) clipArea;
1900                clipBounds = new Rectangle2D.Double(rectx.x, rectx.y, rectx.width, rectx.height);
1901            } else if (clipArea != null && clipArea.getClass().isAssignableFrom(ArrayList.class)) {
1902                clipPoints = (ArrayList<Point2D>) clipArea;
1903            }
1904
1905            if (clipBounds != null && clipBounds.contains(shapeSpec.getShape().getBounds2D()) == false
1906                    && clipBounds.intersects(shapeSpec.getShape().getBounds2D()) == false) {
1907                //this tests if the shape has height or width 0
1908                //but may be contained within the clipbounds or intersect it
1909                //in that case we gave it a default width or thickness of 1
1910                if (clipBounds.contains(x, y, w, h) == false
1911                        && clipBounds.intersects(x, y, w, h) == false) {
1912                    return shapeSpecs2;
1913                }
1914            } else if (clipPoints != null) {
1915                GeneralPath poly = new GeneralPath();
1916                n = clipPoints.size();
1917                //for(j=0;j<clipPoints.size();j++)
1918                for (j = 0; j < n; j++) {
1919                    if (j == 0) {
1920                        poly.moveTo(clipPoints.get(j).getX(), clipPoints.get(j).getY());
1921                    } else {
1922                        poly.lineTo(clipPoints.get(j).getX(), clipPoints.get(j).getY());
1923                    }
1924                }
1925                poly.closePath();
1926                if (poly.contains(shapeSpec.getShape().getBounds2D()) == false
1927                        && poly.intersects(shapeSpec.getShape().getBounds2D()) == false) {
1928                    if (poly.contains(x, y, w, h) == false
1929                            && poly.intersects(x, y, w, h) == false) {
1930                        return shapeSpecs2;
1931                    }
1932                }
1933            }
1934
1935            if (shapeSpec.getShapeType() == Shape2.SHAPE_TYPE_MODIFIER
1936                    || shapeSpec.getShapeType() == Shape2.SHAPE_TYPE_MODIFIER_FILL) {
1937                shapeSpecs2.add(shapeSpec);
1938                return shapeSpecs2;
1939            }
1940            TGLight tg = new TGLight();
1941            POINT2 pt = null;
1942            tg.set_LineType(TacticalLines.PL);
1943            ArrayList<POINT2> pts2 = new ArrayList();
1944            ArrayList<Point2D> pts2d = null;
1945            Shape shape = null;
1946            GeneralPath gp = new GeneralPath();
1947            //loop through the points
1948            n = pts.size();
1949            //for(j=0;j<pts.size();j++)
1950            for (j = 0; j < n; j++) {
1951                pt = pts.get(j);
1952                //new line
1953                switch (pt.style) {
1954                    case 0: //moveTo,
1955                        //they lifted the pencil, so we build the shape from the existing pts and append it
1956                        if (pts2.size() > 1) {
1957                            //clip the points
1958                            tg = new TGLight();
1959                            tg.set_LineType(TacticalLines.PL);
1960                            tg.Pixels = pts2;
1961                            if (clipBounds != null) {
1962                                pts2d = clsClipPolygon2.ClipPolygon(tg, clipBounds);
1963                            } else if (clipPoints != null && !clipPoints.isEmpty()) {
1964                                pts2d = clsClipQuad.ClipPolygon(tg, clipPoints);
1965                            }
1966
1967                            //build a GeneralPath from the points we collected, we will append it
1968                            if (pts2d != null && pts2d.size() > 1) {
1969                                shape = BuildShapeFromPoints(pts2d);
1970                                //append the shape because we want to return only one shape
1971                                gp.append(shape, false);
1972                            }
1973                            //clear the points array and begin the next line
1974                            pts2.clear();
1975                            pts2.add(pt);
1976                        } else {
1977                            pts2.add(pt);
1978                        }
1979                        break;
1980                    case 1: //lineTo
1981                        pts2.add(pt);
1982                        break;
1983                    default:
1984                        pts2.add(pt);
1985                        break;
1986                }
1987            }//end for
1988            //append the last shape
1989            if (pts2.size() > 1) {
1990                //clip the points
1991                tg = new TGLight();
1992                tg.set_LineType(TacticalLines.PL);
1993                tg.Pixels = pts2;
1994                if (clipBounds != null) {
1995                    pts2d = clsClipPolygon2.ClipPolygon(tg, clipBounds);
1996                } else if (clipPoints != null) {
1997                    pts2d = clsClipQuad.ClipPolygon(tg, clipPoints);
1998                }
1999                //build a GeneralPath from the points we collected, we will append it
2000                if (pts2d != null && pts2d.size() > 1) {
2001                    shape = BuildShapeFromPoints(pts2d);
2002                    gp.append(shape, false);
2003                }
2004                tg0.set_WasClipped(tg.get_WasClipped());
2005            }
2006            //create the shapespec here
2007            //initialize the clipped ShapeSpec
2008            shapeSpec2 = new Shape2(shapeSpec.getShapeType());
2009            shapeSpec2.setLineColor(shapeSpec.getLineColor());
2010            shapeSpec2.setFillColor(shapeSpec.getFillColor());
2011            shapeSpec2.setStroke(shapeSpec.getStroke());
2012            shapeSpec2.setTexturePaint(shapeSpec.getTexturePaint());
2013            shapeSpec2.setShape(gp);
2014            shapeSpecs2.add(shapeSpec2);
2015        } catch (Exception exc) {
2016            ErrorLogger.LogException(_className, "buildShapeSpecFromPoints",
2017                    new RendererException("Failed inside buildShapeSpecFromPoints", exc));
2018
2019        }
2020        return shapeSpecs2;
2021    }
2022
2023    /**
2024     * Currently assumes no MeTOC symbols are post clipped
2025     *
2026     * @param tg
2027     * @param shapeSpecsArray
2028     * @param clipArea
2029     * @return
2030     */
2031    protected static ArrayList<Shape2> postClipShapes(TGLight tg, ArrayList<Shape2> shapeSpecsArray, Object clipArea) {
2032        ArrayList<Shape2> shapeSpecs2 = null;
2033        ArrayList<Shape2> tempShapes = null;
2034        try {
2035            if (shapeSpecsArray == null || shapeSpecsArray.size() == 0) {
2036                return null;
2037            }
2038
2039            shapeSpecs2 = new ArrayList();
2040            int j = 0;
2041            ArrayList<Shape2> shapeSpecs = new ArrayList();
2042            int n = shapeSpecsArray.size();
2043            //for(j=0;j<shapeSpecsArray.size();j++)
2044            for (j = 0; j < n; j++) {
2045                shapeSpecs.add(shapeSpecsArray.get(j));
2046            }
2047
2048            ArrayList<POINT2> pts = new ArrayList();//use these
2049            Shape shape = null;
2050            POINT2 pt;
2051            double[] coords = new double[6];
2052            Shape2 shapeSpec = null;
2053            n = shapeSpecs.size();
2054            //for(j=0;j<shapeSpecs.size();j++)
2055            for (j = 0; j < n; j++) {
2056                shapeSpec = shapeSpecs.get(j);
2057                shape = shapeSpec.getShape();
2058                pts.clear();
2059                for (PathIterator i = shape.getPathIterator(null); !i.isDone(); i.next()) {
2060                    int type = i.currentSegment(coords);
2061                    switch (type) {
2062                        case PathIterator.SEG_MOVETO:
2063                            pt = new POINT2(coords[0], coords[1]);
2064                            pt.style = 0;
2065                            pts.add(pt);
2066                            break;
2067                        case PathIterator.SEG_LINETO:
2068                            pt = new POINT2(coords[0], coords[1]);
2069                            pt.style = 1;
2070                            pts.add(pt);
2071                            break;
2072                        case PathIterator.SEG_QUADTO:   //not using this
2073                            pt = new POINT2(coords[0], coords[1]);
2074                            pt.style = 2;
2075                            pts.add(pt);
2076                            pt = new POINT2(coords[2], coords[3]);
2077                            pt.style = 2;
2078                            pts.add(pt);
2079                            break;
2080                        case PathIterator.SEG_CUBICTO:  //not using this
2081                            pt = new POINT2(coords[0], coords[1]);
2082                            pt.style = 3;
2083                            pts.add(pt);
2084                            pt = new POINT2(coords[2], coords[3]);
2085                            pt.style = 3;
2086                            pts.add(pt);
2087                            pt = new POINT2(coords[4], coords[5]);
2088                            pt.style = 3;
2089                            pts.add(pt);
2090                            break;
2091                        case PathIterator.SEG_CLOSE://not using this
2092                            pt = new POINT2(coords[0], coords[1]);
2093                            pt.style = 4;
2094                            pts.add(pt);
2095                            break;
2096                        default:
2097                            pt = null;
2098                            break;
2099                    }//end switch
2100                }   //end for pathiterator i
2101                tempShapes = buildShapeSpecFromPoints(tg, shapeSpec, pts, clipArea);
2102                shapeSpecs2.addAll(tempShapes);
2103            }
2104        } catch (Exception exc) {
2105            ErrorLogger.LogException(_className, "postClipShapes",
2106                    new RendererException("Failed inside postClipShapes", exc));
2107        }
2108        return shapeSpecs2;
2109    }
2110
2111    /**
2112     * For the 3d map we cannot pre-segment the auto-shapes or fire support
2113     * areas. We do need to pre-segment generic lines regardless of the status
2114     * if clipping is set. Currently we are not pre-segmenting axis of advance
2115     * symbols.
2116     *
2117     * @param tg
2118     * @return true if pre-segmenting is to be used
2119     */
2120    private static boolean segmentAnticipatedLine(TGLight tg) {
2121        try {
2122            int linetype = tg.get_LineType();
2123            //do not pre-segment the fire support rectangular and circular areas
2124            if (clsUtility.IsChange1Area(linetype)) {
2125                return false;
2126            }
2127            //do not pre-segment the autoshapes
2128            if (clsUtility.isAutoshape(tg)) {
2129                return false;
2130            }
2131            if (SymbolUtilities.isBasicShape(linetype)) {
2132                return false;
2133            }
2134            //temporarily do not pre-segment the channel types.
2135            switch (linetype) {
2136                case TacticalLines.OVERHEAD_WIRE:
2137                case TacticalLines.CATK:
2138                case TacticalLines.CATKBYFIRE:
2139                case TacticalLines.MAIN:
2140                case TacticalLines.SPT:
2141                case TacticalLines.FRONTAL_ATTACK:
2142                case TacticalLines.TURNING_MOVEMENT:
2143                case TacticalLines.MOVEMENT_TO_CONTACT:
2144                case TacticalLines.AIRAOA:
2145                case TacticalLines.AAAAA:
2146                    return false;
2147                case TacticalLines.MSR_ONEWAY:
2148                case TacticalLines.MSR_TWOWAY:
2149                case TacticalLines.MSR_ALT:
2150                case TacticalLines.ASR_ONEWAY:
2151                case TacticalLines.ASR_TWOWAY:
2152                case TacticalLines.ASR_ALT:
2153                case TacticalLines.TRAFFIC_ROUTE_ONEWAY:
2154                case TacticalLines.TRAFFIC_ROUTE_ALT:
2155                    //added because of segment data 4-22-13
2156                    //removed from this case block since we now post-clip these because of segment color data 10-5-16
2157//                case TacticalLines.MSR:
2158//                case TacticalLines.ASR:
2159//                case TacticalLines.BOUNDARY:
2160                    return false;
2161                default:
2162                    break;
2163            }
2164
2165        } catch (Exception exc) {
2166            ErrorLogger.LogException(_className, "segmentGenericLine",
2167                    new RendererException("Failed inside segmentGenericLine", exc));
2168        }
2169        return true;
2170    }
2171
2172    /**
2173     * cannot pre-segment the fire support areas, must post segment them after
2174     * the pixels were calculated
2175     *
2176     * @param tg
2177     * @param converter
2178     */
2179    protected static void postSegmentFSA(TGLight tg,
2180            IPointConversion converter) {
2181        try {
2182            if (tg.get_Client().equals("2D")) {
2183                return;
2184            }
2185
2186            int linetype = tg.get_LineType();
2187            switch (linetype) {
2188                case TacticalLines.PAA_RECTANGULAR:
2189                case TacticalLines.FSA_RECTANGULAR:
2190                case TacticalLines.SHIP_AOI_RECTANGULAR:
2191                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
2192                case TacticalLines.FFA_RECTANGULAR:
2193                case TacticalLines.ACA_RECTANGULAR:
2194                case TacticalLines.NFA_RECTANGULAR:
2195                case TacticalLines.RFA_RECTANGULAR:
2196                case TacticalLines.ATI_RECTANGULAR:
2197                case TacticalLines.CFFZ_RECTANGULAR:
2198                case TacticalLines.SENSOR_RECTANGULAR:
2199                case TacticalLines.CENSOR_RECTANGULAR:
2200                case TacticalLines.DA_RECTANGULAR:
2201                case TacticalLines.CFZ_RECTANGULAR:
2202                case TacticalLines.ZOR_RECTANGULAR:
2203                case TacticalLines.TBA_RECTANGULAR:
2204                case TacticalLines.TVAR_RECTANGULAR:
2205                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
2206                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
2207                    break;
2208                default:
2209                    return;
2210            }
2211            ArrayList<POINT2> latLongs = new ArrayList();
2212            ArrayList<POINT2> resultPts = new ArrayList();
2213            int j = 0, k = 0, n = 0;
2214            POINT2 pt0 = null, pt1 = null, pt = null;
2215            double dist = 0;
2216            //double interval=1000000;
2217            double interval = 250000;
2218            double az = 0;
2219
2220            double maxDist = 0;
2221            Point2D pt2d = null;
2222            int t = tg.Pixels.size();
2223            //for(j=0;j<tg.Pixels.size();j++)
2224            for (j = 0; j < t; j++) {
2225                pt0 = tg.Pixels.get(j);
2226                pt2d = new Point2D.Double(pt0.x, pt0.y);
2227                pt2d = converter.PixelsToGeo(pt2d);
2228                pt0 = new POINT2(pt2d.getX(), pt2d.getY());
2229                latLongs.add(pt0);
2230            }
2231            t = latLongs.size();
2232            //for(j=0;j<latLongs.size()-1;j++)
2233            for (j = 0; j < t - 1; j++) {
2234                pt0 = latLongs.get(j);
2235                pt1 = latLongs.get(j + 1);
2236                pt1.style = -1;//end point
2237                az = mdlGeodesic.GetAzimuth(pt0, pt1);
2238                dist = mdlGeodesic.geodesic_distance(latLongs.get(j), latLongs.get(j + 1), null, null);
2239                if (dist > maxDist) {
2240                    maxDist = dist;
2241                }
2242            }
2243
2244            if (interval > maxDist) {
2245                interval = maxDist;
2246            }
2247
2248            //for(j=0;j<latLongs.size()-1;j++)
2249            for (j = 0; j < t - 1; j++) {
2250                pt0 = new POINT2(latLongs.get(j));
2251                pt0.style = 0;//anchor point
2252                pt1 = new POINT2(latLongs.get(j + 1));
2253                pt1.style = 0;//anchor point point
2254                az = mdlGeodesic.GetAzimuth(pt0, pt1);
2255                dist = mdlGeodesic.geodesic_distance(latLongs.get(j), latLongs.get(j + 1), null, null);
2256
2257                n = (int) (dist / interval);
2258                if (j == 0) {
2259                    resultPts.add(pt0);
2260                }
2261
2262                for (k = 1; k <= n; k++) {
2263                    pt = mdlGeodesic.geodesic_coordinate(pt0, interval * k, az);
2264                    pt.style = -2;
2265                    //we do not want the last segment to be too close to the anchor point
2266                    //only add the segment point if it is a distance at least half the inteval
2267                    //from the 2nd anchor point
2268                    dist = mdlGeodesic.geodesic_distance(pt, pt1, null, null);
2269                    if (dist >= interval / 2) {
2270                        resultPts.add(pt);
2271                    }
2272                }
2273                //ad the 2nd anchor point
2274                resultPts.add(pt1);
2275            }
2276            latLongs = resultPts;
2277            tg.Pixels = armyc2.c5isr.RenderMultipoints.clsUtility.LatLongToPixels(latLongs, converter);
2278        } catch (Exception exc) {
2279            ErrorLogger.LogException(_className, "postSegmentFSA",
2280                    new RendererException("Failed inside postSegmentFSA", exc));
2281        }
2282    }
2283
2284    /**
2285     * Similar to Vincenty algorithm for more accurate interpolation of geo
2286     * anchor points
2287     *
2288     * @return the interpolated points
2289     */
2290    private static ArrayList<POINT2> toGeodesic(TGLight tg, double interval, HashMap<Integer,String> hmap) {
2291        ArrayList<POINT2> locs = new ArrayList<POINT2>();
2292        try {
2293            int i = 0, k = 0, n = 0;
2294            ArrayList<POINT2> points = tg.LatLongs;
2295            String H = "";
2296            String color = "";
2297            boolean bolIsAC = false;
2298            int acWidth = 0;
2299            int linetype = tg.get_LineType();
2300            switch (linetype) {
2301                case TacticalLines.AC:
2302                case TacticalLines.LLTR:
2303                case TacticalLines.MRR:
2304                case TacticalLines.SL:
2305                case TacticalLines.SAAFR:
2306                case TacticalLines.TC:
2307                case TacticalLines.SC:
2308                    bolIsAC = true;
2309                    break;
2310                default:
2311                    break;
2312            }
2313            for (i = 0; i < points.size() - 1; i++) {
2314                if(bolIsAC)
2315                    acWidth=points.get(i).style;
2316                // Convert coordinates from degrees to Radians
2317                //var lat1 = points[i].latitude * (PI / 180);
2318                //var lon1 = points[i].longitude * (PI / 180);
2319                //var lat2 = points[i + 1].latitude * (PI / 180);
2320                //var lon2 = points[i + 1].longitude * (PI / 180);                
2321                double lat1 = Math.toRadians(points.get(i).y);
2322                double lon1 = Math.toRadians(points.get(i).x);
2323                double lat2 = Math.toRadians(points.get(i + 1).y);
2324                double lon2 = Math.toRadians(points.get(i + 1).x);
2325                // Calculate the total extent of the route
2326                //var d = 2 * asin(sqrt(pow((sin((lat1 - lat2) / 2)), 2) + cos(lat1) * cos(lat2) * pow((sin((lon1 - lon2) / 2)), 2)));
2327                double d = 2 * Math.asin(Math.sqrt(Math.pow((Math.sin((lat1 - lat2) / 2)), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow((Math.sin((lon1 - lon2) / 2)), 2)));
2328
2329                double dist = mdlGeodesic.geodesic_distance(points.get(i), points.get(i + 1), null, null);
2330                //double dist=d;
2331                float flt = (float) dist / (float) interval;
2332                n = Math.round(flt);
2333                if (n < 1) {
2334                    n = 1;
2335                }
2336                if (n > 32) {
2337                    n = 32;
2338                }
2339                // Calculate  positions at fixed intervals along the route
2340                for (k = 0; k <= n; k++) {
2341                    //we must preserve the anchor points
2342                    if (k == 0) {
2343                        locs.add(new POINT2(points.get(i)));
2344                        if (hmap != null && hmap.containsKey(i)) {
2345                            if (!H.isEmpty()) {
2346                                H += ",";
2347                            }
2348                            color = (String) hmap.get(i);
2349                            H += Integer.toString(locs.size() - 1) + ":" + color;
2350                        }
2351                        continue;
2352                    } else if (k == n) {
2353                        if (i == points.size() - 2) {
2354                            locs.add(new POINT2(points.get(i + 1)));
2355                            if (hmap != null && hmap.containsKey(i + 1)) {
2356                                if (!H.isEmpty()) {
2357                                    H += ",";
2358                                }
2359                                color = (String) hmap.get(i + 1);
2360                                H += Integer.toString(locs.size() - 1) + ":" + color;
2361                            }
2362                        }
2363                        break;
2364                    }
2365                    //var f = (k / n);
2366                    //var A = sin((1 - f) * d) / sin(d);
2367                    //var B = sin(f * d) / sin(d);
2368                    double f = ((double) k / (double) n);
2369                    double A = Math.sin((1 - f) * d) / Math.sin(d);
2370                    double B = Math.sin(f * d) / Math.sin(d);
2371                    // Obtain 3D Cartesian coordinates of each point
2372                    //var x = A * cos(lat1) * cos(lon1) + B * cos(lat2) * cos(lon2);
2373                    //var y = A * cos(lat1) * sin(lon1) + B * cos(lat2) * sin(lon2);
2374                    //var z = A * sin(lat1) + B * sin(lat2);
2375                    double x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) * Math.cos(lon2);
2376                    double y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) * Math.sin(lon2);
2377                    double z = A * Math.sin(lat1) + B * Math.sin(lat2);
2378                    // Convert these to latitude/longitude
2379                    //var lat = atan2(z, sqrt(pow(x, 2) + pow(y, 2)));
2380                    //var lon = atan2(y, x);
2381                    double lat = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
2382                    double lon = Math.atan2(y, x);
2383                    lat *= 180.0 / Math.PI;
2384                    lon *= 180.0 / Math.PI;
2385                    POINT2 pt = new POINT2(lon, lat);
2386                    if(bolIsAC)
2387                        pt.style=-acWidth;
2388                    locs.add(pt);
2389                    if (hmap != null && hmap.containsKey(i)) {
2390                        if (!H.isEmpty()) {
2391                            H += ",";
2392                        }
2393                        color = (String) hmap.get(i);
2394                        H += Integer.toString(locs.size() - 1) + ":" + color;
2395                    }
2396                }
2397            }
2398            if (!H.isEmpty()) {
2399                tg.set_H(H);
2400            }
2401        } catch (Exception exc) {
2402            ErrorLogger.LogException(_className, "toGeodesic",
2403                    new RendererException("Failed inside toGeodesic", exc));
2404            return null;
2405        }
2406        return locs;
2407    }
2408
2409    /**
2410     * Pre-segment the lines based on max or min latitude for the segment
2411     * interval. This is necessary because GeoPixelconversion does not work well
2412     * over distance greater than 1M meters, especially at extreme latitudes.
2413     *
2414     * @param tg
2415     * @param converter
2416     */
2417    protected static void SegmentGeoPoints(TGLight tg,
2418            IPointConversion converter,
2419            double zoomFactor) {
2420        try {
2421            if (tg.get_Client().equals("2D")) {
2422                return;
2423            }
2424
2425            ArrayList<POINT2> resultPts = new ArrayList();
2426            int lineType = tg.get_LineType();
2427            //double interval=1000000;
2428            double interval = 250000;
2429            boolean bolSegmentAC = false, bolIsAC = false;
2430            bolSegmentAC = true;
2431            //conservative interval in meters
2432            //return early for those lines not requiring pre-segmenting geo points
2433            switch (lineType) {
2434                case TacticalLines.AC:
2435                case TacticalLines.LLTR:
2436                case TacticalLines.MRR:
2437                case TacticalLines.SL:
2438                case TacticalLines.SAAFR:
2439                case TacticalLines.TC:
2440                case TacticalLines.SC:
2441                    if (!bolSegmentAC) {
2442                        return;
2443                    }
2444                    bolIsAC = true;
2445                    break;
2446                case TacticalLines.PLD:
2447                case TacticalLines.CFL:
2448                case TacticalLines.UNSP:
2449                case TacticalLines.TRIPLE:
2450                case TacticalLines.DOUBLEC:
2451                case TacticalLines.SINGLEC:
2452                case TacticalLines.ATDITCH:
2453                case TacticalLines.ATDITCHC:
2454                case TacticalLines.ATDITCHM:
2455                case TacticalLines.ATWALL:
2456                case TacticalLines.LINE:
2457                case TacticalLines.DIRATKAIR:
2458                case TacticalLines.STRONG:
2459                case TacticalLines.ENCIRCLE:
2460                case TacticalLines.FLOT:
2461                case TacticalLines.ZONE:
2462                case TacticalLines.OBSAREA:
2463                case TacticalLines.OBSFAREA:
2464                case TacticalLines.FORT_REVD:
2465                case TacticalLines.FORT:
2466                case TacticalLines.FORTL:
2467                    break;
2468                case TacticalLines.HWFENCE:
2469                case TacticalLines.LWFENCE:
2470                case TacticalLines.DOUBLEA:
2471                case TacticalLines.DFENCE:
2472                case TacticalLines.SFENCE:
2473                    interval = 500000;
2474                    break;
2475                case TacticalLines.LC:
2476                    interval = 2000000;
2477                    break;
2478                default:
2479                    //if the line is an anticipated generic line then segment the line
2480                    if (segmentAnticipatedLine(tg)) {
2481                        break;
2482                    }
2483                    return;
2484            }
2485
2486            int j = 0, k = 0, n = 0;
2487            POINT2 pt0 = null, pt1 = null, pt = null;
2488            double dist = 0;
2489            double az = 0;
2490
2491            double maxDist = 0;
2492            int t = tg.LatLongs.size();
2493            //for(j=0;j<tg.LatLongs.size()-1;j++)
2494            for (j = 0; j < t - 1; j++) {
2495                pt0 = tg.LatLongs.get(j);
2496                pt1 = tg.LatLongs.get(j + 1);
2497                if(!bolIsAC)
2498                    pt1.style = -1;//end point
2499                az = mdlGeodesic.GetAzimuth(pt0, pt1);
2500                dist = mdlGeodesic.geodesic_distance(tg.LatLongs.get(j), tg.LatLongs.get(j + 1), null, null);
2501                if (dist > maxDist) {
2502                    maxDist = dist;
2503                }
2504            }
2505
2506            if (interval > maxDist) {
2507                interval = maxDist;
2508            }
2509
2510            if (zoomFactor > 0 && zoomFactor < 0.01) {
2511                zoomFactor = 0.01;
2512            }
2513            if (zoomFactor > 0 && zoomFactor < 1) {
2514                interval *= zoomFactor;
2515            }
2516
2517            boolean useVincenty = false;
2518            String H = "";
2519            String color = "";
2520            HashMap<Integer,String> hmap = clsUtility.getMSRSegmentColorStrings(tg);
2521            if (hmap != null) {
2522                tg.set_H("");
2523            }
2524            //uncomment one line to use (similar to) Vincenty algorithm
2525            useVincenty = true;
2526            if (useVincenty) {
2527                resultPts = toGeodesic(tg, interval, hmap);
2528                tg.LatLongs = resultPts;
2529                tg.Pixels = armyc2.c5isr.RenderMultipoints.clsUtility.LatLongToPixels(tg.LatLongs, converter);
2530                return;
2531            }
2532
2533            for (j = 0; j < tg.LatLongs.size() - 1; j++) {
2534                pt0 = new POINT2(tg.LatLongs.get(j));
2535                pt0.style = 0;//anchor point
2536                pt1 = new POINT2(tg.LatLongs.get(j + 1));
2537                pt1.style = 0;//anchor point point
2538                az = mdlGeodesic.GetAzimuth(pt0, pt1);
2539                dist = mdlGeodesic.geodesic_distance(tg.LatLongs.get(j), tg.LatLongs.get(j + 1), null, null);
2540
2541                n = (int) (dist / interval);
2542                if (j == 0) {
2543                    resultPts.add(pt0);
2544                    if (hmap != null && hmap.containsKey(j)) {
2545                        if (!H.isEmpty()) {
2546                            H += ",";
2547                        }
2548                        color = (String) hmap.get(j);
2549                        //H+=(resultPts.size()-1).toString()+":"+color;
2550                        H += Integer.toString(resultPts.size() - 1) + ":" + color;
2551                    }
2552                }
2553                for (k = 1; k <= n; k++) {
2554                    pt = mdlGeodesic.geodesic_coordinate(pt0, interval * k, az);
2555                    pt.style = -2;
2556                    //we do not want the last segment to be too close to the anchor point
2557                    //only add the segment point if it is a distance at least half the inteval
2558                    //from the 2nd anchor point
2559                    dist = mdlGeodesic.geodesic_distance(pt, pt1, null, null);
2560                    if (dist >= interval / 2) {
2561                        resultPts.add(pt);
2562                        if (hmap != null && hmap.containsKey(j)) {
2563                            color = (String) hmap.get(j);
2564                            if (!H.isEmpty()) {
2565                                H += ",";
2566                            }
2567                            //H+=(resultPts.size()-1).toString()+":"+color;
2568                            H += Integer.toString(resultPts.size() - 1) + ":" + color;
2569                        }
2570                    }
2571                }
2572                //ad the 2nd anchor point
2573                resultPts.add(pt1);
2574                if (hmap != null && hmap.containsKey(j + 1)) {
2575                    if (!H.isEmpty()) {
2576                        H += ",";
2577                    }
2578                    color = (String) hmap.get(j + 1);
2579                    //H+=(resultPts.size()-1).toString()+":"+color;
2580                    H += Integer.toString(resultPts.size() - 1) + ":" + color;
2581                }
2582            }
2583            if (!H.isEmpty()) {
2584                tg.set_H(H);
2585            }
2586            tg.LatLongs = resultPts;
2587            tg.Pixels = armyc2.c5isr.RenderMultipoints.clsUtility.LatLongToPixels(tg.LatLongs, converter);
2588        } catch (Exception exc) {
2589            ErrorLogger.LogException(_className, "SegmentGeoPoints",
2590                    new RendererException("Failed inside SegmentGeoPoints", exc));
2591        }
2592    }
2593
2594    /**
2595     * Calculating the signed area will tell you which direction the points are going.
2596     * Negative = Clock-wise, Positive = counter clock-wise
2597     * A = 1/2 * (x1*y2 - x2*y1 + x2*y3 - x3*y2 + ... + xn*y1 - x1*yn)
2598     */
2599    static double CalculateSignedAreaOfPolygon(ArrayList<POINT2> coords) {
2600        double signedArea = 0;
2601        final int len = coords.size();
2602        for (int i = 0; i < len; i++) {
2603            double x1 = coords.get(i).x;
2604            double y1 = coords.get(i).y;
2605            double x2 = coords.get((i + 1) % len).x;
2606            double y2 = coords.get((i + 1) % len).y;
2607            signedArea += (x1 * y2 - x2 * y1);
2608        }
2609        return signedArea / 2;
2610    }
2611}