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