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());
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());
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());
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());
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.OVERHEAD_WIRE:
1343                case TacticalLines.NFL:
1344                case TacticalLines.MFP:
1345                case TacticalLines.RFL:
1346                case TacticalLines.AT:
1347                case TacticalLines.SERIES:
1348                case TacticalLines.STRIKWARN:
1349                case TacticalLines.SMOKE:
1350                case TacticalLines.BOMB:
1351                case TacticalLines.FSA:
1352                case TacticalLines.ACA:
1353                case TacticalLines.FFA:
1354                case TacticalLines.NFA:
1355                case TacticalLines.RFA:
1356                case TacticalLines.PAA:
1357                case TacticalLines.ATI:
1358                case TacticalLines.CFFZ:
1359                case TacticalLines.CFZ:
1360                case TacticalLines.SENSOR:
1361                case TacticalLines.CENSOR:
1362                case TacticalLines.DA:
1363                case TacticalLines.ZOR:
1364                case TacticalLines.TBA:
1365                case TacticalLines.TVAR:
1366                case TacticalLines.KILLBOXBLUE:
1367                case TacticalLines.KILLBOXPURPLE:
1368//                case TacticalLines.MSR:
1369//                case TacticalLines.ASR:
1370                case TacticalLines.MSR_ONEWAY:
1371                case TacticalLines.MSR_TWOWAY:
1372                case TacticalLines.MSR_ALT:
1373                case TacticalLines.ASR_ONEWAY:
1374                case TacticalLines.ASR_TWOWAY:
1375                case TacticalLines.ASR_ALT:
1376                case TacticalLines.ROUTE_ONEWAY:
1377                case TacticalLines.ROUTE_ALT:
1378                case TacticalLines.DHA_REVD:
1379                case TacticalLines.DHA:
1380                case TacticalLines.EPW:
1381                case TacticalLines.FARP:
1382                case TacticalLines.RHA:
1383                case TacticalLines.BSA:
1384                case TacticalLines.DSA:
1385                case TacticalLines.CSA:
1386                case TacticalLines.RSA:
1387                case TacticalLines.TGMF:
1388                    return true;
1389                case TacticalLines.MSR: //post clip these so there are identical points regardless whether segment data is set 10-5-16
1390                case TacticalLines.ASR:
1391                case TacticalLines.ROUTE:
1392                case TacticalLines.BOUNDARY:
1393                    return false;
1394                default:
1395                    return false;
1396            }
1397        } catch (Exception exc) {
1398            ErrorLogger.LogException(_className, "canClipPoints",
1399                    new RendererException("Failed inside canClipPoints", exc));
1400        }
1401        return false;
1402    }
1403
1404    /**
1405     * These get clipped so the fill must be treated as a separate shape.
1406     * Normally lines with fill do not have a separate shape for the fill.
1407     *
1408     * @param linetype
1409     * @return
1410     */
1411    protected static boolean LinesWithSeparateFill(int linetype, ArrayList<Shape2> shapes) {
1412        if (shapes == null) {
1413            return false;
1414        }
1415
1416        switch (linetype) {
1417            case TacticalLines.MSDZ:
1418                return true;
1419            //treat these as lines: because of the feint they need an extra shape for the fill
1420            case TacticalLines.OBSFAREA:
1421            case TacticalLines.OBSAREA:
1422            case TacticalLines.STRONG:
1423            case TacticalLines.ZONE:
1424            case TacticalLines.FORT_REVD:
1425            case TacticalLines.FORT:
1426            case TacticalLines.ENCIRCLE:
1427            //return true;
1428            case TacticalLines.FIX:
1429            case TacticalLines.BOUNDARY:
1430            case TacticalLines.FLOT:
1431            case TacticalLines.LC:
1432            case TacticalLines.PL:
1433            case TacticalLines.FEBA:
1434            case TacticalLines.LL:
1435            case TacticalLines.EWL:
1436            case TacticalLines.AC:
1437            case TacticalLines.MRR:
1438            case TacticalLines.SL:
1439            case TacticalLines.TC:
1440            case TacticalLines.SAAFR:
1441            case TacticalLines.SC:
1442            case TacticalLines.LLTR:
1443            case TacticalLines.DIRATKAIR:
1444            case TacticalLines.DIRATKGND:
1445            case TacticalLines.DIRATKSPT:
1446            case TacticalLines.FCL:
1447            case TacticalLines.HOLD:
1448            case TacticalLines.BRDGHD:
1449            case TacticalLines.HOLD_GE:
1450            case TacticalLines.BRDGHD_GE:
1451            case TacticalLines.LOA:
1452            case TacticalLines.LOD:
1453            case TacticalLines.LDLC:
1454            case TacticalLines.PLD:
1455            case TacticalLines.RELEASE:
1456            case TacticalLines.HOL:
1457            case TacticalLines.BHL:
1458            case TacticalLines.LINE:
1459            case TacticalLines.ABATIS:
1460            case TacticalLines.ATDITCH:
1461            case TacticalLines.ATDITCHC:
1462            case TacticalLines.ATDITCHM:
1463            case TacticalLines.ATWALL:
1464            case TacticalLines.MNFLDFIX:
1465            case TacticalLines.UNSP:
1466            case TacticalLines.SFENCE:
1467            case TacticalLines.DFENCE:
1468            case TacticalLines.DOUBLEA:
1469            case TacticalLines.LWFENCE:
1470            case TacticalLines.HWFENCE:
1471            case TacticalLines.SINGLEC:
1472            case TacticalLines.DOUBLEC:
1473            case TacticalLines.TRIPLE:
1474            case TacticalLines.FORTL:
1475            case TacticalLines.LINTGT:
1476            case TacticalLines.LINTGTS:
1477            case TacticalLines.FSCL:
1478            case TacticalLines.BCL_REVD:
1479            case TacticalLines.BCL:
1480            case TacticalLines.ICL:
1481            case TacticalLines.IFF_OFF:
1482            case TacticalLines.IFF_ON:
1483            case TacticalLines.GENERIC_LINE:
1484            case TacticalLines.CFL:
1485            case TacticalLines.NFL:
1486            case TacticalLines.MFP:
1487            case TacticalLines.RFL:
1488            case TacticalLines.MSR:
1489            case TacticalLines.MSR_ONEWAY:
1490            case TacticalLines.MSR_TWOWAY:
1491            case TacticalLines.MSR_ALT:
1492            case TacticalLines.ASR:
1493            case TacticalLines.ASR_ONEWAY:
1494            case TacticalLines.ASR_TWOWAY:
1495            case TacticalLines.ASR_ALT:
1496            case TacticalLines.ROUTE:
1497            case TacticalLines.ROUTE_ONEWAY:
1498            case TacticalLines.ROUTE_ALT:
1499                //undo any fill
1500                Shape2 shape = null;
1501                if (shapes != null && shapes.size() > 0) {
1502                    int n = shapes.size();
1503                    //for(int j=0;j<shapes.size();j++)
1504                    for (int j = 0; j < n; j++) {
1505                        shape = shapes.get(j);
1506                        if (shape.getShapeType() == Shape2.SHAPE_TYPE_POLYLINE) {
1507                            shapes.get(j).setFillColor(null);
1508                        }
1509                    }
1510                }
1511                return true;
1512            default:
1513                return false;
1514
1515        }
1516    }
1517
1518    /**
1519     * uses a hash map to set the POINT2 style when creating tg.Pixels from
1520     * Point2D ArrayList
1521     *
1522     * @param pts2d
1523     * @param hashMap
1524     * @return
1525     */
1526    protected static ArrayList<POINT2> Point2DtoPOINT2Mapped(ArrayList<Point2D> pts2d, Map<String, Object> hashMap) {
1527        ArrayList<POINT2> pts = new ArrayList();
1528        try {
1529            Point2D pt2d;
1530            int style = 0;
1531            int n = pts2d.size();
1532            //for(int j=0;j<pts2d.size();j++)
1533            for (int j = 0; j < n; j++) {
1534                pt2d = pts2d.get(j);
1535                //the hash map contains the original tg.Pixels before clipping
1536                if (hashMap.containsValue(pt2d)) {
1537                    style = 0;
1538                } else {
1539                    style = -1;   //style set to -1 identifies it as a clip bounds point
1540                }
1541                pts.add(new POINT2(pts2d.get(j).getX(), pts2d.get(j).getY(), style));
1542            }
1543        } catch (Exception exc) {
1544            ErrorLogger.LogException(_className, "Point2DToPOINT2Mapped",
1545                    new RendererException("Failed inside Point2DToPOINT2Mapped", exc));
1546        }
1547        return pts;
1548    }
1549
1550    protected static ArrayList<POINT2> Point2DtoPOINT2(ArrayList<Point2D> pts2d) {
1551        ArrayList<POINT2> pts = new ArrayList();
1552        try {
1553            int n = pts2d.size();
1554            //for(int j=0;j<pts2d.size();j++)
1555            for (int j = 0; j < n; j++) {
1556                pts.add(new POINT2(pts2d.get(j).getX(), pts2d.get(j).getY()));
1557            }
1558        } catch (Exception exc) {
1559            ErrorLogger.LogException(_className, "Point2DToPOINT2",
1560                    new RendererException("Failed inside Point2DToPOINT2", exc));
1561        }
1562        return pts;
1563    }
1564
1565    protected static ArrayList<Point2D> POINT2toPoint2D(ArrayList<POINT2> pts) {
1566        ArrayList<Point2D> pts2d = new ArrayList();
1567        try {
1568            int n = pts.size();
1569            //for(int j=0;j<pts.size();j++)
1570            for (int j = 0; j < n; j++) {
1571                pts2d.add(new Point2D.Double(pts.get(j).x, pts.get(j).y));
1572            }
1573
1574        } catch (Exception exc) {
1575            ErrorLogger.LogException(_className, "POINT2toPoint2D",
1576                    new RendererException("Failed inside POINT2toPoint2D", exc));
1577        }
1578        return pts2d;
1579    }
1580
1581    /**
1582     * Builds a single shape from a point array. Currently we assume the array
1583     * represents a moveTo followed by a series of lineTo operations
1584     *
1585     * @param pts2d
1586     * @return
1587     */
1588    private static Shape BuildShapeFromPoints(ArrayList<Point2D> pts2d) {
1589        GeneralPath shape = new GeneralPath();
1590        try {
1591            shape.moveTo(pts2d.get(0).getX(), pts2d.get(0).getY());
1592            int n = pts2d.size();
1593            //for(int j=1;j<pts2d.size();j++)
1594            for (int j = 1; j < n; j++) {
1595                shape.lineTo(pts2d.get(j).getX(), pts2d.get(j).getY());
1596            }
1597        } catch (Exception exc) {
1598            ErrorLogger.LogException(_className, "buildShapeFromPoints",
1599                    new RendererException("Failed inside buildShapeFromPoints", exc));
1600
1601        }
1602        return shape;
1603    }
1604
1605    /**
1606     * Clips a ShapeSpec. Assumes we are not post clipping splines, therefore
1607     * all the operations are moveTo, lineTo. Each ShapeSpec is assumed to be:
1608     * moveTo, lineTo ... lineTo, followed by another moveTo, lineTo, ...
1609     * lineTo, followed by ...
1610     *
1611     * @param shapeSpec
1612     * @param pts
1613     * @param clipArea
1614     * @return a single clipped shapeSpec
1615     */
1616    protected static ArrayList<Shape2> buildShapeSpecFromPoints(TGLight tg0,
1617            Shape2 shapeSpec, //the original ShapeSpec
1618            ArrayList<POINT2> pts,
1619            Object clipArea) {
1620        ArrayList<Shape2> shapeSpecs2 = null;
1621        Shape2 shapeSpec2;
1622        try {
1623            //create a tg to use for the clip
1624            shapeSpecs2 = new ArrayList();
1625            int j = 0, n = 0;
1626            //return null if it is outside the bounds
1627            Rectangle rect = shapeSpec.getBounds();
1628            int h = shapeSpec.getBounds().height;
1629            int w = shapeSpec.getBounds().width;
1630            int x = shapeSpec.getBounds().x;
1631            int y = shapeSpec.getBounds().y;
1632//            if(h==0 && w==0)
1633//                return shapeSpecs2;
1634
1635            if (h == 0) {
1636                h = 1;
1637            }
1638            if (w == 0) {
1639                w = 1;
1640            }
1641
1642            Rectangle2D clipBounds = null;
1643            ArrayList<Point2D> clipPoints = null;
1644            if (clipArea != null && clipArea.getClass().isAssignableFrom(Rectangle2D.Double.class)) {
1645                clipBounds = (Rectangle2D) clipArea;
1646            } else if (clipArea != null && clipArea.getClass().isAssignableFrom(Rectangle.class)) {
1647                //clipBounds=(Rectangle2D)clipArea;
1648                Rectangle rectx = (Rectangle) clipArea;
1649                clipBounds = new Rectangle2D.Double(rectx.x, rectx.y, rectx.width, rectx.height);
1650            } else if (clipArea != null && clipArea.getClass().isAssignableFrom(ArrayList.class)) {
1651                clipPoints = (ArrayList<Point2D>) clipArea;
1652            }
1653
1654            if (clipBounds != null && clipBounds.contains(shapeSpec.getShape().getBounds2D()) == false
1655                    && clipBounds.intersects(shapeSpec.getShape().getBounds2D()) == false) {
1656                //this tests if the shape has height or width 0
1657                //but may be contained within the clipbounds or intersect it
1658                //in that case we gave it a default width or thickness of 1
1659                if (clipBounds.contains(x, y, w, h) == false
1660                        && clipBounds.intersects(x, y, w, h) == false) {
1661                    return shapeSpecs2;
1662                }
1663            } else if (clipPoints != null) {
1664                GeneralPath poly = new GeneralPath();
1665                n = clipPoints.size();
1666                //for(j=0;j<clipPoints.size();j++)
1667                for (j = 0; j < n; j++) {
1668                    if (j == 0) {
1669                        poly.moveTo(clipPoints.get(j).getX(), clipPoints.get(j).getY());
1670                    } else {
1671                        poly.lineTo(clipPoints.get(j).getX(), clipPoints.get(j).getY());
1672                    }
1673                }
1674                poly.closePath();
1675                if (poly.contains(shapeSpec.getShape().getBounds2D()) == false
1676                        && poly.intersects(shapeSpec.getShape().getBounds2D()) == false) {
1677                    if (poly.contains(x, y, w, h) == false
1678                            && poly.intersects(x, y, w, h) == false) {
1679                        return shapeSpecs2;
1680                    }
1681                }
1682            }
1683
1684            if (shapeSpec.getShapeType() == Shape2.SHAPE_TYPE_MODIFIER
1685                    || shapeSpec.getShapeType() == Shape2.SHAPE_TYPE_MODIFIER_FILL) {
1686                shapeSpecs2.add(shapeSpec);
1687                return shapeSpecs2;
1688            }
1689            TGLight tg = new TGLight();
1690            POINT2 pt = null;
1691            tg.set_LineType(TacticalLines.PL);
1692            ArrayList<POINT2> pts2 = new ArrayList();
1693            ArrayList<Point2D> pts2d = null;
1694            Shape shape = null;
1695            GeneralPath gp = new GeneralPath();
1696            //loop through the points
1697            n = pts.size();
1698            //for(j=0;j<pts.size();j++)
1699            for (j = 0; j < n; j++) {
1700                pt = pts.get(j);
1701                //new line
1702                switch (pt.style) {
1703                    case 0: //moveTo,
1704                        //they lifted the pencil, so we build the shape from the existing pts and append it
1705                        if (pts2.size() > 1) {
1706                            //clip the points
1707                            tg = new TGLight();
1708                            tg.set_LineType(TacticalLines.PL);
1709                            tg.Pixels = pts2;
1710                            if (clipBounds != null) {
1711                                pts2d = clsClipPolygon2.ClipPolygon(tg, clipBounds);
1712                            } else if (clipPoints != null && !clipPoints.isEmpty()) {
1713                                pts2d = clsClipQuad.ClipPolygon(tg, clipPoints);
1714                            }
1715
1716                            //build a GeneralPath from the points we collected, we will append it
1717                            if (pts2d != null && pts2d.size() > 1) {
1718                                shape = BuildShapeFromPoints(pts2d);
1719                                //append the shape because we want to return only one shape
1720                                gp.append(shape, false);
1721                            }
1722                            //clear the points array and begin the next line
1723                            pts2.clear();
1724                            pts2.add(pt);
1725                        } else {
1726                            pts2.add(pt);
1727                        }
1728                        break;
1729                    case 1: //lineTo
1730                        pts2.add(pt);
1731                        break;
1732                    default:
1733                        pts2.add(pt);
1734                        break;
1735                }
1736            }//end for
1737            //append the last shape
1738            if (pts2.size() > 1) {
1739                //clip the points
1740                tg = new TGLight();
1741                tg.set_LineType(TacticalLines.PL);
1742                tg.Pixels = pts2;
1743                if (clipBounds != null) {
1744                    pts2d = clsClipPolygon2.ClipPolygon(tg, clipBounds);
1745                } else if (clipPoints != null) {
1746                    pts2d = clsClipQuad.ClipPolygon(tg, clipPoints);
1747                }
1748                //build a GeneralPath from the points we collected, we will append it
1749                if (pts2d != null && pts2d.size() > 1) {
1750                    shape = BuildShapeFromPoints(pts2d);
1751                    gp.append(shape, false);
1752                }
1753                tg0.set_WasClipped(tg.get_WasClipped());
1754            }
1755            //create the shapespec here
1756            //initialize the clipped ShapeSpec
1757            shapeSpec2 = new Shape2(shapeSpec.getShapeType());
1758            shapeSpec2.setLineColor(shapeSpec.getLineColor());
1759            shapeSpec2.setFillColor(shapeSpec.getFillColor());
1760            shapeSpec2.setStroke(shapeSpec.getStroke());
1761            shapeSpec2.setTexturePaint(shapeSpec.getTexturePaint());
1762            shapeSpec2.setShape(gp);
1763            shapeSpecs2.add(shapeSpec2);
1764        } catch (Exception exc) {
1765            ErrorLogger.LogException(_className, "buildShapeSpecFromPoints",
1766                    new RendererException("Failed inside buildShapeSpecFromPoints", exc));
1767
1768        }
1769        return shapeSpecs2;
1770    }
1771
1772    /**
1773     * Currently assumes no MeTOC symbols are post clipped
1774     *
1775     * @param tg
1776     * @param shapeSpecsArray
1777     * @param clipArea
1778     * @return
1779     */
1780    protected static ArrayList<Shape2> postClipShapes(TGLight tg, ArrayList<Shape2> shapeSpecsArray, Object clipArea) {
1781        ArrayList<Shape2> shapeSpecs2 = null;
1782        ArrayList<Shape2> tempShapes = null;
1783        try {
1784            if (shapeSpecsArray == null || shapeSpecsArray.size() == 0) {
1785                return null;
1786            }
1787
1788            shapeSpecs2 = new ArrayList();
1789            int j = 0;
1790            ArrayList<Shape2> shapeSpecs = new ArrayList();
1791            int n = shapeSpecsArray.size();
1792            //for(j=0;j<shapeSpecsArray.size();j++)
1793            for (j = 0; j < n; j++) {
1794                shapeSpecs.add(shapeSpecsArray.get(j));
1795            }
1796
1797            ArrayList<POINT2> pts = new ArrayList();//use these
1798            Shape shape = null;
1799            POINT2 pt;
1800            double[] coords = new double[6];
1801            Shape2 shapeSpec = null;
1802            n = shapeSpecs.size();
1803            //for(j=0;j<shapeSpecs.size();j++)
1804            for (j = 0; j < n; j++) {
1805                shapeSpec = shapeSpecs.get(j);
1806                shape = shapeSpec.getShape();
1807                pts.clear();
1808                for (PathIterator i = shape.getPathIterator(null); !i.isDone(); i.next()) {
1809                    int type = i.currentSegment(coords);
1810                    switch (type) {
1811                        case PathIterator.SEG_MOVETO:
1812                            pt = new POINT2(coords[0], coords[1]);
1813                            pt.style = 0;
1814                            pts.add(pt);
1815                            break;
1816                        case PathIterator.SEG_LINETO:
1817                            pt = new POINT2(coords[0], coords[1]);
1818                            pt.style = 1;
1819                            pts.add(pt);
1820                            break;
1821                        case PathIterator.SEG_QUADTO:   //not using this
1822                            pt = new POINT2(coords[0], coords[1]);
1823                            pt.style = 2;
1824                            pts.add(pt);
1825                            pt = new POINT2(coords[2], coords[3]);
1826                            pt.style = 2;
1827                            pts.add(pt);
1828                            break;
1829                        case PathIterator.SEG_CUBICTO:  //not using this
1830                            pt = new POINT2(coords[0], coords[1]);
1831                            pt.style = 3;
1832                            pts.add(pt);
1833                            pt = new POINT2(coords[2], coords[3]);
1834                            pt.style = 3;
1835                            pts.add(pt);
1836                            pt = new POINT2(coords[4], coords[5]);
1837                            pt.style = 3;
1838                            pts.add(pt);
1839                            break;
1840                        case PathIterator.SEG_CLOSE://not using this
1841                            pt = new POINT2(coords[0], coords[1]);
1842                            pt.style = 4;
1843                            pts.add(pt);
1844                            break;
1845                        default:
1846                            pt = null;
1847                            break;
1848                    }//end switch
1849                }   //end for pathiterator i
1850                tempShapes = buildShapeSpecFromPoints(tg, shapeSpec, pts, clipArea);
1851                shapeSpecs2.addAll(tempShapes);
1852            }
1853        } catch (Exception exc) {
1854            ErrorLogger.LogException(_className, "postClipShapes",
1855                    new RendererException("Failed inside postClipShapes", exc));
1856        }
1857        return shapeSpecs2;
1858    }
1859
1860    /**
1861     * For the 3d map we cannot pre-segment the auto-shapes or fire support
1862     * areas. We do need to pre-segment generic lines regardless of the status
1863     * if clipping is set. Currently we are not pre-segmenting axis of advance
1864     * symbols.
1865     *
1866     * @param tg
1867     * @return true if pre-segmenting is to be used
1868     */
1869    private static boolean segmentAnticipatedLine(TGLight tg) {
1870        try {
1871            int linetype = tg.get_LineType();
1872            //do not pre-segment the fire support rectangular and circular areas
1873            if (clsUtility.IsChange1Area(linetype)) {
1874                return false;
1875            }
1876            //do not pre-segment the autoshapes
1877            if (clsUtility.isAutoshape(tg)) {
1878                return false;
1879            }
1880            //temporarily do not pre-segment the channel types.
1881            switch (linetype) {
1882                case TacticalLines.OVERHEAD_WIRE:
1883                case TacticalLines.CATK:
1884                case TacticalLines.CATKBYFIRE:
1885                case TacticalLines.MAIN:
1886                case TacticalLines.SPT:
1887                case TacticalLines.AIRAOA:
1888                case TacticalLines.AAAAA:
1889                    return false;
1890                case TacticalLines.MSR_ONEWAY:
1891                case TacticalLines.MSR_TWOWAY:
1892                case TacticalLines.MSR_ALT:
1893                case TacticalLines.ASR_ONEWAY:
1894                case TacticalLines.ASR_TWOWAY:
1895                case TacticalLines.ASR_ALT:
1896                case TacticalLines.ROUTE_ONEWAY:
1897                case TacticalLines.ROUTE_ALT:
1898                    //added because of segment data 4-22-13
1899                    //removed from this case block since we now post-clip these because of segment color data 10-5-16
1900//                case TacticalLines.MSR:
1901//                case TacticalLines.ASR:
1902//                case TacticalLines.BOUNDARY:
1903                    return false;
1904                default:
1905                    break;
1906            }
1907
1908        } catch (Exception exc) {
1909            ErrorLogger.LogException(_className, "segmentGenericLine",
1910                    new RendererException("Failed inside segmentGenericLine", exc));
1911        }
1912        return true;
1913    }
1914
1915    /**
1916     * cannot pre-segment the fire support areas, must post segment them after
1917     * the pixels were calculated
1918     *
1919     * @param tg
1920     * @param converter
1921     */
1922    protected static void postSegmentFSA(TGLight tg,
1923            IPointConversion converter) {
1924        try {
1925            if (tg.get_Client().equals("2D")) {
1926                return;
1927            }
1928
1929            int linetype = tg.get_LineType();
1930            switch (linetype) {
1931                case TacticalLines.PAA_RECTANGULAR:
1932                case TacticalLines.FSA_RECTANGULAR:
1933                case TacticalLines.SHIP_AOI_RECTANGULAR:
1934                case TacticalLines.DEFENDED_AREA_RECTANGULAR:
1935                case TacticalLines.FFA_RECTANGULAR:
1936                case TacticalLines.ACA_RECTANGULAR:
1937                case TacticalLines.NFA_RECTANGULAR:
1938                case TacticalLines.RFA_RECTANGULAR:
1939                case TacticalLines.ATI_RECTANGULAR:
1940                case TacticalLines.CFFZ_RECTANGULAR:
1941                case TacticalLines.SENSOR_RECTANGULAR:
1942                case TacticalLines.CENSOR_RECTANGULAR:
1943                case TacticalLines.DA_RECTANGULAR:
1944                case TacticalLines.CFZ_RECTANGULAR:
1945                case TacticalLines.ZOR_RECTANGULAR:
1946                case TacticalLines.TBA_RECTANGULAR:
1947                case TacticalLines.TVAR_RECTANGULAR:
1948                case TacticalLines.KILLBOXBLUE_RECTANGULAR:
1949                case TacticalLines.KILLBOXPURPLE_RECTANGULAR:
1950                    break;
1951                default:
1952                    return;
1953            }
1954            ArrayList<POINT2> latLongs = new ArrayList();
1955            ArrayList<POINT2> resultPts = new ArrayList();
1956            int j = 0, k = 0, n = 0;
1957            POINT2 pt0 = null, pt1 = null, pt = null;
1958            double dist = 0;
1959            //double interval=1000000;
1960            double interval = 250000;
1961            double az = 0;
1962
1963            double maxDist = 0;
1964            Point2D pt2d = null;
1965            int t = tg.Pixels.size();
1966            //for(j=0;j<tg.Pixels.size();j++)
1967            for (j = 0; j < t; j++) {
1968                pt0 = tg.Pixels.get(j);
1969                pt2d = new Point2D.Double(pt0.x, pt0.y);
1970                pt2d = converter.PixelsToGeo(pt2d);
1971                pt0 = new POINT2(pt2d.getX(), pt2d.getY());
1972                latLongs.add(pt0);
1973            }
1974            t = latLongs.size();
1975            //for(j=0;j<latLongs.size()-1;j++)
1976            for (j = 0; j < t - 1; j++) {
1977                pt0 = latLongs.get(j);
1978                pt1 = latLongs.get(j + 1);
1979                pt1.style = -1;//end point
1980                az = mdlGeodesic.GetAzimuth(pt0, pt1);
1981                dist = mdlGeodesic.geodesic_distance(latLongs.get(j), latLongs.get(j + 1), null, null);
1982                if (dist > maxDist) {
1983                    maxDist = dist;
1984                }
1985            }
1986
1987            if (interval > maxDist) {
1988                interval = maxDist;
1989            }
1990
1991            //for(j=0;j<latLongs.size()-1;j++)
1992            for (j = 0; j < t - 1; j++) {
1993                pt0 = new POINT2(latLongs.get(j));
1994                pt0.style = 0;//anchor point
1995                pt1 = new POINT2(latLongs.get(j + 1));
1996                pt1.style = 0;//anchor point point
1997                az = mdlGeodesic.GetAzimuth(pt0, pt1);
1998                dist = mdlGeodesic.geodesic_distance(latLongs.get(j), latLongs.get(j + 1), null, null);
1999
2000                n = (int) (dist / interval);
2001                if (j == 0) {
2002                    resultPts.add(pt0);
2003                }
2004
2005                for (k = 1; k <= n; k++) {
2006                    pt = mdlGeodesic.geodesic_coordinate(pt0, interval * k, az);
2007                    pt.style = -2;
2008                    //we do not want the last segment to be too close to the anchor point
2009                    //only add the segment point if it is a distance at least half the inteval
2010                    //from the 2nd anchor point
2011                    dist = mdlGeodesic.geodesic_distance(pt, pt1, null, null);
2012                    if (dist >= interval / 2) {
2013                        resultPts.add(pt);
2014                    }
2015                }
2016                //ad the 2nd anchor point
2017                resultPts.add(pt1);
2018            }
2019            latLongs = resultPts;
2020            tg.Pixels = armyc2.c5isr.RenderMultipoints.clsUtility.LatLongToPixels(latLongs, converter);
2021        } catch (Exception exc) {
2022            ErrorLogger.LogException(_className, "postSegmentFSA",
2023                    new RendererException("Failed inside postSegmentFSA", exc));
2024        }
2025    }
2026
2027    /**
2028     * Similar to Vincenty algorithm for more accurate interpolation of geo
2029     * anchor points
2030     *
2031     * @return the interpolated points
2032     */
2033    private static ArrayList<POINT2> toGeodesic(TGLight tg, double interval, HashMap<Integer,String> hmap) {
2034        ArrayList<POINT2> locs = new ArrayList<POINT2>();
2035        try {
2036            int i = 0, k = 0, n = 0;
2037            ArrayList<POINT2> points = tg.LatLongs;
2038            String H = "";
2039            String color = "";
2040            boolean bolIsAC = false;
2041            int acWidth = 0;
2042            int linetype = tg.get_LineType();
2043            switch (linetype) {
2044                case TacticalLines.AC:
2045                case TacticalLines.LLTR:
2046                case TacticalLines.MRR:
2047                case TacticalLines.SL:
2048                case TacticalLines.SAAFR:
2049                case TacticalLines.TC:
2050                case TacticalLines.SC:
2051                    bolIsAC = true;
2052                    break;
2053                default:
2054                    break;
2055            }
2056            for (i = 0; i < points.size() - 1; i++) {
2057                if(bolIsAC)
2058                    acWidth=points.get(i).style;
2059                // Convert coordinates from degrees to Radians
2060                //var lat1 = points[i].latitude * (PI / 180);
2061                //var lon1 = points[i].longitude * (PI / 180);
2062                //var lat2 = points[i + 1].latitude * (PI / 180);
2063                //var lon2 = points[i + 1].longitude * (PI / 180);                
2064                double lat1 = Math.toRadians(points.get(i).y);
2065                double lon1 = Math.toRadians(points.get(i).x);
2066                double lat2 = Math.toRadians(points.get(i + 1).y);
2067                double lon2 = Math.toRadians(points.get(i + 1).x);
2068                // Calculate the total extent of the route
2069                //var d = 2 * asin(sqrt(pow((sin((lat1 - lat2) / 2)), 2) + cos(lat1) * cos(lat2) * pow((sin((lon1 - lon2) / 2)), 2)));
2070                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)));
2071
2072                double dist = mdlGeodesic.geodesic_distance(points.get(i), points.get(i + 1), null, null);
2073                //double dist=d;
2074                float flt = (float) dist / (float) interval;
2075                n = Math.round(flt);
2076                if (n < 1) {
2077                    n = 1;
2078                }
2079                if (n > 32) {
2080                    n = 32;
2081                }
2082                // Calculate  positions at fixed intervals along the route
2083                for (k = 0; k <= n; k++) {
2084                    //we must preserve the anchor points
2085                    if (k == 0) {
2086                        locs.add(new POINT2(points.get(i)));
2087                        if (hmap != null && hmap.containsKey(i)) {
2088                            if (!H.isEmpty()) {
2089                                H += ",";
2090                            }
2091                            color = (String) hmap.get(i);
2092                            H += Integer.toString(locs.size() - 1) + ":" + color;
2093                        }
2094                        continue;
2095                    } else if (k == n) {
2096                        if (i == points.size() - 2) {
2097                            locs.add(new POINT2(points.get(i + 1)));
2098                            if (hmap != null && hmap.containsKey(i + 1)) {
2099                                if (!H.isEmpty()) {
2100                                    H += ",";
2101                                }
2102                                color = (String) hmap.get(i + 1);
2103                                H += Integer.toString(locs.size() - 1) + ":" + color;
2104                            }
2105                        }
2106                        break;
2107                    }
2108                    //var f = (k / n);
2109                    //var A = sin((1 - f) * d) / sin(d);
2110                    //var B = sin(f * d) / sin(d);
2111                    double f = ((double) k / (double) n);
2112                    double A = Math.sin((1 - f) * d) / Math.sin(d);
2113                    double B = Math.sin(f * d) / Math.sin(d);
2114                    // Obtain 3D Cartesian coordinates of each point
2115                    //var x = A * cos(lat1) * cos(lon1) + B * cos(lat2) * cos(lon2);
2116                    //var y = A * cos(lat1) * sin(lon1) + B * cos(lat2) * sin(lon2);
2117                    //var z = A * sin(lat1) + B * sin(lat2);
2118                    double x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) * Math.cos(lon2);
2119                    double y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) * Math.sin(lon2);
2120                    double z = A * Math.sin(lat1) + B * Math.sin(lat2);
2121                    // Convert these to latitude/longitude
2122                    //var lat = atan2(z, sqrt(pow(x, 2) + pow(y, 2)));
2123                    //var lon = atan2(y, x);
2124                    double lat = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
2125                    double lon = Math.atan2(y, x);
2126                    lat *= 180.0 / Math.PI;
2127                    lon *= 180.0 / Math.PI;
2128                    POINT2 pt = new POINT2(lon, lat);
2129                    if(bolIsAC)
2130                        pt.style=-acWidth;
2131                    locs.add(pt);
2132                    if (hmap != null && hmap.containsKey(i)) {
2133                        if (!H.isEmpty()) {
2134                            H += ",";
2135                        }
2136                        color = (String) hmap.get(i);
2137                        H += Integer.toString(locs.size() - 1) + ":" + color;
2138                    }
2139                }
2140            }
2141            if (!H.isEmpty()) {
2142                tg.set_H(H);
2143            }
2144        } catch (Exception exc) {
2145            ErrorLogger.LogException(_className, "toGeodesic",
2146                    new RendererException("Failed inside toGeodesic", exc));
2147            return null;
2148        }
2149        return locs;
2150    }
2151
2152    /**
2153     * Pre-segment the lines based on max or min latitude for the segment
2154     * interval. This is necessary because GeoPixelconversion does not work well
2155     * over distance greater than 1M meters, especially at extreme latitudes.
2156     *
2157     * @param tg
2158     * @param converter
2159     */
2160    protected static void SegmentGeoPoints(TGLight tg,
2161            IPointConversion converter,
2162            double zoomFactor) {
2163        try {
2164            if (tg.get_Client().equals("2D")) {
2165                return;
2166            }
2167
2168            ArrayList<POINT2> resultPts = new ArrayList();
2169            int lineType = tg.get_LineType();
2170            //double interval=1000000;
2171            double interval = 250000;
2172            boolean bolSegmentAC = false, bolIsAC = false;
2173            bolSegmentAC = true;
2174            //conservative interval in meters
2175            //return early for those lines not requiring pre-segmenting geo points
2176            switch (lineType) {
2177                case TacticalLines.AC:
2178                case TacticalLines.LLTR:
2179                case TacticalLines.MRR:
2180                case TacticalLines.SL:
2181                case TacticalLines.SAAFR:
2182                case TacticalLines.TC:
2183                case TacticalLines.SC:
2184                    if (!bolSegmentAC) {
2185                        return;
2186                    }
2187                    bolIsAC = true;
2188                    break;
2189                case TacticalLines.PLD:
2190                case TacticalLines.CFL:
2191                case TacticalLines.UNSP:
2192                case TacticalLines.TRIPLE:
2193                case TacticalLines.DOUBLEC:
2194                case TacticalLines.SINGLEC:
2195                case TacticalLines.ATDITCH:
2196                case TacticalLines.ATDITCHC:
2197                case TacticalLines.ATDITCHM:
2198                case TacticalLines.ATWALL:
2199                case TacticalLines.LINE:
2200                case TacticalLines.DIRATKAIR:
2201                case TacticalLines.STRONG:
2202                case TacticalLines.ENCIRCLE:
2203                case TacticalLines.FLOT:
2204                case TacticalLines.ZONE:
2205                case TacticalLines.OBSAREA:
2206                case TacticalLines.OBSFAREA:
2207                case TacticalLines.FORT_REVD:
2208                case TacticalLines.FORT:
2209                case TacticalLines.FORTL:
2210                    break;
2211                case TacticalLines.HWFENCE:
2212                case TacticalLines.LWFENCE:
2213                case TacticalLines.DOUBLEA:
2214                case TacticalLines.DFENCE:
2215                case TacticalLines.SFENCE:
2216                    interval = 500000;
2217                    break;
2218                case TacticalLines.LC:
2219                    interval = 2000000;
2220                    break;
2221                default:
2222                    //if the line is an anticipated generic line then segment the line
2223                    if (segmentAnticipatedLine(tg)) {
2224                        break;
2225                    }
2226                    return;
2227            }
2228
2229            int j = 0, k = 0, n = 0;
2230            POINT2 pt0 = null, pt1 = null, pt = null;
2231            double dist = 0;
2232            double az = 0;
2233
2234            double maxDist = 0;
2235            int t = tg.LatLongs.size();
2236            //for(j=0;j<tg.LatLongs.size()-1;j++)
2237            for (j = 0; j < t - 1; j++) {
2238                pt0 = tg.LatLongs.get(j);
2239                pt1 = tg.LatLongs.get(j + 1);
2240                if(!bolIsAC)
2241                    pt1.style = -1;//end point
2242                az = mdlGeodesic.GetAzimuth(pt0, pt1);
2243                dist = mdlGeodesic.geodesic_distance(tg.LatLongs.get(j), tg.LatLongs.get(j + 1), null, null);
2244                if (dist > maxDist) {
2245                    maxDist = dist;
2246                }
2247            }
2248
2249            if (interval > maxDist) {
2250                interval = maxDist;
2251            }
2252
2253            if (zoomFactor > 0 && zoomFactor < 0.01) {
2254                zoomFactor = 0.01;
2255            }
2256            if (zoomFactor > 0 && zoomFactor < 1) {
2257                interval *= zoomFactor;
2258            }
2259
2260            boolean useVincenty = false;
2261            String H = "";
2262            String color = "";
2263            HashMap<Integer,String> hmap = clsUtility.getMSRSegmentColorStrings(tg);
2264            if (hmap != null) {
2265                tg.set_H("");
2266            }
2267            //uncomment one line to use (similar to) Vincenty algorithm
2268            useVincenty = true;
2269            if (useVincenty) {
2270                resultPts = toGeodesic(tg, interval, hmap);
2271                tg.LatLongs = resultPts;
2272                tg.Pixels = armyc2.c5isr.RenderMultipoints.clsUtility.LatLongToPixels(tg.LatLongs, converter);
2273                return;
2274            }
2275
2276            for (j = 0; j < tg.LatLongs.size() - 1; j++) {
2277                pt0 = new POINT2(tg.LatLongs.get(j));
2278                pt0.style = 0;//anchor point
2279                pt1 = new POINT2(tg.LatLongs.get(j + 1));
2280                pt1.style = 0;//anchor point point
2281                az = mdlGeodesic.GetAzimuth(pt0, pt1);
2282                dist = mdlGeodesic.geodesic_distance(tg.LatLongs.get(j), tg.LatLongs.get(j + 1), null, null);
2283
2284                n = (int) (dist / interval);
2285                if (j == 0) {
2286                    resultPts.add(pt0);
2287                    if (hmap != null && hmap.containsKey(j)) {
2288                        if (!H.isEmpty()) {
2289                            H += ",";
2290                        }
2291                        color = (String) hmap.get(j);
2292                        //H+=(resultPts.size()-1).toString()+":"+color;
2293                        H += Integer.toString(resultPts.size() - 1) + ":" + color;
2294                    }
2295                }
2296                for (k = 1; k <= n; k++) {
2297                    pt = mdlGeodesic.geodesic_coordinate(pt0, interval * k, az);
2298                    pt.style = -2;
2299                    //we do not want the last segment to be too close to the anchor point
2300                    //only add the segment point if it is a distance at least half the inteval
2301                    //from the 2nd anchor point
2302                    dist = mdlGeodesic.geodesic_distance(pt, pt1, null, null);
2303                    if (dist >= interval / 2) {
2304                        resultPts.add(pt);
2305                        if (hmap != null && hmap.containsKey(j)) {
2306                            color = (String) hmap.get(j);
2307                            if (!H.isEmpty()) {
2308                                H += ",";
2309                            }
2310                            //H+=(resultPts.size()-1).toString()+":"+color;
2311                            H += Integer.toString(resultPts.size() - 1) + ":" + color;
2312                        }
2313                    }
2314                }
2315                //ad the 2nd anchor point
2316                resultPts.add(pt1);
2317                if (hmap != null && hmap.containsKey(j + 1)) {
2318                    if (!H.isEmpty()) {
2319                        H += ",";
2320                    }
2321                    color = (String) hmap.get(j + 1);
2322                    //H+=(resultPts.size()-1).toString()+":"+color;
2323                    H += Integer.toString(resultPts.size() - 1) + ":" + color;
2324                }
2325            }
2326            if (!H.isEmpty()) {
2327                tg.set_H(H);
2328            }
2329            tg.LatLongs = resultPts;
2330            tg.Pixels = armyc2.c5isr.RenderMultipoints.clsUtility.LatLongToPixels(tg.LatLongs, converter);
2331        } catch (Exception exc) {
2332            ErrorLogger.LogException(_className, "SegmentGeoPoints",
2333                    new RendererException("Failed inside SegmentGeoPoints", exc));
2334        }
2335    }
2336
2337}