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