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