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.FEBA:
1317                case TacticalLines.LOA:
1318                case TacticalLines.LL:
1319                case TacticalLines.EWL:
1320                case TacticalLines.FCL:
1321                case TacticalLines.LOD:
1322                case TacticalLines.LDLC:
1323                case TacticalLines.PLD:
1324                case TacticalLines.HOLD:
1325                case TacticalLines.HOLD_GE:
1326                case TacticalLines.RELEASE:
1327                case TacticalLines.HOL:
1328                case TacticalLines.BHL:
1329                case TacticalLines.BRDGHD:
1330                case TacticalLines.BRDGHD_GE:
1331                case TacticalLines.NFL:
1332                    minSpikeDistance = arraysupport.getScaledSize(5, tg.get_LineThickness(), tg.get_patternScale());
1333                    segmented = false;
1334                    break;
1335                case TacticalLines.ATDITCH:
1336                case TacticalLines.ATDITCHC:
1337                case TacticalLines.ATDITCHM:
1338                case TacticalLines.FLOT:
1339                case TacticalLines.FORT_REVD:
1340                case TacticalLines.FORT:
1341                case TacticalLines.FORTL:
1342                case TacticalLines.STRONG:
1343                    minSpikeDistance = arraysupport.getScaledSize(25, tg.get_LineThickness(), tg.get_patternScale());
1344                    break;
1345                case TacticalLines.LC:
1346                case TacticalLines.OBSAREA:
1347                case TacticalLines.OBSFAREA:
1348                case TacticalLines.ENCIRCLE:
1349                case TacticalLines.ZONE:
1350                case TacticalLines.LINE:
1351                case TacticalLines.ATWALL:
1352                //case TacticalLines.ATWALL3D:
1353                case TacticalLines.UNSP:
1354                case TacticalLines.SFENCE:
1355                case TacticalLines.DFENCE:
1356                case TacticalLines.DOUBLEA:
1357                case TacticalLines.LWFENCE:
1358                case TacticalLines.HWFENCE:
1359                case TacticalLines.SINGLEC:
1360                case TacticalLines.DOUBLEC:
1361                case TacticalLines.TRIPLE:
1362                    minSpikeDistance = arraysupport.getScaledSize(35, tg.get_LineThickness(), tg.get_patternScale());
1363                    break;
1364                case TacticalLines.ICE_EDGE_RADAR:  //METOCs
1365                case TacticalLines.ICE_OPENINGS_FROZEN:
1366                case TacticalLines.CRACKS_SPECIFIC_LOCATION:
1367                    minSpikeDistance = arraysupport.getScaledSize(35, tg.get_LineThickness(), tg.get_patternScale());
1368                    break;
1369                default:
1370                    return;
1371            }
1372            double dist = 0;
1373
1374            ArrayList<POINT2> pts = new ArrayList();
1375
1376            //stuff pts with tg.Pixels
1377            //loop through pts to remove any points which are too close
1378            //then reset tg.Pixels with the new array with boundary points removed,            
1379            int j = 0;
1380            POINT2 pt = null, pt0 = null, pt1 = null;
1381            int n = tg.Pixels.size();
1382            //for(j=0;j<tg.Pixels.size();j++)
1383            for (j = 0; j < n; j++) {
1384                pt = tg.Pixels.get(j);
1385                pt.style = tg.Pixels.get(j).style;
1386                pts.add(pt);
1387            }
1388
1389            boolean removedPt = true;
1390            //order of priority is: keep anchor points, then boundary points, then segmented points
1391            outer:
1392            while (removedPt == true) {
1393                removedPt = false;
1394                //n=pts.size();
1395                for (j = 0; j < pts.size() - 1; j++) {
1396                    pt0 = pts.get(j);
1397                    pt1 = pts.get(j + 1);
1398                    dist = lineutility.CalcDistanceDouble(pts.get(j), pts.get(j + 1));
1399                    if (dist < minSpikeDistance) {
1400                        if (segmented == false) {
1401                            if (j + 1 == pts.size() - 1) {
1402                                pts.remove(j);
1403                            } else {
1404                                pts.remove(j + 1);
1405                            }
1406
1407                            removedPt = true;
1408                            break outer;
1409                        } else if (pt0.style == 0 && pt1.style == -1)//-1 are clipped boundary points
1410                        {
1411                            pts.remove(j + 1);
1412                            removedPt = true;
1413                            break outer;
1414                        } else if (pt0.style == 0 && pt1.style == -2)//-2 are segmented points, this should never happen
1415                        {
1416                            pts.remove(j + 1);
1417                            removedPt = true;
1418                            break outer;
1419                        } else if (pt0.style == -1 && pt1.style == 0) {
1420                            pts.remove(j);
1421                            removedPt = true;
1422                            break outer;
1423                        } else if (pt0.style == -1 && pt1.style == -1) {
1424                            pts.remove(j + 1);
1425                            removedPt = true;
1426                            break outer;
1427                        } else if (pt0.style == -1 && pt1.style == -2) {
1428                            pts.remove(j + 1);
1429                            removedPt = true;
1430                            break outer;
1431                        } else if (pt0.style == -2 && pt1.style == 0)//this should never happen
1432                        {
1433                            pts.remove(j);
1434                            removedPt = true;
1435                            break outer;
1436                        } else if (pt0.style == -2 && pt1.style == -1) {
1437                            pts.remove(j);
1438                            removedPt = true;
1439                            break outer;
1440                        } else if (pt0.style == -2 && pt1.style == -2) {
1441                            pts.remove(j + 1);
1442                            removedPt = true;
1443                            break outer;
1444                        }
1445                    }
1446                    //n=pts.size();
1447                }
1448            }
1449            tg.Pixels = pts;
1450            tg.LatLongs = armyc2.c5isr.RenderMultipoints.clsUtility.PixelsToLatLong(pts, converter);
1451
1452        } catch (Exception exc) {
1453            ErrorLogger.LogException(_className, "FilterPoints2",
1454                    new RendererException("Failed inside FilterPoints2", exc));
1455
1456        }
1457    }
1458
1459    /**
1460     * returns true if the line type can be clipped before calculating the
1461     * shapes
1462     *
1463     * @param tg tactical graphic
1464     * @return true if can pre-clip points
1465     */
1466    public static Boolean canClipPoints(TGLight tg) {
1467        try {
1468            String symbolId = tg.get_SymbolId();
1469            if (clsMETOC.IsWeather(symbolId) > 0) {
1470                return true;
1471            }
1472
1473            int linetype = tg.get_LineType();
1474            switch (linetype) {
1475                case TacticalLines.ABATIS:
1476//                case TacticalLines.BOUNDARY:
1477                case TacticalLines.FLOT:
1478                case TacticalLines.LC:
1479                case TacticalLines.PL:
1480                case TacticalLines.FEBA:
1481                case TacticalLines.LL:
1482                case TacticalLines.EWL:
1483                case TacticalLines.GENERAL:
1484                case TacticalLines.JTAA:
1485                case TacticalLines.SAA:
1486                case TacticalLines.SGAA:
1487                case TacticalLines.BS_AREA:
1488                case TacticalLines.BS_LINE:
1489                case TacticalLines.ASSY:
1490                case TacticalLines.EA:
1491                case TacticalLines.FORT_REVD:
1492                case TacticalLines.FORT:
1493                case TacticalLines.DZ:
1494                case TacticalLines.EZ:
1495                case TacticalLines.LZ:
1496                case TacticalLines.PZ:
1497                case TacticalLines.LAA:
1498                case TacticalLines.ROZ:
1499                case TacticalLines.AARROZ:
1500                case TacticalLines.UAROZ:
1501                case TacticalLines.WEZ:
1502                case TacticalLines.FEZ:
1503                case TacticalLines.JEZ:
1504                case TacticalLines.FAADZ:
1505                case TacticalLines.HIDACZ:
1506                case TacticalLines.MEZ:
1507                case TacticalLines.LOMEZ:
1508                case TacticalLines.HIMEZ:
1509                case TacticalLines.WFZ_REVD:
1510                case TacticalLines.WFZ:
1511                case TacticalLines.AIRFIELD:
1512                case TacticalLines.BATTLE:
1513                case TacticalLines.PNO:
1514                case TacticalLines.DIRATKAIR:
1515                case TacticalLines.DIRATKGND:
1516                case TacticalLines.DIRATKSPT:
1517                case TacticalLines.INFILTRATION:
1518                case TacticalLines.FCL:
1519                case TacticalLines.HOLD:
1520                case TacticalLines.BRDGHD:
1521                case TacticalLines.HOLD_GE:
1522                case TacticalLines.BRDGHD_GE:
1523                case TacticalLines.LOA:
1524                case TacticalLines.LOD:
1525                case TacticalLines.LDLC:
1526                case TacticalLines.PLD:
1527                case TacticalLines.ASSAULT:
1528                case TacticalLines.ATKPOS:
1529                case TacticalLines.OBJ:
1530                case TacticalLines.PEN:
1531                case TacticalLines.RELEASE:
1532                case TacticalLines.HOL:
1533                case TacticalLines.BHL:
1534                case TacticalLines.AO:
1535                case TacticalLines.AIRHEAD:
1536                case TacticalLines.ENCIRCLE:
1537                case TacticalLines.NAI:
1538                case TacticalLines.TAI:
1539                case TacticalLines.BASE_CAMP_REVD:
1540                case TacticalLines.BASE_CAMP:
1541                case TacticalLines.GUERILLA_BASE_REVD:
1542                case TacticalLines.GUERILLA_BASE:
1543                case TacticalLines.GENERIC_AREA:
1544                case TacticalLines.LINE:
1545                case TacticalLines.ZONE:
1546                case TacticalLines.OBSAREA:
1547                case TacticalLines.OBSFAREA:
1548                case TacticalLines.ATDITCH:
1549                case TacticalLines.ATDITCHC:
1550                case TacticalLines.ATDITCHM:
1551                case TacticalLines.ATWALL:
1552                case TacticalLines.DEPICT:
1553                case TacticalLines.MINED:
1554                case TacticalLines.FENCED:
1555                case TacticalLines.UXO:
1556                case TacticalLines.UNSP:
1557                case TacticalLines.SFENCE:
1558                case TacticalLines.DFENCE:
1559                case TacticalLines.DOUBLEA:
1560                case TacticalLines.LWFENCE:
1561                case TacticalLines.HWFENCE:
1562                case TacticalLines.SINGLEC:
1563                case TacticalLines.DOUBLEC:
1564                case TacticalLines.TRIPLE:
1565                case TacticalLines.FORTL:
1566                case TacticalLines.STRONG:
1567                case TacticalLines.RAD:
1568                case TacticalLines.BIO:
1569                case TacticalLines.NUC:
1570                case TacticalLines.CHEM:
1571                case TacticalLines.DRCL:
1572                case TacticalLines.LINTGT:
1573                case TacticalLines.LINTGTS:
1574                case TacticalLines.FPF:
1575                case TacticalLines.FSCL:
1576                case TacticalLines.BCL_REVD:
1577                case TacticalLines.BCL:
1578                case TacticalLines.ICL:
1579                case TacticalLines.IFF_OFF:
1580                case TacticalLines.IFF_ON:
1581                case TacticalLines.GENERIC_LINE:
1582                case TacticalLines.CFL:
1583                case TacticalLines.TRIP:
1584                case TacticalLines.OVERHEAD_WIRE:
1585                case TacticalLines.NFL:
1586                case TacticalLines.MFP:
1587                case TacticalLines.RFL:
1588                case TacticalLines.AT:
1589                case TacticalLines.SERIES:
1590                case TacticalLines.STRIKWARN:
1591                case TacticalLines.SMOKE:
1592                case TacticalLines.BOMB:
1593                case TacticalLines.FSA:
1594                case TacticalLines.ACA:
1595                case TacticalLines.FFA:
1596                case TacticalLines.NFA:
1597                case TacticalLines.RFA:
1598                case TacticalLines.PAA:
1599                case TacticalLines.ATI:
1600                case TacticalLines.CFFZ:
1601                case TacticalLines.CFZ:
1602                case TacticalLines.SENSOR:
1603                case TacticalLines.CENSOR:
1604                case TacticalLines.DA:
1605                case TacticalLines.ZOR:
1606                case TacticalLines.TBA:
1607                case TacticalLines.TVAR:
1608                case TacticalLines.KILLBOXBLUE:
1609                case TacticalLines.KILLBOXPURPLE:
1610//                case TacticalLines.MSR:
1611//                case TacticalLines.ASR:
1612                case TacticalLines.MSR_ONEWAY:
1613                case TacticalLines.MSR_TWOWAY:
1614                case TacticalLines.MSR_ALT:
1615                case TacticalLines.ASR_ONEWAY:
1616                case TacticalLines.ASR_TWOWAY:
1617                case TacticalLines.ASR_ALT:
1618                case TacticalLines.TRAFFIC_ROUTE_ONEWAY:
1619                case TacticalLines.TRAFFIC_ROUTE_ALT:
1620                case TacticalLines.DHA_REVD:
1621                case TacticalLines.DHA:
1622                case TacticalLines.KILL_ZONE:
1623                case TacticalLines.EPW:
1624                case TacticalLines.FARP:
1625                case TacticalLines.RHA:
1626                case TacticalLines.BSA:
1627                case TacticalLines.DSA:
1628                case TacticalLines.CSA:
1629                case TacticalLines.RSA:
1630                case TacticalLines.TGMF:
1631                    return true;
1632                case TacticalLines.MSR: //post clip these so there are identical points regardless whether segment data is set 10-5-16
1633                case TacticalLines.ASR:
1634                case TacticalLines.TRAFFIC_ROUTE:
1635                case TacticalLines.BOUNDARY:
1636                    return false;
1637                default:
1638                    return false;
1639            }
1640        } catch (Exception exc) {
1641            ErrorLogger.LogException(_className, "canClipPoints",
1642                    new RendererException("Failed inside canClipPoints", exc));
1643        }
1644        return false;
1645    }
1646
1647    /**
1648     * These get clipped so the fill must be treated as a separate shape.
1649     * Normally lines with fill do not have a separate shape for the fill.
1650     *
1651     * @param linetype
1652     * @return
1653     */
1654    protected static boolean LinesWithSeparateFill(int linetype, ArrayList<Shape2> shapes) {
1655        if (shapes == null) {
1656            return false;
1657        }
1658
1659        switch (linetype) {
1660            case TacticalLines.MSDZ:
1661                return true;
1662            //treat these as lines: because of the feint they need an extra shape for the fill
1663            case TacticalLines.OBSFAREA:
1664            case TacticalLines.OBSAREA:
1665            case TacticalLines.STRONG:
1666            case TacticalLines.ZONE:
1667            case TacticalLines.FORT_REVD:
1668            case TacticalLines.FORT:
1669            case TacticalLines.ENCIRCLE:
1670            //return true;
1671            case TacticalLines.FIX:
1672            case TacticalLines.BOUNDARY:
1673            case TacticalLines.FLOT:
1674            case TacticalLines.LC:
1675            case TacticalLines.PL:
1676            case TacticalLines.FEBA:
1677            case TacticalLines.LL:
1678            case TacticalLines.EWL:
1679            case TacticalLines.AC:
1680            case TacticalLines.MRR:
1681            case TacticalLines.SL:
1682            case TacticalLines.TC:
1683            case TacticalLines.SAAFR:
1684            case TacticalLines.SC:
1685            case TacticalLines.LLTR:
1686            case TacticalLines.DIRATKAIR:
1687            case TacticalLines.DIRATKGND:
1688            case TacticalLines.DIRATKSPT:
1689            case TacticalLines.INFILTRATION:
1690            case TacticalLines.FCL:
1691            case TacticalLines.HOLD:
1692            case TacticalLines.BRDGHD:
1693            case TacticalLines.HOLD_GE:
1694            case TacticalLines.BRDGHD_GE:
1695            case TacticalLines.LOA:
1696            case TacticalLines.LOD:
1697            case TacticalLines.LDLC:
1698            case TacticalLines.PLD:
1699            case TacticalLines.RELEASE:
1700            case TacticalLines.HOL:
1701            case TacticalLines.BHL:
1702            case TacticalLines.LINE:
1703            case TacticalLines.ABATIS:
1704            case TacticalLines.ATDITCH:
1705            case TacticalLines.ATDITCHC:
1706            case TacticalLines.ATDITCHM:
1707            case TacticalLines.ATWALL:
1708            case TacticalLines.MNFLDFIX:
1709            case TacticalLines.UNSP:
1710            case TacticalLines.SFENCE:
1711            case TacticalLines.DFENCE:
1712            case TacticalLines.DOUBLEA:
1713            case TacticalLines.LWFENCE:
1714            case TacticalLines.HWFENCE:
1715            case TacticalLines.SINGLEC:
1716            case TacticalLines.DOUBLEC:
1717            case TacticalLines.TRIPLE:
1718            case TacticalLines.FORTL:
1719            case TacticalLines.LINTGT:
1720            case TacticalLines.LINTGTS:
1721            case TacticalLines.FSCL:
1722            case TacticalLines.BCL_REVD:
1723            case TacticalLines.BCL:
1724            case TacticalLines.ICL:
1725            case TacticalLines.IFF_OFF:
1726            case TacticalLines.IFF_ON:
1727            case TacticalLines.GENERIC_LINE:
1728            case TacticalLines.CFL:
1729            case TacticalLines.TRIP:
1730            case TacticalLines.NFL:
1731            case TacticalLines.MFP:
1732            case TacticalLines.RFL:
1733            case TacticalLines.MSR:
1734            case TacticalLines.MSR_ONEWAY:
1735            case TacticalLines.MSR_TWOWAY:
1736            case TacticalLines.MSR_ALT:
1737            case TacticalLines.ASR:
1738            case TacticalLines.ASR_ONEWAY:
1739            case TacticalLines.ASR_TWOWAY:
1740            case TacticalLines.ASR_ALT:
1741            case TacticalLines.TRAFFIC_ROUTE:
1742            case TacticalLines.TRAFFIC_ROUTE_ONEWAY:
1743            case TacticalLines.TRAFFIC_ROUTE_ALT:
1744                //undo any fill
1745                Shape2 shape = null;
1746                if (shapes != null && shapes.size() > 0) {
1747                    int n = shapes.size();
1748                    //for(int j=0;j<shapes.size();j++)
1749                    for (int j = 0; j < n; j++) {
1750                        shape = shapes.get(j);
1751                        if (shape.getShapeType() == Shape2.SHAPE_TYPE_POLYLINE) {
1752                            shapes.get(j).setFillColor(null);
1753                        }
1754                    }
1755                }
1756                return true;
1757            default:
1758                return false;
1759
1760        }
1761    }
1762
1763    /**
1764     * uses a hash map to set the POINT2 style when creating tg.Pixels from
1765     * Point2D ArrayList
1766     *
1767     * @param pts2d
1768     * @param hashMap
1769     * @return
1770     */
1771    protected static ArrayList<POINT2> Point2DtoPOINT2Mapped(ArrayList<Point2D> pts2d, Map<String, Object> hashMap) {
1772        ArrayList<POINT2> pts = new ArrayList();
1773        try {
1774            Point2D pt2d;
1775            int style = 0;
1776            int n = pts2d.size();
1777            //for(int j=0;j<pts2d.size();j++)
1778            for (int j = 0; j < n; j++) {
1779                pt2d = pts2d.get(j);
1780                //the hash map contains the original tg.Pixels before clipping
1781                if (hashMap.containsValue(pt2d)) {
1782                    style = 0;
1783                } else {
1784                    style = -1;   //style set to -1 identifies it as a clip bounds point
1785                }
1786                pts.add(new POINT2(pts2d.get(j).getX(), pts2d.get(j).getY(), style));
1787            }
1788        } catch (Exception exc) {
1789            ErrorLogger.LogException(_className, "Point2DToPOINT2Mapped",
1790                    new RendererException("Failed inside Point2DToPOINT2Mapped", exc));
1791        }
1792        return pts;
1793    }
1794
1795    protected static ArrayList<POINT2> Point2DtoPOINT2(ArrayList<Point2D> pts2d) {
1796        ArrayList<POINT2> pts = new ArrayList();
1797        try {
1798            int n = pts2d.size();
1799            //for(int j=0;j<pts2d.size();j++)
1800            for (int j = 0; j < n; j++) {
1801                pts.add(new POINT2(pts2d.get(j).getX(), pts2d.get(j).getY()));
1802            }
1803        } catch (Exception exc) {
1804            ErrorLogger.LogException(_className, "Point2DToPOINT2",
1805                    new RendererException("Failed inside Point2DToPOINT2", exc));
1806        }
1807        return pts;
1808    }
1809
1810    protected static ArrayList<Point2D> POINT2toPoint2D(ArrayList<POINT2> pts) {
1811        ArrayList<Point2D> pts2d = new ArrayList();
1812        try {
1813            int n = pts.size();
1814            //for(int j=0;j<pts.size();j++)
1815            for (int j = 0; j < n; j++) {
1816                pts2d.add(new Point2D.Double(pts.get(j).x, pts.get(j).y));
1817            }
1818
1819        } catch (Exception exc) {
1820            ErrorLogger.LogException(_className, "POINT2toPoint2D",
1821                    new RendererException("Failed inside POINT2toPoint2D", exc));
1822        }
1823        return pts2d;
1824    }
1825
1826    /**
1827     * Builds a single shape from a point array. Currently we assume the array
1828     * represents a moveTo followed by a series of lineTo operations
1829     *
1830     * @param pts2d
1831     * @return
1832     */
1833    private static Shape BuildShapeFromPoints(ArrayList<Point2D> pts2d) {
1834        GeneralPath shape = new GeneralPath();
1835        try {
1836            shape.moveTo(pts2d.get(0).getX(), pts2d.get(0).getY());
1837            int n = pts2d.size();
1838            //for(int j=1;j<pts2d.size();j++)
1839            for (int j = 1; j < n; j++) {
1840                shape.lineTo(pts2d.get(j).getX(), pts2d.get(j).getY());
1841            }
1842        } catch (Exception exc) {
1843            ErrorLogger.LogException(_className, "buildShapeFromPoints",
1844                    new RendererException("Failed inside buildShapeFromPoints", exc));
1845
1846        }
1847        return shape;
1848    }
1849
1850    /**
1851     * Clips a ShapeSpec. Assumes we are not post clipping splines, therefore
1852     * all the operations are moveTo, lineTo. Each ShapeSpec is assumed to be:
1853     * moveTo, lineTo ... lineTo, followed by another moveTo, lineTo, ...
1854     * lineTo, followed by ...
1855     *
1856     * @param shapeSpec
1857     * @param pts
1858     * @param clipArea
1859     * @return a single clipped shapeSpec
1860     */
1861    protected static ArrayList<Shape2> buildShapeSpecFromPoints(TGLight tg0,
1862            Shape2 shapeSpec, //the original ShapeSpec
1863            ArrayList<POINT2> pts,
1864            Object clipArea) {
1865        ArrayList<Shape2> shapeSpecs2 = null;
1866        Shape2 shapeSpec2;
1867        try {
1868            //create a tg to use for the clip
1869            shapeSpecs2 = new ArrayList();
1870            int j = 0, n = 0;
1871            //return null if it is outside the bounds
1872            Rectangle rect = shapeSpec.getBounds();
1873            int h = shapeSpec.getBounds().height;
1874            int w = shapeSpec.getBounds().width;
1875            int x = shapeSpec.getBounds().x;
1876            int y = shapeSpec.getBounds().y;
1877//            if(h==0 && w==0)
1878//                return shapeSpecs2;
1879
1880            if (h == 0) {
1881                h = 1;
1882            }
1883            if (w == 0) {
1884                w = 1;
1885            }
1886
1887            Rectangle2D clipBounds = null;
1888            ArrayList<Point2D> clipPoints = null;
1889            if (clipArea != null && clipArea.getClass().isAssignableFrom(Rectangle2D.Double.class)) {
1890                clipBounds = (Rectangle2D) clipArea;
1891            } else if (clipArea != null && clipArea.getClass().isAssignableFrom(Rectangle.class)) {
1892                //clipBounds=(Rectangle2D)clipArea;
1893                Rectangle rectx = (Rectangle) clipArea;
1894                clipBounds = new Rectangle2D.Double(rectx.x, rectx.y, rectx.width, rectx.height);
1895            } else if (clipArea != null && clipArea.getClass().isAssignableFrom(ArrayList.class)) {
1896                clipPoints = (ArrayList<Point2D>) clipArea;
1897            }
1898
1899            if (clipBounds != null && clipBounds.contains(shapeSpec.getShape().getBounds2D()) == false
1900                    && clipBounds.intersects(shapeSpec.getShape().getBounds2D()) == false) {
1901                //this tests if the shape has height or width 0
1902                //but may be contained within the clipbounds or intersect it
1903                //in that case we gave it a default width or thickness of 1
1904                if (clipBounds.contains(x, y, w, h) == false
1905                        && clipBounds.intersects(x, y, w, h) == false) {
1906                    return shapeSpecs2;
1907                }
1908            } else if (clipPoints != null) {
1909                GeneralPath poly = new GeneralPath();
1910                n = clipPoints.size();
1911                //for(j=0;j<clipPoints.size();j++)
1912                for (j = 0; j < n; j++) {
1913                    if (j == 0) {
1914                        poly.moveTo(clipPoints.get(j).getX(), clipPoints.get(j).getY());
1915                    } else {
1916                        poly.lineTo(clipPoints.get(j).getX(), clipPoints.get(j).getY());
1917                    }
1918                }
1919                poly.closePath();
1920                if (poly.contains(shapeSpec.getShape().getBounds2D()) == false
1921                        && poly.intersects(shapeSpec.getShape().getBounds2D()) == false) {
1922                    if (poly.contains(x, y, w, h) == false
1923                            && poly.intersects(x, y, w, h) == false) {
1924                        return shapeSpecs2;
1925                    }
1926                }
1927            }
1928
1929            if (shapeSpec.getShapeType() == Shape2.SHAPE_TYPE_MODIFIER
1930                    || shapeSpec.getShapeType() == Shape2.SHAPE_TYPE_MODIFIER_FILL) {
1931                shapeSpecs2.add(shapeSpec);
1932                return shapeSpecs2;
1933            }
1934            TGLight tg = new TGLight();
1935            POINT2 pt = null;
1936            tg.set_LineType(TacticalLines.PL);
1937            ArrayList<POINT2> pts2 = new ArrayList();
1938            ArrayList<Point2D> pts2d = null;
1939            Shape shape = null;
1940            GeneralPath gp = new GeneralPath();
1941            //loop through the points
1942            n = pts.size();
1943            //for(j=0;j<pts.size();j++)
1944            for (j = 0; j < n; j++) {
1945                pt = pts.get(j);
1946                //new line
1947                switch (pt.style) {
1948                    case 0: //moveTo,
1949                        //they lifted the pencil, so we build the shape from the existing pts and append it
1950                        if (pts2.size() > 1) {
1951                            //clip the points
1952                            tg = new TGLight();
1953                            tg.set_LineType(TacticalLines.PL);
1954                            tg.Pixels = pts2;
1955                            if (clipBounds != null) {
1956                                pts2d = clsClipPolygon2.ClipPolygon(tg, clipBounds);
1957                            } else if (clipPoints != null && !clipPoints.isEmpty()) {
1958                                pts2d = clsClipQuad.ClipPolygon(tg, clipPoints);
1959                            }
1960
1961                            //build a GeneralPath from the points we collected, we will append it
1962                            if (pts2d != null && pts2d.size() > 1) {
1963                                shape = BuildShapeFromPoints(pts2d);
1964                                //append the shape because we want to return only one shape
1965                                gp.append(shape, false);
1966                            }
1967                            //clear the points array and begin the next line
1968                            pts2.clear();
1969                            pts2.add(pt);
1970                        } else {
1971                            pts2.add(pt);
1972                        }
1973                        break;
1974                    case 1: //lineTo
1975                        pts2.add(pt);
1976                        break;
1977                    default:
1978                        pts2.add(pt);
1979                        break;
1980                }
1981            }//end for
1982            //append the last shape
1983            if (pts2.size() > 1) {
1984                //clip the points
1985                tg = new TGLight();
1986                tg.set_LineType(TacticalLines.PL);
1987                tg.Pixels = pts2;
1988                if (clipBounds != null) {
1989                    pts2d = clsClipPolygon2.ClipPolygon(tg, clipBounds);
1990                } else if (clipPoints != null) {
1991                    pts2d = clsClipQuad.ClipPolygon(tg, clipPoints);
1992                }
1993                //build a GeneralPath from the points we collected, we will append it
1994                if (pts2d != null && pts2d.size() > 1) {
1995                    shape = BuildShapeFromPoints(pts2d);
1996                    gp.append(shape, false);
1997                }
1998                tg0.set_WasClipped(tg.get_WasClipped());
1999            }
2000            //create the shapespec here
2001            //initialize the clipped ShapeSpec
2002            shapeSpec2 = new Shape2(shapeSpec.getShapeType());
2003            shapeSpec2.setLineColor(shapeSpec.getLineColor());
2004            shapeSpec2.setFillColor(shapeSpec.getFillColor());
2005            shapeSpec2.setStroke(shapeSpec.getStroke());
2006            shapeSpec2.setTexturePaint(shapeSpec.getTexturePaint());
2007            shapeSpec2.setShape(gp);
2008            shapeSpecs2.add(shapeSpec2);
2009        } catch (Exception exc) {
2010            ErrorLogger.LogException(_className, "buildShapeSpecFromPoints",
2011                    new RendererException("Failed inside buildShapeSpecFromPoints", exc));
2012
2013        }
2014        return shapeSpecs2;
2015    }
2016
2017    /**
2018     * Currently assumes no MeTOC symbols are post clipped
2019     *
2020     * @param tg
2021     * @param shapeSpecsArray
2022     * @param clipArea
2023     * @return
2024     */
2025    protected static ArrayList<Shape2> postClipShapes(TGLight tg, ArrayList<Shape2> shapeSpecsArray, Object clipArea) {
2026        ArrayList<Shape2> shapeSpecs2 = null;
2027        ArrayList<Shape2> tempShapes = null;
2028        try {
2029            if (shapeSpecsArray == null || shapeSpecsArray.size() == 0) {
2030                return null;
2031            }
2032
2033            shapeSpecs2 = new ArrayList();
2034            int j = 0;
2035            ArrayList<Shape2> shapeSpecs = new ArrayList();
2036            int n = shapeSpecsArray.size();
2037            //for(j=0;j<shapeSpecsArray.size();j++)
2038            for (j = 0; j < n; j++) {
2039                shapeSpecs.add(shapeSpecsArray.get(j));
2040            }
2041
2042            ArrayList<POINT2> pts = new ArrayList();//use these
2043            Shape shape = null;
2044            POINT2 pt;
2045            double[] coords = new double[6];
2046            Shape2 shapeSpec = null;
2047            n = shapeSpecs.size();
2048            //for(j=0;j<shapeSpecs.size();j++)
2049            for (j = 0; j < n; j++) {
2050                shapeSpec = shapeSpecs.get(j);
2051                shape = shapeSpec.getShape();
2052                pts.clear();
2053                for (PathIterator i = shape.getPathIterator(null); !i.isDone(); i.next()) {
2054                    int type = i.currentSegment(coords);
2055                    switch (type) {
2056                        case PathIterator.SEG_MOVETO:
2057                            pt = new POINT2(coords[0], coords[1]);
2058                            pt.style = 0;
2059                            pts.add(pt);
2060                            break;
2061                        case PathIterator.SEG_LINETO:
2062                            pt = new POINT2(coords[0], coords[1]);
2063                            pt.style = 1;
2064                            pts.add(pt);
2065                            break;
2066                        case PathIterator.SEG_QUADTO:   //not using this
2067                            pt = new POINT2(coords[0], coords[1]);
2068                            pt.style = 2;
2069                            pts.add(pt);
2070                            pt = new POINT2(coords[2], coords[3]);
2071                            pt.style = 2;
2072                            pts.add(pt);
2073                            break;
2074                        case PathIterator.SEG_CUBICTO:  //not using this
2075                            pt = new POINT2(coords[0], coords[1]);
2076                            pt.style = 3;
2077                            pts.add(pt);
2078                            pt = new POINT2(coords[2], coords[3]);
2079                            pt.style = 3;
2080                            pts.add(pt);
2081                            pt = new POINT2(coords[4], coords[5]);
2082                            pt.style = 3;
2083                            pts.add(pt);
2084                            break;
2085                        case PathIterator.SEG_CLOSE://not using this
2086                            pt = new POINT2(coords[0], coords[1]);
2087                            pt.style = 4;
2088                            pts.add(pt);
2089                            break;
2090                        default:
2091                            pt = null;
2092                            break;
2093                    }//end switch
2094                }   //end for pathiterator i
2095                tempShapes = buildShapeSpecFromPoints(tg, shapeSpec, pts, clipArea);
2096                shapeSpecs2.addAll(tempShapes);
2097            }
2098        } catch (Exception exc) {
2099            ErrorLogger.LogException(_className, "postClipShapes",
2100                    new RendererException("Failed inside postClipShapes", exc));
2101        }
2102        return shapeSpecs2;
2103    }
2104
2105    /**
2106     * For the 3d map we cannot pre-segment the auto-shapes or fire support
2107     * areas. We do need to pre-segment generic lines regardless of the status
2108     * if clipping is set. Currently we are not pre-segmenting axis of advance
2109     * symbols.
2110     *
2111     * @param tg
2112     * @return true if pre-segmenting is to be used
2113     */
2114    private static boolean segmentAnticipatedLine(TGLight tg) {
2115        try {
2116            int linetype = tg.get_LineType();
2117            //do not pre-segment the fire support rectangular and circular areas
2118            if (clsUtility.IsChange1Area(linetype)) {
2119                return false;
2120            }
2121            //do not pre-segment the autoshapes
2122            if (clsUtility.isAutoshape(tg)) {
2123                return false;
2124            }
2125            if (SymbolUtilities.isBasicShape(linetype)) {
2126                return false;
2127            }
2128            //temporarily do not pre-segment the channel types.
2129            switch (linetype) {
2130                case TacticalLines.OVERHEAD_WIRE:
2131                case TacticalLines.CATK:
2132                case TacticalLines.CATKBYFIRE:
2133                case TacticalLines.MAIN:
2134                case TacticalLines.SPT:
2135                case TacticalLines.FRONTAL_ATTACK:
2136                case TacticalLines.TURNING_MOVEMENT:
2137                case TacticalLines.MOVEMENT_TO_CONTACT:
2138                case TacticalLines.AIRAOA:
2139                case TacticalLines.AAAAA:
2140                    return false;
2141                case TacticalLines.MSR_ONEWAY:
2142                case TacticalLines.MSR_TWOWAY:
2143                case TacticalLines.MSR_ALT:
2144                case TacticalLines.ASR_ONEWAY:
2145                case TacticalLines.ASR_TWOWAY:
2146                case TacticalLines.ASR_ALT:
2147                case TacticalLines.TRAFFIC_ROUTE_ONEWAY:
2148                case TacticalLines.TRAFFIC_ROUTE_ALT:
2149                    //added because of segment data 4-22-13
2150                    //removed from this case block since we now post-clip these because of segment color data 10-5-16
2151//                case TacticalLines.MSR:
2152//                case TacticalLines.ASR:
2153//                case TacticalLines.BOUNDARY:
2154                    return false;
2155                default:
2156                    break;
2157            }
2158
2159        } catch (Exception exc) {
2160            ErrorLogger.LogException(_className, "segmentGenericLine",
2161                    new RendererException("Failed inside segmentGenericLine", exc));
2162        }
2163        return true;
2164    }
2165
2166    /**
2167     * cannot pre-segment the fire support areas, must post segment them after
2168     * the pixels were calculated
2169     *
2170     * @param tg
2171     * @param converter
2172     */
2173    protected static void postSegmentFSA(TGLight tg,
2174            IPointConversion converter) {
2175        try {
2176            if (tg.get_Client().equals("2D")) {
2177                return;
2178            }
2179
2180            int linetype = tg.get_LineType();
2181            switch (linetype) {
2182                case TacticalLines.PAA_RECTANGULAR:
2183                case TacticalLines.FSA_RECTANGULAR:
2184                case TacticalLines.SHIP_AOI_RECTANGULAR:
2185                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
2186                case TacticalLines.FFA_RECTANGULAR:
2187                case TacticalLines.ACA_RECTANGULAR:
2188                case TacticalLines.NFA_RECTANGULAR:
2189                case TacticalLines.RFA_RECTANGULAR:
2190                case TacticalLines.ATI_RECTANGULAR:
2191                case TacticalLines.CFFZ_RECTANGULAR:
2192                case TacticalLines.SENSOR_RECTANGULAR:
2193                case TacticalLines.CENSOR_RECTANGULAR:
2194                case TacticalLines.DA_RECTANGULAR:
2195                case TacticalLines.CFZ_RECTANGULAR:
2196                case TacticalLines.ZOR_RECTANGULAR:
2197                case TacticalLines.TBA_RECTANGULAR:
2198                case TacticalLines.TVAR_RECTANGULAR:
2199                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
2200                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
2201                    break;
2202                default:
2203                    return;
2204            }
2205            ArrayList<POINT2> latLongs = new ArrayList();
2206            ArrayList<POINT2> resultPts = new ArrayList();
2207            int j = 0, k = 0, n = 0;
2208            POINT2 pt0 = null, pt1 = null, pt = null;
2209            double dist = 0;
2210            //double interval=1000000;
2211            double interval = 250000;
2212            double az = 0;
2213
2214            double maxDist = 0;
2215            Point2D pt2d = null;
2216            int t = tg.Pixels.size();
2217            //for(j=0;j<tg.Pixels.size();j++)
2218            for (j = 0; j < t; j++) {
2219                pt0 = tg.Pixels.get(j);
2220                pt2d = new Point2D.Double(pt0.x, pt0.y);
2221                pt2d = converter.PixelsToGeo(pt2d);
2222                pt0 = new POINT2(pt2d.getX(), pt2d.getY());
2223                latLongs.add(pt0);
2224            }
2225            t = latLongs.size();
2226            //for(j=0;j<latLongs.size()-1;j++)
2227            for (j = 0; j < t - 1; j++) {
2228                pt0 = latLongs.get(j);
2229                pt1 = latLongs.get(j + 1);
2230                pt1.style = -1;//end point
2231                az = mdlGeodesic.GetAzimuth(pt0, pt1);
2232                dist = mdlGeodesic.geodesic_distance(latLongs.get(j), latLongs.get(j + 1), null, null);
2233                if (dist > maxDist) {
2234                    maxDist = dist;
2235                }
2236            }
2237
2238            if (interval > maxDist) {
2239                interval = maxDist;
2240            }
2241
2242            //for(j=0;j<latLongs.size()-1;j++)
2243            for (j = 0; j < t - 1; j++) {
2244                pt0 = new POINT2(latLongs.get(j));
2245                pt0.style = 0;//anchor point
2246                pt1 = new POINT2(latLongs.get(j + 1));
2247                pt1.style = 0;//anchor point point
2248                az = mdlGeodesic.GetAzimuth(pt0, pt1);
2249                dist = mdlGeodesic.geodesic_distance(latLongs.get(j), latLongs.get(j + 1), null, null);
2250
2251                n = (int) (dist / interval);
2252                if (j == 0) {
2253                    resultPts.add(pt0);
2254                }
2255
2256                for (k = 1; k <= n; k++) {
2257                    pt = mdlGeodesic.geodesic_coordinate(pt0, interval * k, az);
2258                    pt.style = -2;
2259                    //we do not want the last segment to be too close to the anchor point
2260                    //only add the segment point if it is a distance at least half the inteval
2261                    //from the 2nd anchor point
2262                    dist = mdlGeodesic.geodesic_distance(pt, pt1, null, null);
2263                    if (dist >= interval / 2) {
2264                        resultPts.add(pt);
2265                    }
2266                }
2267                //ad the 2nd anchor point
2268                resultPts.add(pt1);
2269            }
2270            latLongs = resultPts;
2271            tg.Pixels = armyc2.c5isr.RenderMultipoints.clsUtility.LatLongToPixels(latLongs, converter);
2272        } catch (Exception exc) {
2273            ErrorLogger.LogException(_className, "postSegmentFSA",
2274                    new RendererException("Failed inside postSegmentFSA", exc));
2275        }
2276    }
2277
2278    /**
2279     * Similar to Vincenty algorithm for more accurate interpolation of geo
2280     * anchor points
2281     *
2282     * @return the interpolated points
2283     */
2284    private static ArrayList<POINT2> toGeodesic(TGLight tg, double interval, HashMap<Integer,String> hmap) {
2285        ArrayList<POINT2> locs = new ArrayList<POINT2>();
2286        try {
2287            int i = 0, k = 0, n = 0;
2288            ArrayList<POINT2> points = tg.LatLongs;
2289            String H = "";
2290            String color = "";
2291            boolean bolIsAC = false;
2292            int acWidth = 0;
2293            int linetype = tg.get_LineType();
2294            switch (linetype) {
2295                case TacticalLines.AC:
2296                case TacticalLines.LLTR:
2297                case TacticalLines.MRR:
2298                case TacticalLines.SL:
2299                case TacticalLines.SAAFR:
2300                case TacticalLines.TC:
2301                case TacticalLines.SC:
2302                    bolIsAC = true;
2303                    break;
2304                default:
2305                    break;
2306            }
2307            for (i = 0; i < points.size() - 1; i++) {
2308                if(bolIsAC)
2309                    acWidth=points.get(i).style;
2310                // Convert coordinates from degrees to Radians
2311                //var lat1 = points[i].latitude * (PI / 180);
2312                //var lon1 = points[i].longitude * (PI / 180);
2313                //var lat2 = points[i + 1].latitude * (PI / 180);
2314                //var lon2 = points[i + 1].longitude * (PI / 180);                
2315                double lat1 = Math.toRadians(points.get(i).y);
2316                double lon1 = Math.toRadians(points.get(i).x);
2317                double lat2 = Math.toRadians(points.get(i + 1).y);
2318                double lon2 = Math.toRadians(points.get(i + 1).x);
2319                // Calculate the total extent of the route
2320                //var d = 2 * asin(sqrt(pow((sin((lat1 - lat2) / 2)), 2) + cos(lat1) * cos(lat2) * pow((sin((lon1 - lon2) / 2)), 2)));
2321                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)));
2322
2323                double dist = mdlGeodesic.geodesic_distance(points.get(i), points.get(i + 1), null, null);
2324                //double dist=d;
2325                float flt = (float) dist / (float) interval;
2326                n = Math.round(flt);
2327                if (n < 1) {
2328                    n = 1;
2329                }
2330                if (n > 32) {
2331                    n = 32;
2332                }
2333                // Calculate  positions at fixed intervals along the route
2334                for (k = 0; k <= n; k++) {
2335                    //we must preserve the anchor points
2336                    if (k == 0) {
2337                        locs.add(new POINT2(points.get(i)));
2338                        if (hmap != null && hmap.containsKey(i)) {
2339                            if (!H.isEmpty()) {
2340                                H += ",";
2341                            }
2342                            color = (String) hmap.get(i);
2343                            H += Integer.toString(locs.size() - 1) + ":" + color;
2344                        }
2345                        continue;
2346                    } else if (k == n) {
2347                        if (i == points.size() - 2) {
2348                            locs.add(new POINT2(points.get(i + 1)));
2349                            if (hmap != null && hmap.containsKey(i + 1)) {
2350                                if (!H.isEmpty()) {
2351                                    H += ",";
2352                                }
2353                                color = (String) hmap.get(i + 1);
2354                                H += Integer.toString(locs.size() - 1) + ":" + color;
2355                            }
2356                        }
2357                        break;
2358                    }
2359                    //var f = (k / n);
2360                    //var A = sin((1 - f) * d) / sin(d);
2361                    //var B = sin(f * d) / sin(d);
2362                    double f = ((double) k / (double) n);
2363                    double A = Math.sin((1 - f) * d) / Math.sin(d);
2364                    double B = Math.sin(f * d) / Math.sin(d);
2365                    // Obtain 3D Cartesian coordinates of each point
2366                    //var x = A * cos(lat1) * cos(lon1) + B * cos(lat2) * cos(lon2);
2367                    //var y = A * cos(lat1) * sin(lon1) + B * cos(lat2) * sin(lon2);
2368                    //var z = A * sin(lat1) + B * sin(lat2);
2369                    double x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) * Math.cos(lon2);
2370                    double y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) * Math.sin(lon2);
2371                    double z = A * Math.sin(lat1) + B * Math.sin(lat2);
2372                    // Convert these to latitude/longitude
2373                    //var lat = atan2(z, sqrt(pow(x, 2) + pow(y, 2)));
2374                    //var lon = atan2(y, x);
2375                    double lat = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
2376                    double lon = Math.atan2(y, x);
2377                    lat *= 180.0 / Math.PI;
2378                    lon *= 180.0 / Math.PI;
2379                    POINT2 pt = new POINT2(lon, lat);
2380                    if(bolIsAC)
2381                        pt.style=-acWidth;
2382                    locs.add(pt);
2383                    if (hmap != null && hmap.containsKey(i)) {
2384                        if (!H.isEmpty()) {
2385                            H += ",";
2386                        }
2387                        color = (String) hmap.get(i);
2388                        H += Integer.toString(locs.size() - 1) + ":" + color;
2389                    }
2390                }
2391            }
2392            if (!H.isEmpty()) {
2393                tg.set_H(H);
2394            }
2395        } catch (Exception exc) {
2396            ErrorLogger.LogException(_className, "toGeodesic",
2397                    new RendererException("Failed inside toGeodesic", exc));
2398            return null;
2399        }
2400        return locs;
2401    }
2402
2403    /**
2404     * Pre-segment the lines based on max or min latitude for the segment
2405     * interval. This is necessary because GeoPixelconversion does not work well
2406     * over distance greater than 1M meters, especially at extreme latitudes.
2407     *
2408     * @param tg
2409     * @param converter
2410     */
2411    protected static void SegmentGeoPoints(TGLight tg,
2412            IPointConversion converter,
2413            double zoomFactor) {
2414        try {
2415            if (tg.get_Client().equals("2D")) {
2416                return;
2417            }
2418
2419            ArrayList<POINT2> resultPts = new ArrayList();
2420            int lineType = tg.get_LineType();
2421            //double interval=1000000;
2422            double interval = 250000;
2423            boolean bolSegmentAC = false, bolIsAC = false;
2424            bolSegmentAC = true;
2425            //conservative interval in meters
2426            //return early for those lines not requiring pre-segmenting geo points
2427            switch (lineType) {
2428                case TacticalLines.AC:
2429                case TacticalLines.LLTR:
2430                case TacticalLines.MRR:
2431                case TacticalLines.SL:
2432                case TacticalLines.SAAFR:
2433                case TacticalLines.TC:
2434                case TacticalLines.SC:
2435                    if (!bolSegmentAC) {
2436                        return;
2437                    }
2438                    bolIsAC = true;
2439                    break;
2440                case TacticalLines.PLD:
2441                case TacticalLines.CFL:
2442                case TacticalLines.UNSP:
2443                case TacticalLines.TRIPLE:
2444                case TacticalLines.DOUBLEC:
2445                case TacticalLines.SINGLEC:
2446                case TacticalLines.ATDITCH:
2447                case TacticalLines.ATDITCHC:
2448                case TacticalLines.ATDITCHM:
2449                case TacticalLines.ATWALL:
2450                case TacticalLines.LINE:
2451                case TacticalLines.DIRATKAIR:
2452                case TacticalLines.STRONG:
2453                case TacticalLines.ENCIRCLE:
2454                case TacticalLines.FLOT:
2455                case TacticalLines.ZONE:
2456                case TacticalLines.OBSAREA:
2457                case TacticalLines.OBSFAREA:
2458                case TacticalLines.FORT_REVD:
2459                case TacticalLines.FORT:
2460                case TacticalLines.FORTL:
2461                    break;
2462                case TacticalLines.HWFENCE:
2463                case TacticalLines.LWFENCE:
2464                case TacticalLines.DOUBLEA:
2465                case TacticalLines.DFENCE:
2466                case TacticalLines.SFENCE:
2467                    interval = 500000;
2468                    break;
2469                case TacticalLines.LC:
2470                    interval = 2000000;
2471                    break;
2472                default:
2473                    //if the line is an anticipated generic line then segment the line
2474                    if (segmentAnticipatedLine(tg)) {
2475                        break;
2476                    }
2477                    return;
2478            }
2479
2480            int j = 0, k = 0, n = 0;
2481            POINT2 pt0 = null, pt1 = null, pt = null;
2482            double dist = 0;
2483            double az = 0;
2484
2485            double maxDist = 0;
2486            int t = tg.LatLongs.size();
2487            //for(j=0;j<tg.LatLongs.size()-1;j++)
2488            for (j = 0; j < t - 1; j++) {
2489                pt0 = tg.LatLongs.get(j);
2490                pt1 = tg.LatLongs.get(j + 1);
2491                if(!bolIsAC)
2492                    pt1.style = -1;//end point
2493                az = mdlGeodesic.GetAzimuth(pt0, pt1);
2494                dist = mdlGeodesic.geodesic_distance(tg.LatLongs.get(j), tg.LatLongs.get(j + 1), null, null);
2495                if (dist > maxDist) {
2496                    maxDist = dist;
2497                }
2498            }
2499
2500            if (interval > maxDist) {
2501                interval = maxDist;
2502            }
2503
2504            if (zoomFactor > 0 && zoomFactor < 0.01) {
2505                zoomFactor = 0.01;
2506            }
2507            if (zoomFactor > 0 && zoomFactor < 1) {
2508                interval *= zoomFactor;
2509            }
2510
2511            boolean useVincenty = false;
2512            String H = "";
2513            String color = "";
2514            HashMap<Integer,String> hmap = clsUtility.getMSRSegmentColorStrings(tg);
2515            if (hmap != null) {
2516                tg.set_H("");
2517            }
2518            //uncomment one line to use (similar to) Vincenty algorithm
2519            useVincenty = true;
2520            if (useVincenty) {
2521                resultPts = toGeodesic(tg, interval, hmap);
2522                tg.LatLongs = resultPts;
2523                tg.Pixels = armyc2.c5isr.RenderMultipoints.clsUtility.LatLongToPixels(tg.LatLongs, converter);
2524                return;
2525            }
2526
2527            for (j = 0; j < tg.LatLongs.size() - 1; j++) {
2528                pt0 = new POINT2(tg.LatLongs.get(j));
2529                pt0.style = 0;//anchor point
2530                pt1 = new POINT2(tg.LatLongs.get(j + 1));
2531                pt1.style = 0;//anchor point point
2532                az = mdlGeodesic.GetAzimuth(pt0, pt1);
2533                dist = mdlGeodesic.geodesic_distance(tg.LatLongs.get(j), tg.LatLongs.get(j + 1), null, null);
2534
2535                n = (int) (dist / interval);
2536                if (j == 0) {
2537                    resultPts.add(pt0);
2538                    if (hmap != null && hmap.containsKey(j)) {
2539                        if (!H.isEmpty()) {
2540                            H += ",";
2541                        }
2542                        color = (String) hmap.get(j);
2543                        //H+=(resultPts.size()-1).toString()+":"+color;
2544                        H += Integer.toString(resultPts.size() - 1) + ":" + color;
2545                    }
2546                }
2547                for (k = 1; k <= n; k++) {
2548                    pt = mdlGeodesic.geodesic_coordinate(pt0, interval * k, az);
2549                    pt.style = -2;
2550                    //we do not want the last segment to be too close to the anchor point
2551                    //only add the segment point if it is a distance at least half the inteval
2552                    //from the 2nd anchor point
2553                    dist = mdlGeodesic.geodesic_distance(pt, pt1, null, null);
2554                    if (dist >= interval / 2) {
2555                        resultPts.add(pt);
2556                        if (hmap != null && hmap.containsKey(j)) {
2557                            color = (String) hmap.get(j);
2558                            if (!H.isEmpty()) {
2559                                H += ",";
2560                            }
2561                            //H+=(resultPts.size()-1).toString()+":"+color;
2562                            H += Integer.toString(resultPts.size() - 1) + ":" + color;
2563                        }
2564                    }
2565                }
2566                //ad the 2nd anchor point
2567                resultPts.add(pt1);
2568                if (hmap != null && hmap.containsKey(j + 1)) {
2569                    if (!H.isEmpty()) {
2570                        H += ",";
2571                    }
2572                    color = (String) hmap.get(j + 1);
2573                    //H+=(resultPts.size()-1).toString()+":"+color;
2574                    H += Integer.toString(resultPts.size() - 1) + ":" + color;
2575                }
2576            }
2577            if (!H.isEmpty()) {
2578                tg.set_H(H);
2579            }
2580            tg.LatLongs = resultPts;
2581            tg.Pixels = armyc2.c5isr.RenderMultipoints.clsUtility.LatLongToPixels(tg.LatLongs, converter);
2582        } catch (Exception exc) {
2583            ErrorLogger.LogException(_className, "SegmentGeoPoints",
2584                    new RendererException("Failed inside SegmentGeoPoints", exc));
2585        }
2586    }
2587
2588    /**
2589     * Calculating the signed area will tell you which direction the points are going.
2590     * Negative = Clock-wise, Positive = counter clock-wise
2591     * A = 1/2 * (x1*y2 - x2*y1 + x2*y3 - x3*y2 + ... + xn*y1 - x1*yn)
2592     */
2593    static double CalculateSignedAreaOfPolygon(ArrayList<POINT2> coords) {
2594        double signedArea = 0;
2595        final int len = coords.size();
2596        for (int i = 0; i < len; i++) {
2597            double x1 = coords.get(i).x;
2598            double y1 = coords.get(i).y;
2599            double x2 = coords.get((i + 1) % len).x;
2600            double y2 = coords.get((i + 1) % len).y;
2601            signedArea += (x1 * y2 - x2 * y1);
2602        }
2603        return signedArea / 2;
2604    }
2605}