001/*
002 * A class to create renderables for the ShapeInfo from the GeneralPath
003 * This class is used for the GoogleEarth Renderer
004 * To change this template, choose Tools | Templates
005 * and open the template in the editor.
006 */
007
008package armyc2.c5isr.RenderMultipoints;
009import armyc2.c5isr.JavaTacticalRenderer.TGLight;
010import armyc2.c5isr.JavaLineArray.TacticalLines;
011import java.util.ArrayList;
012import java.util.Arrays;
013
014import armyc2.c5isr.JavaLineArray.Shape2;
015import armyc2.c5isr.JavaLineArray.lineutility;
016import armyc2.c5isr.renderer.utilities.ErrorLogger;
017import armyc2.c5isr.renderer.utilities.RendererException;
018import armyc2.c5isr.renderer.utilities.ShapeInfo;
019import armyc2.c5isr.JavaLineArray.POINT2;
020import static armyc2.c5isr.JavaLineArray.lineutility.CalcDistanceDouble;
021
022import armyc2.c5isr.JavaTacticalRenderer.clsMETOC;
023import armyc2.c5isr.renderer.utilities.Color;
024import armyc2.c5isr.renderer.utilities.RendererSettings;
025import armyc2.c5isr.graphics2d.Area;
026import armyc2.c5isr.graphics2d.BasicStroke;
027import armyc2.c5isr.graphics2d.Line2D;
028import armyc2.c5isr.graphics2d.PathIterator;
029import armyc2.c5isr.graphics2d.Point2D;
030import armyc2.c5isr.graphics2d.Polygon;
031import armyc2.c5isr.graphics2d.Rectangle;
032import armyc2.c5isr.graphics2d.Rectangle2D;
033import armyc2.c5isr.graphics2d.Shape;
034
035/**
036 * Utilities require for GoogleEarth functionality
037*
038 */
039public final class clsUtilityGE {
040    private static final String _className="clsUtilityGE";
041    protected static void setSplineLinetype(TGLight tg)
042    {
043        switch(tg.get_LineType())
044        {
045            case TacticalLines.BRDGHD:
046                tg.set_LineType(TacticalLines.BRDGHD_GE);
047                break;
048            case TacticalLines.HOLD:
049                tg.set_LineType(TacticalLines.HOLD_GE);
050                break;
051            case TacticalLines.ICE_OPENINGS_FROZEN:
052                tg.set_LineType(TacticalLines.ICE_OPENINGS_FROZEN_GE);
053                break;
054            case TacticalLines.ICE_OPENINGS_LEAD:
055                tg.set_LineType(TacticalLines.ICE_OPENINGS_LEAD_GE);
056                break;
057            case TacticalLines.ICE_EDGE_RADAR:
058                tg.set_LineType(TacticalLines.ICE_EDGE_RADAR_GE);
059                break;
060            case TacticalLines.CRACKS_SPECIFIC_LOCATION:
061                tg.set_LineType(TacticalLines.CRACKS_SPECIFIC_LOCATION_GE);
062                break;
063            case TacticalLines.JET:
064                tg.set_LineType(TacticalLines.JET_GE);
065                break;
066            case TacticalLines.STREAM:
067                tg.set_LineType(TacticalLines.STREAM_GE);
068                break;
069            case TacticalLines.FLOOD_TIDE:
070                tg.set_LineType(TacticalLines.FLOOD_TIDE_GE);
071                break;
072            case TacticalLines.EBB_TIDE:
073                tg.set_LineType(TacticalLines.EBB_TIDE_GE);
074                break;
075            case TacticalLines.SEAWALL:
076                tg.set_LineType(TacticalLines.SEAWALL_GE);
077                break;
078            case TacticalLines.JETTY_BELOW_WATER:
079                tg.set_LineType(TacticalLines.JETTY_BELOW_WATER_GE);
080                break;
081            case TacticalLines.JETTY_ABOVE_WATER:
082                tg.set_LineType(TacticalLines.JETTY_ABOVE_WATER_GE);
083                break;
084            case TacticalLines.RAMP_BELOW_WATER:
085                tg.set_LineType(TacticalLines.RAMP_BELOW_WATER_GE);
086                break;
087            case TacticalLines.RAMP_ABOVE_WATER:
088                tg.set_LineType(TacticalLines.RAMP_ABOVE_WATER_GE);
089                break;
090            case TacticalLines.PIER:
091                tg.set_LineType(TacticalLines.PIER_GE);
092                break;
093            case TacticalLines.COASTLINE:
094                tg.set_LineType(TacticalLines.COASTLINE_GE);
095                break;
096            case TacticalLines.DEPTH_CONTOUR:
097                tg.set_LineType(TacticalLines.DEPTH_CONTOUR_GE);
098                break;
099            case TacticalLines.DEPTH_CURVE:
100                tg.set_LineType(TacticalLines.DEPTH_CURVE_GE);
101                break;
102            case TacticalLines.CRACKS:
103                tg.set_LineType(TacticalLines.CRACKS_GE);
104                break;
105            case TacticalLines.ESTIMATED_ICE_EDGE:
106                tg.set_LineType(TacticalLines.ESTIMATED_ICE_EDGE_GE);
107                break;
108            case TacticalLines.ICE_EDGE:
109                tg.set_LineType(TacticalLines.ICE_EDGE_GE);
110                break;
111            case TacticalLines.ISOTHERM:
112                tg.set_LineType(TacticalLines.ISOTHERM_GE);
113                break;
114            case TacticalLines.UPPER_AIR:
115                tg.set_LineType(TacticalLines.UPPER_AIR_GE);
116                break;
117            case TacticalLines.ISOBAR:
118                tg.set_LineType(TacticalLines.ISOBAR_GE);
119                break;
120            case TacticalLines.ISODROSOTHERM:
121                tg.set_LineType(TacticalLines.ISODROSOTHERM_GE);
122                break;
123            case TacticalLines.ISOTACH:
124                tg.set_LineType(TacticalLines.ISOTACH_GE);
125                break;
126            case TacticalLines.ISOPLETHS:
127                tg.set_LineType(TacticalLines.ISOPLETHS_GE);
128                break;
129            default:
130                break;
131        }
132        return;
133    }
134    
135    /**
136     * GE has no capability for dashed lines. This function sets each polyline in the array as a new
137     * polyline broken into points corresponding to the dash pattern
138     * @param polylines
139     * @param shape
140     */
141    private static void createDashedPolylines(ArrayList<ArrayList<Point2D>>polylines, ShapeInfo shape) {
142        try {
143            if (shape.getLineColor() == null) {
144                return;
145            }
146
147            BasicStroke stroke = shape.getStroke();
148            float[] dash = stroke.getDashArray();
149            if (dash == null || dash.length < 2) {
150                return;
151            }
152
153            ArrayList<ArrayList<Point2D>> dashedPolylines = new ArrayList<>();
154
155            for (ArrayList<Point2D> polyline : polylines) {
156                int dashIndex = 0; // Current index in dash array
157                double remainingInIndex = dash[dashIndex]; // Length remaining in current dash array index
158                for (int i = 0; i < polyline.size() - 1; i++) {
159                    Point2D segStartPt = polyline.get(i); // segment start, moves as segment is processed
160                    final Point2D segEndPt = polyline.get(i + 1); // Segment end
161
162                    double segLength; // distance remaining in segment
163                    while ((segLength = lineutility.CalcDistanceDouble(segStartPt, segEndPt)) > 0) {
164                        // If the line segment length is shorter than the current dash then move to the end of the segment continuing to draw or move
165                        // Otherwise move to the end of the current dash and start the next dash there
166                        if (segLength < remainingInIndex) {
167                            if (dashIndex % 2 == 0) {
168                                // Continue line
169                                ArrayList<Point2D> dashedPolyline = new ArrayList<>(Arrays.asList(segStartPt, segEndPt));
170                                dashedPolylines.add(dashedPolyline);
171                            }
172                            remainingInIndex -= segLength;
173                            break; // Next segment
174                        } else {
175                            // Flip to line or space at dashFlipPoint
176                            Point2D dashFlipPoint = lineutility.ExtendAlongLineDouble2(segStartPt, segEndPt, remainingInIndex);
177                            if (dashIndex % 2 == 0) {
178                                // Continue line
179                                ArrayList<Point2D> dashedPolyline = new ArrayList<>(Arrays.asList(segStartPt, dashFlipPoint));
180                                dashedPolylines.add(dashedPolyline);
181                            }
182                            // Next dash
183                            dashIndex++;
184                            if (dashIndex >= dash.length)
185                                dashIndex = 0;
186                            remainingInIndex = dash[dashIndex];
187                            segStartPt = dashFlipPoint;
188                        }
189                    }
190                }
191            }
192            polylines.clear();
193            polylines.addAll(dashedPolylines);
194        } catch (Exception exc) {
195            ErrorLogger.LogException(_className, "createDashedPolylines",
196                    new RendererException("Failed inside createDashedPolylines", exc));
197        }
198    }
199    private static ShapeInfo createSimpleFillShape(TGLight tg,ShapeInfo shape,ArrayList<ArrayList<Point2D>>polylines)
200    {
201        try
202        {
203            BasicStroke s=shape.getStroke();
204            float[]dash=s.getDashArray();
205            if(armyc2.c5isr.JavaTacticalRenderer.clsUtility.isClosedPolygon(tg.get_LineType())==false)
206                if(armyc2.c5isr.JavaTacticalRenderer.clsUtility.IsChange1Area(tg.get_LineType())==false)
207                    return null;
208            if(dash==null || dash.length<2)
209                return null;   
210            if(shape.getFillColor()==null)
211                return null;
212            
213            //if we reach this point we know it is a dashed line so we need a separate fill shape
214            int j=0,k=0;
215            ShapeInfo shape2=new ShapeInfo(shape.getShape());
216            shape2.setShapeType(ShapeInfo.SHAPE_TYPE_FILL);
217            ArrayList<ArrayList<Point2D>>polylines2=new ArrayList();
218            ArrayList<Point2D>polyline=null,polyline2=null;
219            Point2D pt2d=null;
220            s=new BasicStroke(0);
221            shape2.setStroke(s);
222            shape2.setFillColor(shape.getFillColor());            
223            int n=polylines.size();
224            //for(j=0;j<polylines.size();j++)
225            for(j=0;j<n;j++)
226            {
227                polyline=polylines.get(j);
228                polyline2=new ArrayList();
229                int t=polyline.size();
230                //for(k=0;k<polyline.size();k++)
231                for(k=0;k<t;k++)
232                {
233                    pt2d=new Point2D.Double(polyline.get(k).getX(),polyline.get(k).getY());
234                    polyline2.add(pt2d);
235                }
236                polylines2.add(polyline2);
237            }
238            //reset our original dashed shapinfo type to polyline
239            shape.setShapeType(ShapeInfo.SHAPE_TYPE_POLYLINE);
240            //this line will prevent unecessary work by multipointhandler
241            shape.setFillColor(null);
242            shape2.setPolylines(polylines2);            
243//            shape2.setAffineTransform(new AffineTransform());
244            return shape2;
245        }
246        catch (Exception exc) {
247            ErrorLogger.LogException(_className, "createSimpleFillShape",
248                    new RendererException("Failed inside createSimpleFillShape", exc));
249        }
250        return null;
251    }
252    private static ShapeInfo createSimplePatternFillShape(TGLight tg,ShapeInfo shape,ArrayList<ArrayList<Point2D>>polylines)
253    {
254        try
255        {
256            BasicStroke s=shape.getStroke();
257            float[]dash=s.getDashArray();
258            if(armyc2.c5isr.JavaTacticalRenderer.clsUtility.isClosedPolygon(tg.get_LineType())==false)
259                if(armyc2.c5isr.JavaTacticalRenderer.clsUtility.IsChange1Area(tg.get_LineType())==false)
260                    return null;
261            if(dash==null || dash.length<2)
262                return null;
263            if(shape.getPatternFillImage()==null)
264                return null;
265
266            //if we reach this point we know it is a dashed line so we need a separate pattern fill shape
267            int j=0,k=0;
268            ShapeInfo shape2=new ShapeInfo(shape.getShape());
269            shape2.setShapeType(ShapeInfo.SHAPE_TYPE_FILL);
270            ArrayList<ArrayList<Point2D>>polylines2=new ArrayList();
271            ArrayList<Point2D>polyline=null,polyline2=null;
272            Point2D pt2d=null;
273            s=new BasicStroke(0);
274            shape2.setStroke(s);
275            shape2.setPatternFillImage(shape.getPatternFillImage());
276            shape2.setShader(shape.getShader());
277            int n=polylines.size();
278            //for(j=0;j<polylines.size();j++)
279            for(j=0;j<n;j++)
280            {
281                polyline=polylines.get(j);
282                polyline2=new ArrayList();
283                int t=polyline.size();
284                //for(k=0;k<polyline.size();k++)
285                for(k=0;k<t;k++)
286                {
287                    pt2d=new Point2D.Double(polyline.get(k).getX(),polyline.get(k).getY());
288                    polyline2.add(pt2d);
289                }
290                polylines2.add(polyline2);
291            }
292            //reset our original dashed shapinfo type to polyline
293            shape.setShapeType(ShapeInfo.SHAPE_TYPE_POLYLINE);
294            //this line will prevent unecessary work by multipointhandler
295            shape.setPatternFillImage(null);
296            shape.setShader(null);
297            shape2.setPolylines(polylines2);
298//            shape2.setAffineTransform(new AffineTransform());
299            return shape2;
300        }
301        catch (Exception exc) {
302            ErrorLogger.LogException(_className, "createSimplePatternFillShape",
303                    new RendererException("Failed inside createSimplePatternFillShape", exc));
304        }
305        return null;
306    }
307    private static boolean allowFillForThese(TGLight tg)
308    {
309        try
310        {
311            int linetype=tg.get_LineType();
312            int bolMETOC=clsMETOC.IsWeather(tg.get_SymbolId());
313            if(bolMETOC >= 0)            
314                return true;            
315
316            switch(linetype)
317            {
318                case TacticalLines.CATK:
319                case TacticalLines.CATKBYFIRE:
320                case TacticalLines.AIRAOA:
321                case TacticalLines.AAAAA:
322                case TacticalLines.MAIN:
323                case TacticalLines.SPT:
324                
325                case TacticalLines.SARA:
326                case TacticalLines.RANGE_FAN_SECTOR:
327                case TacticalLines.RADAR_SEARCH:
328                case TacticalLines.RANGE_FAN:
329                case TacticalLines.MNFLDFIX:
330                case TacticalLines.TURN:
331                case TacticalLines.MNFLDDIS:
332                //case TacticalLines.OVERHEAD_WIRE:
333                case TacticalLines.EASY:
334                case TacticalLines.ATDITCHC:
335                case TacticalLines.ATDITCHM:
336                case TacticalLines.FERRY:
337                case TacticalLines.BYDIF:
338                case TacticalLines.BYIMP:
339                case TacticalLines.DEPTH_AREA:
340                    return true;
341                default:
342                    return false;
343            }
344        }
345        catch (Exception exc) {
346            ErrorLogger.LogException(_className, "allowFillForThese",
347                    new RendererException("Failed inside allowFillForThese", exc));
348        }
349        return false;
350    }
351    protected static void SetShapeInfosPolylines(TGLight tg, ArrayList<ShapeInfo> shapeInfos, Object clipBounds)
352    {
353        try
354        {
355            int j=0;
356            Shape shape=null;
357            ShapeInfo shapeInfo=null;
358            ArrayList<ArrayList<Point2D>>polylines=null;
359            int type=-1;
360            ShapeInfo simpleFillShape =null;//diagnostic
361            Boolean isClosed= armyc2.c5isr.JavaTacticalRenderer.clsUtility.isClosedPolygon(tg.get_LineType());
362            int linetype=tg.get_LineType();
363            Color fillColor=null;
364            int n=shapeInfos.size();
365            //for(j=0;j<shapeInfos.size();j++)
366            for(j=0;j<n;j++)
367            {
368                shapeInfo=shapeInfos.get(j);
369                type=shapeInfo.getShapeType();
370                shape=shapeInfo.getShape();
371                if(isClosed==false && type != Shape2.SHAPE_TYPE_FILL)
372                    polylines=createRenderablesFromShape(tg,shape,type,clipBounds);
373                else
374                    polylines=createRenderablesFromShape(tg,shape,type,null);
375                //create a simple fill shape here and change the shape type to SHAPE_TYPE_POLYLINE if it has non-null dash
376                //add the simple fill shape to shapeInfos after the loop
377                if(simpleFillShape==null)
378                    simpleFillShape=createSimpleFillShape(tg,shapeInfo,polylines);
379                if(simpleFillShape==null)
380                    simpleFillShape=createSimplePatternFillShape(tg,shapeInfo,polylines);
381                
382                fillColor=shapeInfo.getFillColor();
383                //if(simpleFillShape!=null || fillColor != null)//the symbol has a basic fill shape
384                if(simpleFillShape!=null)//the symbol has a basic fill shape
385                    if(allowFillForThese(tg)==false)
386                        shapeInfo.setFillColor(null);
387
388                if (!tg.get_UseDashArray())
389                    createDashedPolylines(polylines, shapeInfo);
390                
391                shapeInfo.setPolylines(polylines);
392            }            
393            if(simpleFillShape != null)
394                shapeInfos.add(0,simpleFillShape);
395        }
396        catch (Exception exc) {
397            ErrorLogger.LogException(_className, "SetShapeInfosPolylines",
398                    new RendererException("Failed inside SetShapeInfosPolylines", exc));
399        }
400    }
401    /**
402     * Separates the Shape into separate polylines, eas as an ArrayList of Point2D
403     * @param shape
404     * @return
405     */
406    private static ArrayList<ArrayList<Point2D>>createRenderablesFromShape(TGLight tg, Shape shape, int shapeType, Object clipArea)
407    {
408        ArrayList<Point2D> ptsPoly=new ArrayList();
409        ArrayList<ArrayList<Point2D>>polylines2=new ArrayList<ArrayList<Point2D>>();
410        Point2D ptPoly=null;        
411        try 
412        {
413            //this is not going to work for splines
414            double[] coords = new double[6];
415            for (PathIterator i = shape.getPathIterator(null); !i.isDone(); i.next())
416            {
417                int type = i.currentSegment(coords);
418                switch (type) {
419                    case PathIterator.SEG_MOVETO:
420                        //newshape.moveTo(coords[0], coords[1]);
421                        //finalize the last Polyline and add it to the array
422                        if(ptsPoly.size()>0)
423                        {
424                            if(shapeType==ShapeInfo.SHAPE_TYPE_FILL)
425                            {
426                                if(ptsPoly.get(ptsPoly.size()-1).getX() != ptsPoly.get(0).getX() || 
427                                        ptsPoly.get(ptsPoly.size()-1).getY() != ptsPoly.get(0).getY() )
428                                {
429                                    Point2D pt2d=new Point2D.Double(ptsPoly.get(0).getX(), ptsPoly.get(0).getY());
430                                    ptsPoly.add(pt2d);
431                                }
432                            }                           
433                            if(ptsPoly.size()>1)
434                                polylines2.add(ptsPoly);
435                        }
436                        //start the ArrayList for next Polyline                       
437                        ptsPoly=new ArrayList();
438                        ptPoly=new Point2D.Double(coords[0], coords[1]);
439                        ptsPoly.add(ptPoly);
440                        break;
441                    case PathIterator.SEG_LINETO:
442                        //newshape.lineTo(coords[0], coords[1]);
443                        ptPoly=new Point2D.Double(coords[0],coords[1]);
444                        ptsPoly.add(ptPoly);                        
445                        break;
446                    case PathIterator.SEG_QUADTO: //quadTo was never used
447                        //no idea what to do with this
448                        //newshape.quadTo(coords[0], coords[1], coords[2], coords[3]);
449                        break;
450                    case PathIterator.SEG_CUBICTO:  //curveTo was used for some METOC's
451                        //no idea what to do with these
452                        //newshape.curveTo(coords[0], coords[1], coords[2], coords[3],
453                        //        coords[4], coords[5]);
454                        break;
455                    case PathIterator.SEG_CLOSE:    //closePath was never used
456                        //newshape.closePath();
457                        break;
458                }
459            }
460            if(ptsPoly.size()>1)
461            {
462                //add the last line to the ArrayList
463                //if it is a fill shape then the Google Earth linear ring requires the last point be added
464                if(shapeType==ShapeInfo.SHAPE_TYPE_FILL)
465                {
466                    if(ptsPoly.get(ptsPoly.size()-1).getX() != ptsPoly.get(0).getX() || 
467                            ptsPoly.get(ptsPoly.size()-1).getY() != ptsPoly.get(0).getY() )
468                    {
469                        Point2D pt2d=new Point2D.Double(ptsPoly.get(0).getX(), ptsPoly.get(0).getY());
470                        ptsPoly.add(pt2d);
471                    }
472                }
473                polylines2.add(ptsPoly);                
474            }
475        }
476        catch (Exception exc) {
477            ErrorLogger.LogException(_className, "createRenderableFromShape",
478                    new RendererException("Failed inside createRenderableFromShape", exc));
479        }
480        //return newshape;
481        return polylines2;
482    }   
483    /**
484     * Assumes a convex polygon for the clipping area.
485     * expand the polygon using pixels and a similar algorithm to what flash renderer does for DEPTH AREA
486     * @param pts clipping area to expand
487     * @param expand pixels expansion
488     * @return
489     */
490    protected static ArrayList<Point2D>expandPolygon(ArrayList<Point2D>pts,
491            double expand)
492    {
493        ArrayList<Point2D>lgPoly=null;
494        try
495        {
496            int j=0;
497            Point2D[]destPts=null;
498            boolean isClosed=false;
499            if(pts.get(pts.size()-1).getX()==pts.get(0).getX() && pts.get(pts.size()-1).getY()==pts.get(0).getY())
500            {
501                pts.remove(pts.size()-1);
502                isClosed=true;
503            }
504            ArrayList<POINT2>pts2=clsUtility.Points2DToPOINT2(pts);
505            POINT2 pt0=null,pt1=null,pt2=null,pt3=null;
506            double m=0,m1=0,b=0,b1=0;
507            ArrayList<Line2D>lineSegments=new ArrayList();
508            //n vertical segments
509            int n=pts2.size();
510            //for(j=0;j<pts2.size()-1;j++)
511            for(j=0;j<n-1;j++)
512            {
513                pt0=new POINT2(pts2.get(j));
514                pt1=new POINT2(pts2.get(j+1));
515                //no vertical segments
516                if(pt0.x==pt1.x)
517                {
518                    pt1.x+=1;                                
519                    pts2.set(j+1, pt1);
520                }
521            }
522            POINT2 ptn=pts2.get(pts2.size()-1);
523            pt0=new POINT2(pts2.get(0));
524            //last segment not vertical
525            if(ptn.x==pt0.x)
526            {
527                ptn.x+=1;
528                pts2.set(pts2.size()-1, ptn);
529            }
530            //close pts2
531            pts2.add(pt0);;
532            
533            //POINT2 ptOther=null;
534            //int quadrant=-1,otherQuadrant=-1;
535            Polygon poly=new Polygon();
536            n=pts2.size();
537            //for(j=0;j<pts2.size();j++)
538            for(j=0;j<n;j++)
539                poly.addPoint((int)pts2.get(j).x, (int)pts2.get(j).y);
540            
541            Line2D lineSegment=null;
542            POINT2 midPt=null;
543            //pts2 is closed
544            n=pts2.size();
545            //for(j=0;j<pts2.size()-1;j++)
546            for(j=0;j<n-1;j++)
547            {                
548                pt0=new POINT2(pts2.get(j));
549                pt1=new POINT2(pts2.get(j+1));                
550                m=(pt0.y-pt1.y)/(pt0.x-pt1.x);
551                //m1=-1/m;
552                if(Math.abs(m)<1)
553                {
554                    pt2=lineutility.ExtendDirectedLine(pt0, pt1, pt0, lineutility.extend_above, expand);
555                    pt3=lineutility.ExtendDirectedLine(pt0, pt1, pt1, lineutility.extend_above, expand);
556                    midPt=lineutility.MidPointDouble(pt2, pt3, 0);
557                    //we want the polygon to not contain the extended points
558                    if(poly.contains(midPt.x, midPt.y))
559                    {
560                        pt2=lineutility.ExtendDirectedLine(pt0, pt1, pt0, lineutility.extend_below, expand);
561                        pt3=lineutility.ExtendDirectedLine(pt0, pt1, pt1, lineutility.extend_below, expand);                        
562                    }                                        
563                }
564                else
565                {
566                    pt2=lineutility.ExtendDirectedLine(pt0, pt1, pt0, lineutility.extend_left, expand);
567                    pt3=lineutility.ExtendDirectedLine(pt0, pt1, pt1, lineutility.extend_left, expand);
568                    midPt=lineutility.MidPointDouble(pt2, pt3, 0);
569                    //we want the polygon to not contain the extended points
570                    if(poly.contains(midPt.x, midPt.y))
571                    {
572                        pt2=lineutility.ExtendDirectedLine(pt0, pt1, pt0, lineutility.extend_right, expand);
573                        pt3=lineutility.ExtendDirectedLine(pt0, pt1, pt1, lineutility.extend_right, expand);                        
574                    }                                                            
575                }
576                lineSegment=new Line2D.Double(pt2.x, pt2.y, pt3.x, pt3.y);
577                lineSegments.add(lineSegment);
578            }
579            //we will intersect the line segments to form an expanded polygon
580            ArrayList<POINT2> expandPts=new ArrayList();
581            Line2D thisLine=null,nextLine=null;
582            double x1=0,y1=0,x2=0,y2=0,x=0,y=0;
583            int t=lineSegments.size();
584            //for(j=0;j<lineSegments.size();j++)
585            for(j=0;j<t;j++)
586            {
587                thisLine=lineSegments.get(j);
588                x1=thisLine.getX1();
589                y1=thisLine.getY1();
590                x2=thisLine.getX2();
591                y2=thisLine.getY2();
592                //thisLine line equation
593                m=(y1-y2)/(x1-x2);
594                b=y1-m*x1;
595                
596                if(j==lineSegments.size()-1)
597                    nextLine=lineSegments.get(0);
598                else
599                    nextLine=lineSegments.get(j+1);
600                
601                x1=nextLine.getX1();
602                y1=nextLine.getY1();
603                x2=nextLine.getX2();
604                y2=nextLine.getY2();
605                //nextLine line equation
606                m1=(y1-y2)/(x1-x2);
607                b1=y1-m1*x1;
608                
609                //intersect thisLine with nextLine
610                if(m != m1)
611                {
612                    x=(b1-b)/(m-m1);    //cannot blow up
613                    y=(m*x+b);
614                }
615                else    //this should not happen
616                {
617                    x=thisLine.getX2();
618                    y=thisLine.getY2();
619                }
620                expandPts.add(new POINT2(x,y));
621            }           
622            lgPoly=new ArrayList();
623            t=expandPts.size();
624            //for(j=0;j<expandPts.size();j++)            
625            for(j=0;j<t;j++)            
626                lgPoly.add(new Point2D.Double(expandPts.get(j).x, expandPts.get(j).y));
627            
628            //close the aray if the original clipping array if applicable
629            if(isClosed)
630                lgPoly.add( new Point2D.Double( lgPoly.get(0).getX(),lgPoly.get(0).getY() ) );
631        }
632        catch (Exception exc) 
633        {
634            ErrorLogger.LogException(_className, "expandPolygon2",
635                    new RendererException("Failed inside expandPolygon2", exc));
636        }
637        return lgPoly;
638    }
639    /**
640     * use cheap algorithm to expand polygons, works best on regular 4+ sided convex polygons
641     * used primarily for expanding the original clipping areas. After clipping a tactical line against
642     * the expanded clipping area, the original clipping area can be used to drop the clip lines
643     * @param pts points to expand, usually a clipping area
644     * @param expandX X expansion factor, e.g 10% growth would be 1.1
645     * @param expandY Y expansion factor
646     * @return points for the expanded polygon
647     */
648    protected static ArrayList<Point2D>expandPolygon2(ArrayList<Point2D>pts,
649            double expandX, 
650            double expandY)
651    {
652        ArrayList<Point2D>lgPoly=null;
653        try
654        {
655//            AffineTransform at=new AffineTransform();
656//            at.setToIdentity();        
657            //get the center of the pts using an average
658            double avgX=0,avgY=0,totalX=0,totalY=0;
659            int j=0;
660            boolean isClosed=false;
661            //open the array, remove the last point if necessary
662            if(pts.get(pts.size()-1).getX()==pts.get(0).getX() && pts.get(pts.size()-1).getY()==pts.get(0).getY())
663            {
664                pts.remove(pts.size()-1);
665                isClosed=true;
666            }
667            //asumes open array
668            int n=pts.size();
669            //for(j=0;j<pts.size();j++)
670            for(j=0;j<n;j++)
671            {
672                totalX+=pts.get(j).getX();
673                totalY+=pts.get(j).getY();
674            }
675            avgX=totalX/pts.size();
676            avgY=totalY/pts.size();
677            Point2D.Double[]srcPts=new Point2D.Double[pts.size()];
678            //for(j=0;j<pts.size();j++)
679            n=pts.size();
680            for(j=0;j<n;j++)
681            {
682                srcPts[j]=new Point2D.Double(pts.get(j).getX(),pts.get(j).getY());
683            }
684            Point2D[]destPts=new Point2D[pts.size()];
685            //translate the points to crcumscribe 0,0
686//            at.translate(-avgY, -avgY);//ideally would be close to 0        
687//            at.transform(srcPts, 0, destPts, 0, srcPts.length);
688//            at.setToIdentity();
689            //scale the points by 10%
690//            at.scale(expandX, expandY);
691//            at.transform(destPts, 0, destPts, 0, destPts.length);
692//            at.setToIdentity();
693//            at.translate(avgY, avgY);
694//            at.transform(destPts, 0, destPts, 0, destPts.length);
695            lgPoly=new ArrayList<Point2D>();
696            int t=destPts.length;
697            //for(j=0;j<destPts.length;j++)
698            for(j=0;j<t;j++)
699            {
700                lgPoly.add(destPts[j]);
701            }
702            //close the aray if the original clipping array was closed
703            if(isClosed)
704                lgPoly.add(new Point2D.Double(destPts[0].getX(),destPts[0].getY()));
705        }
706        catch (Exception exc) {
707            ErrorLogger.LogException(_className, "expandPolygon",
708                    new RendererException("Failed inside expandPolygon", exc));
709        }
710        return lgPoly;
711    }
712    /**
713     * @deprecated 
714     * For tactical lines break up the arraylists into separate arraylists within the bounds.
715     * This was added for the Google Earth 3D map because small scales cut off and we want the clip lines
716     * to not be visible.
717     * @param ptsPoly
718     * @param clipBounds
719     * @return 
720     */
721    private static ArrayList<ArrayList<Point2D>> ptsPolyToPtsPoly(TGLight tg, ArrayList<ArrayList<Point2D>>ptsPoly,
722            Rectangle2D clipBounds)
723    {
724        ArrayList<ArrayList<Point2D>> ptsPoly2=null;
725        try
726        {
727            if(armyc2.c5isr.JavaTacticalRenderer.clsUtility.IsChange1Area(tg.get_LineType())==true)
728                return ptsPoly;
729            
730            int j=0,k=0;
731            ArrayList<Point2D>pts=null;
732            ArrayList<Point2D>addPts=null;
733            Point2D pt0=null;
734            Point2D pt1=null;
735            Line2D line=null;
736            ptsPoly2=new ArrayList();
737            int n=ptsPoly.size();
738            //for(j=0;j<ptsPoly.size();j++)
739            for(j=0;j<n;j++)
740            {
741                addPts=null;
742                pts=ptsPoly.get(j);
743                //find the first point inside the clipbounds
744                int t=pts.size();
745                //for(k=0;k<pts.size()-1;k++)
746                for(k=0;k<t-1;k++)
747                {
748                    pt0=pts.get(k);
749                    pt1=pts.get(k+1);
750                                        
751                    line=new Line2D.Double(pt0,pt1);
752                    //both points out of bounds, do not add points
753                    if(clipBounds.contains(pt0)==false && clipBounds.contains(pt1)==false)
754                    {                                                
755                        if(clipBounds.intersectsLine(line)==false)
756                        {
757                            addPts=null;
758                            continue;
759                        }
760                        else
761                        {
762                            if(addPts==null)
763                            {
764                                addPts=new ArrayList();
765                                addPts.add(pt0);
766                            }
767                            if(addPts.contains(pt0)==false)
768                                addPts.add(pt0);
769                            
770                            addPts.add(pt1);
771                            ptsPoly2.add(addPts);
772                            addPts=null;
773                        }
774                    }
775                    else if(clipBounds.contains(pt0)==false && clipBounds.contains(pt1)==true)
776                    {
777                        if(addPts == null)
778                        {
779                            addPts=new ArrayList();
780                            addPts.add(pt0);
781                        }
782                        if(addPts.contains(pt0)==false)
783                            addPts.add(pt0);
784                        
785                        addPts.add(pt1);
786                    }
787                    else if(clipBounds.contains(pt0)==true && clipBounds.contains(pt1)==true)
788                    {
789                        if(addPts==null)
790                        {
791                            addPts=new ArrayList();
792                            addPts.add(pt0);
793                        }
794                        if(addPts.contains(pt0)==false)
795                            addPts.add(pt0);
796                        
797                        addPts.add(pt1);                        
798                    }
799                    else if(clipBounds.contains(pt0)==true && clipBounds.contains(pt1)==false)
800                    {
801                        if(addPts==null)
802                        {
803                            addPts=new ArrayList();
804                            addPts.add(pt0);
805                        }
806                        if(addPts.contains(pt0)==false)
807                            addPts.add(pt0);
808                        //end the current polyline
809                        //and add it to the array list
810                        addPts.add(pt1);
811                        ptsPoly2.add(addPts);                                                
812                        addPts=null;                                                
813                    }                                            
814                }
815                //add the final array list
816                if(addPts != null && addPts.size()>0)
817                    ptsPoly2.add(addPts);
818            }
819        }
820        catch (Exception exc) {
821            ErrorLogger.LogException(_className, "ptsPolyToPtsPoly",
822                    new RendererException("Failed inside ptsPolyToPtsPoly", exc));
823        }
824        return ptsPoly2;
825    }
826    /**
827     * @deprecated 
828     * function to remove the clip lines from the polygon that was clipped
829     * @param ptsPoly the clipped points array
830     * @param clipBounds the clipping points
831     * @return 
832     */
833    private static ArrayList<ArrayList<Point2D>> ptsPolyToPtsPoly(TGLight tg, ArrayList<ArrayList<Point2D>>ptsPoly,
834            ArrayList<Point2D> clipBounds)//was rectangle2D clipBounds
835    {
836        ArrayList<ArrayList<Point2D>> ptsPoly2=null;
837        try
838        {
839            if(armyc2.c5isr.JavaTacticalRenderer.clsUtility.IsChange1Area(tg.get_LineType())==true)
840                return ptsPoly;
841            
842            int j=0,k=0;
843            ArrayList<Point2D>pts=null;
844            ArrayList<Point2D>addPts=null;
845            Point2D pt0=null;
846            Point2D pt1=null;
847            Line2D line=null;
848            ptsPoly2=new ArrayList();
849            Polygon clipPoly=new Polygon();
850            
851            //ArrayList<Point2D>ptsClipArea=null;
852            int n=clipBounds.size();
853            //for(j=0;j<clipBounds.size();j++)    
854            for(j=0;j<n;j++)    
855            {
856                clipPoly.addPoint((int)clipBounds.get(j).getX(), (int)clipBounds.get(j).getY());
857            }
858            n=ptsPoly.size();
859            //for(j=0;j<ptsPoly.size();j++)
860            for(j=0;j<n;j++)
861            {
862                addPts=null;
863                pts=ptsPoly.get(j);
864                //find the first point inside the clipbounds
865                int t=pts.size();
866                //for(k=0;k<pts.size()-1;k++)
867                for(k=0;k<t-1;k++)
868                {
869                    pt0=pts.get(k);
870                    pt1=pts.get(k+1);
871                    line=new Line2D.Double(pt0,pt1);
872                    //both points out of bounds, do not add points
873                    if(clipPoly.contains(pt0)==false && clipPoly.contains(pt1)==false)
874                    {                                                
875                        if(lineIntersectsClipArea(line,clipBounds)==false)
876                        {
877                            addPts=null;
878                            continue;
879                        }
880                        else
881                        {
882                            if(addPts==null)
883                            {
884                                addPts=new ArrayList();
885                                addPts.add(pt0);
886                            }
887                            if(addPts.contains(pt0)==false)
888                                addPts.add(pt0);
889                            
890                            addPts.add(pt1);
891                            ptsPoly2.add(addPts);
892                            addPts=null;
893                        }
894                    }
895                    else if(clipPoly.contains(pt0)==false && clipPoly.contains(pt1)==true)
896                    {
897                        if(addPts == null)
898                        {
899                            addPts=new ArrayList();
900                            addPts.add(pt0);
901                        }
902                        if(addPts.contains(pt0)==false)
903                            addPts.add(pt0);
904                        
905                        addPts.add(pt1);
906                    }
907                    else if(clipPoly.contains(pt0)==true && clipPoly.contains(pt1)==true)
908                    {
909                        if(addPts==null)
910                        {
911                            addPts=new ArrayList();
912                            addPts.add(pt0);
913                        }
914                        if(addPts.contains(pt0)==false)
915                            addPts.add(pt0);
916                        
917                        addPts.add(pt1);                        
918                    }
919                    else if(clipPoly.contains(pt0)==true && clipPoly.contains(pt1)==false)
920                    {
921                        if(addPts==null)
922                        {
923                            addPts=new ArrayList();
924                            addPts.add(pt0);
925                        }
926                        if(addPts.contains(pt0)==false)
927                            addPts.add(pt0);
928                        //end the current polyline
929                        //and add it to the array list
930                        addPts.add(pt1);
931                        ptsPoly2.add(addPts);                                                
932                        addPts=null;                                                
933                    }                                            
934                }
935                //add the final array list
936                if(addPts != null && addPts.size()>0)
937                    ptsPoly2.add(addPts);
938            }
939        }
940        catch (Exception exc) {
941            ErrorLogger.LogException(_className, "ptsPolyToPtsPoly",
942                    new RendererException("Failed inside ptsPolyToPtsPoly", exc));
943        }
944        return ptsPoly2;
945    }    
946    /**
947     * removes leading or trailing segments after the points were clipped
948     * @param tg
949     * @param clipArea 
950     */
951    protected static void removeTrailingPoints(TGLight tg, Object clipArea)
952    {
953        try
954        {
955            boolean isClosed= armyc2.c5isr.JavaTacticalRenderer.clsUtility.isClosedPolygon(tg.get_LineType());
956            if(isClosed)
957                return;
958            
959            Polygon poly=new Polygon();
960            Area area=null;
961            Rectangle2D clipBounds=null;
962            ArrayList<Point2D>clipPoints=null;
963            Point2D pt2d=null;
964            int j=0;
965            if(clipArea==null)
966                return;
967
968            if(clipArea.getClass().isAssignableFrom(Rectangle2D.Double.class))
969            {
970                clipBounds=(Rectangle2D.Double)clipArea;
971            }
972            else if(clipArea.getClass().isAssignableFrom(Rectangle.class))
973            {
974                //clipBounds=(Rectangle2D)clipArea;
975                Rectangle rectx=(Rectangle)clipArea;
976                clipBounds=new Rectangle2D.Double(rectx.x,rectx.y,rectx.width,rectx.height);
977            }
978            else if(clipArea.getClass().isAssignableFrom(ArrayList.class))
979            {
980                clipPoints=(ArrayList<Point2D>)clipArea;            
981            }
982            if(clipBounds != null)
983            {
984                clipPoints=new ArrayList<Point2D>();
985                clipPoints.add(new Point2D.Double(clipBounds.getX(),clipBounds.getY()));                
986                clipPoints.add(new Point2D.Double(clipBounds.getX()+clipBounds.getWidth(),clipBounds.getY()));                
987                clipPoints.add(new Point2D.Double(clipBounds.getX()+clipBounds.getWidth(),clipBounds.getY()+clipBounds.getHeight()));                
988                clipPoints.add(new Point2D.Double(clipBounds.getX(),clipBounds.getY()+clipBounds.getHeight()));                
989                clipPoints.add(new Point2D.Double(clipBounds.getX(),clipBounds.getY()));                
990            }   
991
992            Point2D ptLast=clipPoints.get(clipPoints.size()-1);
993            Point2D pt02d=clipPoints.get(0);
994            Point2D pt12d=null;
995            //close the area
996            if(pt02d.getX() != ptLast.getX() || pt02d.getY() != ptLast.getY())
997            {
998                clipPoints.add(new Point2D.Double(pt02d.getX(),pt02d.getY()));
999                //poly.addPoint((int)pt02d.getX(),(int)pt02d.getY());
1000            }
1001            //fill the polygon
1002            int n=clipPoints.size();
1003            //for(j=0;j<clipPoints.size();j++)
1004            for(j=0;j<n;j++)
1005            {
1006                pt02d=clipPoints.get(j);            
1007                poly.addPoint((int)pt02d.getX(), (int)pt02d.getY());
1008            }
1009            area=new Area(poly);
1010            Line2D line=null;
1011            POINT2 pt0=null,pt1=null;
1012            boolean intersects=false;
1013            int frontIndex=0,backIndex=tg.Pixels.size()-1;
1014            //breaks at the first leading segment that intersects the clip area
1015            n=tg.Pixels.size();
1016            //for(j=0;j<tg.Pixels.size()-1;j++)
1017            for(j=0;j<n-1;j++)
1018            {
1019               pt0=tg.Pixels.get(j);
1020               pt1=tg.Pixels.get(j+1);
1021               line=new Line2D.Double(pt0.x, pt0.y, pt1.x, pt1.y);
1022               intersects=lineIntersectsClipArea(line, clipPoints);
1023               if(intersects==true)
1024               {
1025                   frontIndex=j;
1026                   break;
1027               }
1028               else if(area.contains((int)pt0.x,(int)pt0.y) || area.contains((int)pt1.x,(int)pt1.y))
1029               {
1030                   frontIndex=j;
1031                   break;               
1032               }           
1033            }
1034            //breaks at the first trailing segment that intersects the clip area
1035            n=tg.Pixels.size();
1036            //for(j=tg.Pixels.size()-1;j>0;j--)
1037            for(j=n-1;j>0;j--)
1038            {
1039               pt0=tg.Pixels.get(j);
1040               pt1=tg.Pixels.get(j-1);
1041               line=new Line2D.Double(pt0.x, pt0.y, pt1.x, pt1.y);
1042               intersects=lineIntersectsClipArea(line, clipPoints);
1043               if(intersects==true)
1044               {
1045                   backIndex=j;
1046                   break;
1047               }
1048               else if(area.contains((int)pt0.x,(int)pt0.y) || area.contains((int)pt1.x,(int)pt1.y))
1049               {
1050                   backIndex=j;
1051                   break;               
1052               }           
1053            }
1054            ArrayList<POINT2>pts=new ArrayList();
1055            for(j=frontIndex;j<=backIndex;j++)
1056            {
1057                pt0=new POINT2(tg.Pixels.get(j));
1058                pts.add(pt0);
1059            }
1060            tg.Pixels=pts;           
1061        }
1062        catch(Exception exc)
1063        {
1064            ErrorLogger.LogException("clsRenderer" ,"removeTrailingPoints",
1065                    new RendererException("Failed inside removeTrailingPoints", exc));
1066        }
1067    }
1068/**
1069     * tests of a Line2D intersects a polygon by using line.intersectsLine on each segment of the polygon
1070     * assumes clip clipping area was parsed to shift points of vertical segments to make them not vertical
1071     * @param line a clipping line in the clipping polygon
1072     * @param clipPts array of clip points assumed to be closed
1073     * @return true if the line intersects the clip bounds
1074     */
1075    private static boolean lineIntersectsClipArea(Line2D line, 
1076            ArrayList<Point2D> clipPts)
1077    {
1078        boolean result=false;
1079        try
1080        {
1081            int j=0;           
1082            
1083            //test if polygon contains an end point
1084            Polygon poly=new Polygon();
1085            int n=clipPts.size();
1086            //for(j=0;j<clipPts.size();j++)            
1087            for(j=0;j<n;j++)            
1088                poly.addPoint((int)clipPts.get(j).getX(),(int)clipPts.get(j).getY());
1089            
1090            if(poly.contains(line.getX1(),line.getY1()))
1091                return true;
1092            if(poly.contains(line.getX2(),line.getY2()))
1093                return true;
1094            //end section
1095            
1096            Line2D currentSegment=null;
1097            n=clipPts.size();
1098            //for(j=0;j<clipPts.size()-1;j++)
1099            for(j=0;j<n-1;j++)
1100            {
1101                currentSegment=new Line2D.Double(clipPts.get(j).getX(),clipPts.get(j).getY(),clipPts.get(j+1).getX(),clipPts.get(j+1).getY());
1102                if(line.intersectsLine(currentSegment)==true)
1103                    return true;            
1104            }
1105            //if the clipPts are not closed then the above loop did not test the closing segment            
1106            Point2D pt0=clipPts.get(0);
1107            Point2D ptLast=clipPts.get(clipPts.size()-1);
1108            //int n=clipPts.size()-1;            
1109            if(pt0.getX()!=ptLast.getX() || pt0.getY()!=ptLast.getY())
1110            {
1111                //currentSegment=new Line2D.Double(clipPts.get(n).getX(),clipPts.get(n).getY(),clipPts.get(0).getX(),clipPts.get(0).getY());
1112                currentSegment=new Line2D.Double(ptLast.getX(),ptLast.getY(),pt0.getX(),pt0.getY());
1113                if(line.intersectsLine(currentSegment)==true)
1114                    return true;                            
1115            }
1116        }
1117        catch (Exception exc) {
1118            ErrorLogger.LogException(_className, "lineIntersectsClipArea",
1119                    new RendererException("Failed inside lineIntersectsClipArea", exc));
1120        }
1121        return result;
1122    }
1123    /**
1124     * returns true if segment data set for MSR, ASR, Boundary
1125     * @param tg
1126     * @return 
1127     */
1128    protected static boolean segmentColorsSet(TGLight tg)
1129    {
1130        try
1131        {
1132            switch(tg.get_LineType())
1133            {
1134                case TacticalLines.BOUNDARY:
1135                case TacticalLines.MSR:
1136                case TacticalLines.ASR:
1137                case TacticalLines.ROUTE:
1138                    break;
1139                default:
1140                    return false;
1141            }
1142            String strH=tg.get_H();
1143            if(strH==null || strH.isEmpty())
1144                return false;
1145            String[] strs=strH.split(",");
1146            if(strs.length>1)
1147                return true;            
1148        }
1149        catch(Exception exc)
1150        {
1151            ErrorLogger.LogException(_className, "segmentColorsSet",
1152                    new RendererException("Failed inside segmentColorsSet", exc));
1153        }
1154        return false;
1155    }
1156    /**
1157     * Use clipping rectangle or clip points to build a zoom factor if the client zoomed in after the initial render.
1158     * Multiply the geo segmenting interval by this factor.
1159     * @param rect
1160     * @param clipPoints
1161     * @param pixels
1162     * @return 
1163     */
1164    protected static double getZoomFactor(Rectangle2D rect, ArrayList<Point2D> clipPoints, ArrayList<POINT2>pixels)
1165    {
1166        double factor=-1;
1167        try
1168        {
1169            if(pixels==null || pixels.size()<2)
1170                return factor;
1171            if(clipPoints==null && rect==null)
1172                return factor;
1173            double maxLengthPixels=0, maxLengthClipArea=0,temp=0;
1174            int j=0;
1175            Point2D pt2d0=null,pt2d1=null;POINT2 pt0=null, pt1=null;
1176            for(j=0;j<pixels.size()-1;j++)
1177            {
1178               pt0=pixels.get(j);
1179               pt1=pixels.get(j+1);
1180               temp=lineutility.CalcDistanceDouble(pt0, pt1);
1181               if(temp>maxLengthPixels)
1182                   maxLengthPixels=temp;
1183            }
1184            temp=0;
1185            if(clipPoints != null)
1186            {
1187                for(j=0;j<clipPoints.size()-1;j++)
1188                {
1189                   pt2d0=clipPoints.get(j);
1190                   pt2d1=clipPoints.get(j+1);
1191                   pt0=new POINT2(pt2d0.getX(),pt2d0.getY());
1192                   pt1=new POINT2(pt2d1.getX(),pt2d1.getY());
1193                   temp=lineutility.CalcDistanceDouble(pt0, pt1);
1194                }
1195            }
1196            else if(rect != null)
1197            {
1198                temp=rect.getMaxX()-rect.getMinX();
1199                if(temp < rect.getMaxY()-rect.getMinY())
1200                    temp=rect.getMaxY()-rect.getMinY();
1201            }
1202            if(temp>maxLengthClipArea)
1203                maxLengthClipArea=temp;
1204            if(maxLengthPixels > 0 && maxLengthClipArea > 0)
1205                factor=maxLengthClipArea/maxLengthPixels;
1206        }
1207        catch(Exception exc)
1208        {
1209            ErrorLogger.LogException(_className, "getZoomFactor",
1210                    new RendererException("Failed inside getZoomFactor", exc));
1211        }
1212        return factor;
1213    }
1214    
1215}