001package armyc2.c5isr.web.render;
002
003import android.graphics.Bitmap;
004import android.graphics.Typeface;
005import android.util.Base64;
006import android.util.Log;
007
008import java.io.ByteArrayOutputStream;
009import java.util.ArrayList;
010import java.util.HashMap;
011import java.util.LinkedList;
012import java.util.List;
013import java.util.Map;
014import java.util.logging.Level;
015
016import armyc2.c5isr.JavaLineArray.POINT2;
017import armyc2.c5isr.JavaTacticalRenderer.TGLight;
018import armyc2.c5isr.JavaTacticalRenderer.mdlGeodesic;
019import armyc2.c5isr.RenderMultipoints.clsClipPolygon2;
020import armyc2.c5isr.RenderMultipoints.clsRenderer;
021import armyc2.c5isr.graphics2d.AffineTransform;
022import armyc2.c5isr.graphics2d.BasicStroke;
023import armyc2.c5isr.graphics2d.GeneralPath;
024import armyc2.c5isr.graphics2d.Point2D;
025import armyc2.c5isr.graphics2d.Rectangle;
026import armyc2.c5isr.graphics2d.Rectangle2D;
027import armyc2.c5isr.renderer.utilities.Color;
028import armyc2.c5isr.renderer.utilities.DistanceUnit;
029import armyc2.c5isr.renderer.utilities.DrawRules;
030import armyc2.c5isr.renderer.utilities.ErrorLogger;
031import armyc2.c5isr.renderer.utilities.GENCLookup;
032import armyc2.c5isr.renderer.utilities.IPointConversion;
033import armyc2.c5isr.renderer.utilities.MSInfo;
034import armyc2.c5isr.renderer.utilities.MSLookup;
035import armyc2.c5isr.renderer.utilities.MilStdAttributes;
036import armyc2.c5isr.renderer.utilities.MilStdSymbol;
037import armyc2.c5isr.renderer.utilities.Modifiers;
038import armyc2.c5isr.renderer.utilities.PointConversion;
039import armyc2.c5isr.renderer.utilities.RendererSettings;
040import armyc2.c5isr.renderer.utilities.RendererUtilities;
041import armyc2.c5isr.renderer.utilities.ShapeInfo;
042import armyc2.c5isr.renderer.utilities.SymbolID;
043import armyc2.c5isr.renderer.utilities.SymbolUtilities;
044import armyc2.c5isr.web.render.utilities.JavaRendererUtilities;
045import armyc2.c5isr.web.render.utilities.LineInfo;
046import armyc2.c5isr.web.render.utilities.SymbolInfo;
047import armyc2.c5isr.web.render.utilities.TextInfo;
048
049@SuppressWarnings({"unused", "rawtypes", "unchecked"})
050public class MultiPointHandler {
051
052
053    /**
054     * GE has the unusual distinction of being an application with coordinates
055     * outside its own extents. It appears to only be a problem when lines cross
056     * the IDL
057     *
058     * @param pts2d the client points
059     */
060    public static void NormalizeGECoordsToGEExtents(double leftLongitude,
061            double rightLongitude,
062            ArrayList<Point2D> pts2d) {
063        try {
064            int j = 0;
065            double x = 0, y = 0;
066            Point2D pt2d = null;
067            int n = pts2d.size();
068            //for (j = 0; j < pts2d.size(); j++) 
069            for (j = 0; j < n; j++) {
070                pt2d = pts2d.get(j);
071                x = pt2d.getX();
072                y = pt2d.getY();
073                while (x < leftLongitude) {
074                    x += 360;
075                }
076                while (x > rightLongitude) {
077                    x -= 360;
078                }
079
080                pt2d = new Point2D.Double(x, y);
081                pts2d.set(j, pt2d);
082            }
083        } catch (Exception exc) {
084        }
085    }
086
087    /**
088     * GE recognizes coordinates in the range of -180 to +180
089     *
090     * @param pt2d
091     * @return
092     */
093    protected static Point2D NormalizeCoordToGECoord(Point2D pt2d) {
094        Point2D ptGeo = null;
095        try {
096            double x = pt2d.getX(), y = pt2d.getY();
097            while (x < -180) {
098                x += 360;
099            }
100            while (x > 180) {
101                x -= 360;
102            }
103
104            ptGeo = new Point2D.Double(x, y);
105        } catch (Exception exc) {
106        }
107        return ptGeo;
108    }
109
110    /**
111     * We have to ensure the bounding rectangle at least includes the symbol or
112     * there are problems rendering, especially when the symbol crosses the IDL
113     *
114     * @param controlPoints the client symbol anchor points
115     * @param bbox the original bounding box
116     * @return the modified bounding box
117     */
118    private static String getBoundingRectangle(String controlPoints,
119            String bbox) {
120        String bbox2 = "";
121        try {
122            //first get the minimum bounding rect for the geo coords
123            Double left = 0.0;
124            Double right = 0.0;
125            Double top = 0.0;
126            Double bottom = 0.0;
127
128            String[] coordinates = controlPoints.split(" ");
129            int len = coordinates.length;
130            int i = 0;
131            left = Double.MAX_VALUE;
132            right = -Double.MAX_VALUE;
133            top = -Double.MAX_VALUE;
134            bottom = Double.MAX_VALUE;
135            for (i = 0; i < len; i++) {
136                String[] coordPair = coordinates[i].split(",");
137                Double latitude = Double.valueOf(coordPair[1].trim());
138                Double longitude = Double.valueOf(coordPair[0].trim());
139                if (longitude < left) {
140                    left = longitude;
141                }
142                if (longitude > right) {
143                    right = longitude;
144                }
145                if (latitude > top) {
146                    top = latitude;
147                }
148                if (latitude < bottom) {
149                    bottom = latitude;
150                }
151            }
152            bbox2 = left.toString() + "," + bottom.toString() + "," + right.toString() + "," + top.toString();
153        } catch (Exception ex) {
154            System.out.println("Failed to create bounding rectangle in MultiPointHandler.getBoundingRect");
155        }
156        return bbox2;
157    }
158
159    /**
160     * need to use the symbol to get the upper left control point in order to
161     * produce a valid PointConverter
162     *
163     * @param geoCoords
164     * @return
165     */
166    private static Point2D getControlPoint(ArrayList<Point2D> geoCoords) {
167        Point2D pt2d = null;
168        try {
169            double left = Double.MAX_VALUE;
170            double right = -Double.MAX_VALUE;
171            double top = -Double.MAX_VALUE;
172            double bottom = Double.MAX_VALUE;
173            Point2D ptTemp = null;
174            int n = geoCoords.size();
175            //for (int j = 0; j < geoCoords.size(); j++) 
176            for (int j = 0; j < n; j++) {
177                ptTemp = geoCoords.get(j);
178                if (ptTemp.getX() < left) {
179                    left = ptTemp.getX();
180                }
181                if (ptTemp.getX() > right) {
182                    right = ptTemp.getX();
183                }
184                if (ptTemp.getY() > top) {
185                    top = ptTemp.getY();
186                }
187                if (ptTemp.getY() < bottom) {
188                    bottom = ptTemp.getY();
189                }
190            }
191            pt2d = new Point2D.Double(left, top);
192        } catch (Exception ex) {
193            System.out.println("Failed to create control point in MultiPointHandler.getControlPoint");
194        }
195        return pt2d;
196    }
197
198    /**
199     * Assumes a reference in which the north pole is on top.
200     *
201     * @param geoCoords the geographic coordinates
202     * @return the upper left corner of the MBR containing the geographic
203     * coordinates
204     */
205    private static Point2D getGeoUL(ArrayList<Point2D> geoCoords) {
206        Point2D ptGeo = null;
207        try {
208            int j = 0;
209            Point2D pt = null;
210            double left = geoCoords.get(0).getX();
211            double top = geoCoords.get(0).getY();
212            double right = geoCoords.get(0).getX();
213            double bottom = geoCoords.get(0).getY();
214            int n = geoCoords.size();
215            //for (j = 1; j < geoCoords.size(); j++) 
216            for (j = 1; j < n; j++) {
217                pt = geoCoords.get(j);
218                if (pt.getX() < left) {
219                    left = pt.getX();
220                }
221                if (pt.getX() > right) {
222                    right = pt.getX();
223                }
224                if (pt.getY() > top) {
225                    top = pt.getY();
226                }
227                if (pt.getY() < bottom) {
228                    bottom = pt.getY();
229                }
230            }
231            //if geoCoords crosses the IDL
232            if (right - left > 180) {
233                //There must be at least one x value on either side of +/-180. Also, there is at least
234                //one positive value to the left of +/-180 and negative x value to the right of +/-180.
235                //We are using the orientation with the north pole on top so we can keep
236                //the existing value for top. Then the left value will be the least positive x value
237                //left = geoCoords.get(0).getX();
238                left = 180;
239                //for (j = 1; j < geoCoords.size(); j++) 
240                n = geoCoords.size();
241                for (j = 0; j < n; j++) {
242                    pt = geoCoords.get(j);
243                    if (pt.getX() > 0 && pt.getX() < left) {
244                        left = pt.getX();
245                    }
246                }
247            }
248            ptGeo = new Point2D.Double(left, top);
249        } catch (Exception ex) {
250            System.out.println("Failed to create control point in MultiPointHandler.getControlPoint");
251        }
252        return ptGeo;
253    }
254    private static String getBboxFromCoords(ArrayList<Point2D> geoCoords) {
255        //var ptGeo = null;
256        String bbox = null;
257        try {
258            int j = 0;
259            Point2D pt = null;
260            double left = geoCoords.get(0).getX();
261            double top = geoCoords.get(0).getY();
262            double right = geoCoords.get(0).getX();
263            double bottom = geoCoords.get(0).getY();
264            for (j = 1; j < geoCoords.size(); j++) {
265                pt = geoCoords.get(j);
266                if (pt.getX() < left) {
267                    left = pt.getX();
268                }
269                if (pt.getX() > right) {
270                    right = pt.getX();
271                }
272                if (pt.getY() > top) {
273                    top = pt.getY();
274                }
275                if (pt.getY() < bottom) {
276                    bottom = pt.getY();
277                }
278            }
279            //if geoCoords crosses the IDL
280            if (right - left > 180) {
281                //There must be at least one x value on either side of +/-180. Also, there is at least
282                //one positive value to the left of +/-180 and negative x value to the right of +/-180.
283                //We are using the orientation with the north pole on top so we can keep
284                //the existing value for top. Then the left value will be the least positive x value
285                //left = geoCoords[0].x;
286                left = 180;
287                right = -180;
288                for (j = 0; j < geoCoords.size(); j++) {
289                    pt = geoCoords.get(j);
290                    if (pt.getX() > 0 && pt.getX() < left) {
291                        left = pt.getX();
292                    }
293                    if (pt.getX() < 0 && pt.getX() > right) {
294                        right = pt.getX();
295                    }
296                }
297            }
298            //ptGeo = new Point2D(left, top);
299            bbox = Double.toString(left) + "," + Double.toString(bottom) + "," + Double.toString(right) + "," + Double.toString(top);
300        } catch (Exception ex) {
301            System.out.println("Failed to create control point in MultiPointHandler.getBboxFromCoords");
302        }
303        //return ptGeo;            
304        return bbox;
305    }
306
307    private static boolean crossesIDL(ArrayList<Point2D> geoCoords) {
308        boolean result = false;
309        Point2D pt2d = getControlPoint(geoCoords);
310        double left = pt2d.getX();
311        Point2D ptTemp = null;
312        int n = geoCoords.size();
313        //for (int j = 0; j < geoCoords.size(); j++) 
314        for (int j = 0; j < n; j++) {
315            ptTemp = geoCoords.get(j);
316            if (Math.abs(ptTemp.getX() - left) > 180) {
317                return true;
318            }
319        }
320        return result;
321    }
322
323    /**
324     * Checks if a symbol is one with decorated lines which puts a strain on
325     * google earth when rendering like FLOT. These complicated lines should be
326     * clipped when possible.
327     *
328     * @param symbolID
329     * @return
330     */
331    public static Boolean ShouldClipSymbol(String symbolID) {
332        //TODO: need to reevaluate this function to make sure we clip the right symbols.
333        int status = SymbolID.getStatus(symbolID);
334
335        if (SymbolUtilities.isTacticalGraphic(symbolID) && status == SymbolID.Status_Planned_Anticipated_Suspect) {
336            return true;
337        }
338
339        if (SymbolUtilities.isWeather(symbolID)) {
340            return true;
341        }
342
343        int id = Integer.valueOf(SymbolUtilities.getBasicSymbolID(symbolID));
344        //TODO: needs to be reworked
345        if(id == 25341100 || //Task Fix
346                id == 25260200 || //CFL
347                id == 25110100 || //Boundary
348                id == 25110200 || //Light Line (LL)
349                id == 25110300 || //Engineer Work Line (EWL)
350                id == 25140100 || //FLOT
351                id == 25140200 ||  //Line of contact is now just two flots, APP-6 still has it
352                id == 25151000 || //Fortified Area
353                id == 25151100 || //Limited Access Area
354                id == 25172000 || //Weapons Free Zone
355                id == 25151202 || //Battle Position/Prepared but not Occupied
356                id == 25151203 || //Strong Point
357                id == 25141200 || //Probable Line of Deployment (PLD)
358                id == 25270800 || //Mined Area
359                id == 25270801 || //Mined Area, Fenced
360                id == 25170100 || //Air Corridor
361                id == 25170200 || //Low Level Transit Route (LLTR)
362                id == 25170300 || //Minimum-Risk Route (MRR)
363                id == 25170400 || //Safe Lane (SL)
364                id == 25170500 || //Standard Use ARmy Aircraft Flight Route (SAAFR)
365                id == 25170600 || //Transit Corridors (TC)
366                id == 25170700 || //Special Corridor (SC)
367
368                id == 25270100 || //Obstacle Belt
369                id == 25270200 || //Obstacle Zone
370                id == 25270300 || //Obstacle Free Zone
371                id == 25270400 || //Obstacle Restricted Zone
372
373                id == 25290100 || //Obstacle Line
374                id == 25290201 || //Antitank Ditch - Under Construction
375                id == 25290202 || //Antitank Ditch - Completed
376                id == 25290203 || //Antitank Ditch Reinforced, with Antitank Mines
377                id == 25290204 || //Antitank Wall
378                id == 25290301 || //Unspecified
379                id == 25290302 || //Single Fence
380                id == 25290303 || //Double Fence
381                id == 25290304 || //Double Apron Fence
382                id == 25290305 || //Low Wire Fence
383                id == 25290306 || //High Wire Fence
384                id == 25290307 || //Single Concertina
385                id == 25290308 || //Double Strand Concertina
386                id == 25290309 || //Triple Strand Concertina
387
388                id == 25341100 || //Obstacles Effect Fix now Mission Tasks Fix
389                id == 25290400 || //Mine Cluster
390                id == 25282003 || //Aviation / Overhead Wire
391                id == 25270602 || //Bypass Difficult
392                id == 25271500 || //Ford Easy
393                id == 25271600 || //Ford Difficult
394
395                id == 25290900 || //Fortified Line
396
397                id == 25271700 || //Biological Contaminated Area
398                id == 25271800 || //Chemical Contaminated Area
399                id == 25271900 || //Nuclear Contaminated Area
400                id == 25272000 || //Radiological Contaminated Area
401
402                id == 25240301 || //No Fire Area (NFA) - Irregular
403                id == 25240302 || //No Fire Area (NFA) - Rectangular
404                id == 25240303 || //No Fire Area (NFA) - Circular
405
406
407                id == 25240701 || //Linear Target
408                id == 25240702 || //Linear Smoke Target
409                id == 25240703 || //Final Protective Fire (FPF)
410                id == 25151800 || //Encirclement
411
412                id == 25330300 || //MSR
413                id == 25330301 || //MSR / One Way Traffic
414                id == 25330302 || //MSR / Two Way Traffic
415                id == 25330303 || //MSR / Alternating Traffic
416
417                id == 25330400 || //ASR
418                id == 25330401 || //ASR / One Way Traffic
419                id == 25330402 || //ASR / Two Way Traffic
420                id == 25330403 || //AMSR / Alternating Traffic
421
422                id == 25151205 || //Retain
423                id == 25341500 || //Isolate
424
425                id == 25340600 || //counterattack.
426                id == 25340700 || //counterattack by fire.
427                //id == G*G*PA----****X || //AoA for Feint - appears to be gone in 2525D
428                id == 25271200 || //Blown Bridges Planned
429                id == 25271202 || //Blown Bridges Explosives, State of Readiness 1 (Safe)
430                id == 25341200) // Follow and Assume
431        {
432            return true;
433        } else {
434            return false;
435        }
436    }
437
438    /**
439     * Assumes bbox is of form left,bottom,right,top and it is currently only
440     * using the width to calculate a reasonable scale. If the original scale is
441     * within the max and min range it returns the original scale.
442     *
443     * @param bbox
444     * @param origScale
445     * @return
446     */
447    private static double getReasonableScale(String bbox, double origScale) {
448        try {
449            String[] bounds = bbox.split(",");
450            double left = Double.valueOf(bounds[0]);
451            double right = Double.valueOf(bounds[2]);
452            double top = Double.valueOf(bounds[3]);
453            double bottom = Double.valueOf(bounds[1]);
454
455            POINT2 ul = new POINT2(left, top);
456            POINT2 ur = new POINT2(right, top);
457
458            double widthInMeters;
459            if ((left == -180 && right == 180) || (left == 180 && right == -180))
460                widthInMeters = 40075017d / 2d; // Earth's circumference / 2
461            else
462                widthInMeters = mdlGeodesic.geodesic_distance(ul, ur, null, null);
463
464            double maxWidthInPixels = Math.max(RendererSettings.getInstance().getDeviceWidth(), RendererSettings.getInstance().getDeviceHeight());
465            double minScale = widthInMeters / (maxWidthInPixels / RendererSettings.getInstance().getDeviceDPI() / GeoPixelConversion.INCHES_PER_METER);
466            if (origScale < minScale) {
467                return minScale;
468            }
469
470            double minWidthInPixels = Math.min(RendererSettings.getInstance().getDeviceWidth(), RendererSettings.getInstance().getDeviceHeight()) / 2.0;
471            double maxScale = widthInMeters / (minWidthInPixels / RendererSettings.getInstance().getDeviceDPI() / GeoPixelConversion.INCHES_PER_METER);
472            if (origScale > maxScale) {
473                return maxScale;
474            }
475        } catch (NumberFormatException ignored) {
476        }
477        return origScale;
478    }
479
480    /**
481     *
482     * @param id - For the client to track the symbol, not related to rendering
483     * @param name - For the client to track the symbol, not related to rendering
484     * @param description - For the client to track the symbol, not related to rendering
485     * @param symbolCode
486     * @param controlPoints
487     * @param scale
488     * @param bbox
489     * @param symbolModifiers keyed using constants from
490     * Modifiers. Pass in comma delimited String for modifiers with multiple
491     * values like AM, AN &amp; X
492     * @param symbolAttributes keyed using constants from
493     * MilStdAttributes. pass in double[] for AM, AN and X; Strings for the
494     * rest.
495     * @param format
496     * @return
497     */
498    public static String RenderSymbol(String id,
499            String name,
500            String description,
501            String symbolCode,
502            String controlPoints,
503            Double scale,
504            String bbox,
505            Map<String,String> symbolModifiers,
506            Map<String,String> symbolAttributes,
507            int format)//,
508    {
509        //System.out.println("MultiPointHandler.RenderSymbol()");
510        boolean normalize = true;
511        //Double controlLat = 0.0;
512        //Double controlLong = 0.0;
513        //Double metPerPix = GeoPixelConversion.metersPerPixel(scale);
514        //String bbox2=getBoundingRectangle(controlPoints,bbox);
515        StringBuilder jsonOutput = new StringBuilder();
516        String jsonContent = "";
517
518        Rectangle rect = null;
519        String[] coordinates = controlPoints.split(" ");
520        TGLight tgl = new TGLight();
521        ArrayList<ShapeInfo> shapes = new ArrayList<ShapeInfo>();
522        ArrayList<ShapeInfo> modifiers = new ArrayList<ShapeInfo>();
523        //ArrayList<Point2D> pixels = new ArrayList<Point2D>();
524        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
525        int len = coordinates.length;
526        //diagnostic create geoCoords here
527        Point2D coordsUL=null;
528
529        String symbolIsValid = canRenderMultiPoint(symbolCode, symbolModifiers, len);
530        if (!symbolIsValid.equals("true")) {
531            String ErrorOutput = "";
532            ErrorOutput += ("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + " - ID: " + id + " - ");
533            ErrorOutput += symbolIsValid; //reason for error
534            ErrorOutput += ("\"}");
535            ErrorLogger.LogMessage("MultiPointHandler","RenderSymbol",symbolIsValid,Level.WARNING);
536            return ErrorOutput;
537        }
538
539        if (MSLookup.getInstance().getMSLInfo(symbolCode).getDrawRule() != DrawRules.AREA10) // AREA10 can support infinite points
540            len = Math.min(len, MSLookup.getInstance().getMSLInfo(symbolCode).getMaxPointCount());
541        for (int i = 0; i < len; i++) 
542        {
543            String[] coordPair = coordinates[i].split(",");
544            Double latitude = Double.valueOf(coordPair[1].trim()).doubleValue();
545            Double longitude = Double.valueOf(coordPair[0].trim()).doubleValue();
546            geoCoords.add(new Point2D.Double(longitude, latitude));
547        }
548        ArrayList<POINT2> tgPoints = null;
549        IPointConversion ipc = null;
550
551        //Deutch moved section 6-29-11
552        Double left = 0.0;
553        Double right = 0.0;
554        Double top = 0.0;
555        Double bottom = 0.0;
556        Point2D temp = null;
557        Point2D ptGeoUL = null;
558        int width = 0;
559        int height = 0;
560        int leftX = 0;
561        int topY = 0;
562        int bottomY = 0;
563        int rightX = 0;
564        int j = 0;
565        ArrayList<Point2D> bboxCoords = null;
566        if (bbox != null && bbox.equals("") == false) {
567            String[] bounds = null;
568            if (bbox.contains(" "))//trapezoid
569            {
570                bboxCoords = new ArrayList<Point2D>();
571                double x = 0;
572                double y = 0;
573                String[] coords = bbox.split(" ");
574                String[] arrCoord;
575                for (String coord : coords) {
576                    arrCoord = coord.split(",");
577                    x = Double.valueOf(arrCoord[0]);
578                    y = Double.valueOf(arrCoord[1]);
579                    bboxCoords.add(new Point2D.Double(x, y));
580                }
581                //use the upper left corner of the MBR containing geoCoords
582                //to set the converter
583                ptGeoUL = getGeoUL(bboxCoords);
584                left = ptGeoUL.getX();
585                top = ptGeoUL.getY();
586                String bbox2=getBboxFromCoords(bboxCoords);
587                scale = getReasonableScale(bbox2, scale);
588                ipc = new PointConverter(left, top, scale);
589                Point2D ptPixels = null;
590                Point2D ptGeo = null;
591                int n = bboxCoords.size();
592                //for (j = 0; j < bboxCoords.size(); j++) 
593                for (j = 0; j < n; j++) {
594                    ptGeo = bboxCoords.get(j);
595                    ptPixels = ipc.GeoToPixels(ptGeo);
596                    x = ptPixels.getX();
597                    y = ptPixels.getY();
598                    if (x < 20) {
599                        x = 20;
600                    }
601                    if (y < 20) {
602                        y = 20;
603                    }
604                    ptPixels.setLocation(x, y);
605                    //end section
606                    bboxCoords.set(j, (Point2D) ptPixels);
607                }
608            } else//rectangle
609            {
610                bounds = bbox.split(",");
611                left = Double.valueOf(bounds[0]);
612                right = Double.valueOf(bounds[2]);
613                top = Double.valueOf(bounds[3]);
614                bottom = Double.valueOf(bounds[1]);
615                scale = getReasonableScale(bbox, scale);
616                ipc = new PointConverter(left, top, scale);
617            }
618
619            Point2D pt2d = null;
620            if (bboxCoords == null) {
621                pt2d = new Point2D.Double(left, top);
622                temp = ipc.GeoToPixels(pt2d);
623
624                leftX = (int) temp.getX();
625                topY = (int) temp.getY();
626
627                pt2d = new Point2D.Double(right, bottom);
628                temp = ipc.GeoToPixels(pt2d);
629
630                bottomY = (int) temp.getY();
631                rightX = (int) temp.getX();
632                //diagnostic clipping does not work at large scales
633//                if(scale>10e6)
634//                {
635//                    //diagnostic replace above by using a new ipc based on the coordinates MBR
636//                    coordsUL=getGeoUL(geoCoords);
637//                    temp = ipc.GeoToPixels(coordsUL);
638//                    left=coordsUL.getX();
639//                    top=coordsUL.getY();
640//                    //shift the ipc to coordsUL origin so that conversions will be more accurate for large scales.
641//                    ipc = new PointConverter(left, top, scale);
642//                    //shift the rect to compenstate for the shifted ipc so that we can maintain the original clipping area.
643//                    leftX -= (int)temp.getX();
644//                    rightX -= (int)temp.getX();
645//                    topY -= (int)temp.getY();
646//                    bottomY -= (int)temp.getY();
647//                    //end diagnostic
648//                }
649                //end section
650
651                width = (int) Math.abs(rightX - leftX);
652                height = (int) Math.abs(bottomY - topY);
653
654                rect = new Rectangle(leftX, topY, width, height);
655            }
656        } else {
657            rect = null;
658        }
659        //end section
660
661//        for (int i = 0; i < len; i++) {
662//            String[] coordPair = coordinates[i].split(",");
663//            Double latitude = Double.valueOf(coordPair[1].trim());
664//            Double longitude = Double.valueOf(coordPair[0].trim());
665//            geoCoords.add(new Point2D.Double(longitude, latitude));
666//        }
667        if (ipc == null) {
668            Point2D ptCoordsUL = getGeoUL(geoCoords);
669            ipc = new PointConverter(ptCoordsUL.getX(), ptCoordsUL.getY(), scale);
670        }
671        //if (crossesIDL(geoCoords) == true) 
672//        if(Math.abs(right-left)>180)
673//        {
674//            normalize = true;
675//            ((PointConverter)ipc).set_normalize(true);
676//        } 
677//        else {
678//            normalize = false;
679//            ((PointConverter)ipc).set_normalize(false);
680//        }
681
682        //seems to work ok at world view
683//        if (normalize) {
684//            NormalizeGECoordsToGEExtents(0, 360, geoCoords);
685//        }
686
687        //M. Deutch 10-3-11
688        //must shift the rect pixels to synch with the new ipc
689        //the old ipc was in synch with the bbox, so rect x,y was always 0,0
690        //the new ipc synchs with the upper left of the geocoords so the boox is shifted
691        //and therefore the clipping rectangle must shift by the delta x,y between
692        //the upper left corner of the original bbox and the upper left corner of the geocoords
693        ArrayList<Point2D> geoCoords2 = new ArrayList<Point2D>();
694        geoCoords2.add(new Point2D.Double(left, top));
695        geoCoords2.add(new Point2D.Double(right, bottom));
696
697//        if (normalize) {
698//            NormalizeGECoordsToGEExtents(0, 360, geoCoords2);
699//        }
700
701        //disable clipping
702        if (ShouldClipSymbol(symbolCode) == false) 
703            if(crossesIDL(geoCoords)==false)
704            {
705                rect = null;
706                bboxCoords = null;
707            }   
708        
709        tgl.set_SymbolId(symbolCode);// "GFGPSLA---****X" AMBUSH symbol code
710        tgl.set_Pixels(null);
711
712        try {
713
714            //String fillColor = null;
715            MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
716
717            if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG){
718                // Use dash array and hatch pattern fill for SVG output
719                symbolAttributes.put(MilStdAttributes.UseDashArray, "true");
720                symbolAttributes.put(MilStdAttributes.UsePatternFill, "true");
721            }
722
723            if (symbolModifiers != null || symbolAttributes != null) {
724                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
725            } else {
726                mSymbol.setFillColor(null);
727            }
728
729            if (bboxCoords == null) {
730                clsRenderer.renderWithPolylines(mSymbol, ipc, rect);
731            } else {
732                clsRenderer.renderWithPolylines(mSymbol, ipc, bboxCoords);
733            }
734
735            shapes = mSymbol.getSymbolShapes();
736            modifiers = mSymbol.getModifierShapes();
737
738            if (format == WebRenderer.OUTPUT_FORMAT_JSON) {
739                jsonOutput.append("{\"type\":\"symbol\",");
740                jsonContent = JSONize(shapes, modifiers, ipc, true, normalize);
741                jsonOutput.append(jsonContent);
742                jsonOutput.append("}");
743            } else if (format == WebRenderer.OUTPUT_FORMAT_KML) {
744                Color textColor = mSymbol.getTextColor();
745                if(textColor==null)
746                    textColor=mSymbol.getLineColor();
747
748                jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor);
749                jsonOutput.append(jsonContent);
750            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOJSON)
751            {
752                jsonOutput.append("{\"type\":\"FeatureCollection\",\"features\":");
753                jsonContent = GeoJSONize(shapes, modifiers, ipc, normalize, mSymbol.getTextColor(), mSymbol.getTextBackgroundColor());
754                jsonOutput.append(jsonContent);
755
756                //moving meta data properties to the last feature with no coords as feature collection doesn't allow properties
757                jsonOutput.replace(jsonOutput.toString().length()-1,jsonOutput.toString().length(),"" );
758                jsonOutput.append(",{\"type\": \"Feature\",\"geometry\": { \"type\": \"Polygon\",\"coordinates\": [ ]}");
759
760                jsonOutput.append(",\"properties\":{\"id\":\"");
761                jsonOutput.append(id);
762                jsonOutput.append("\",\"name\":\"");
763                jsonOutput.append(name);
764                jsonOutput.append("\",\"description\":\"");
765                jsonOutput.append(description);
766                jsonOutput.append("\",\"symbolID\":\"");
767                jsonOutput.append(symbolCode);
768                jsonOutput.append("\",\"wasClipped\":\"");
769                jsonOutput.append(String.valueOf(mSymbol.get_WasClipped()));
770                //jsonOutput.append("\"}}");
771
772                jsonOutput.append("\"}}]}");
773            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG) {
774                String textColor = mSymbol.getTextColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextColor(), false) : "";
775                String backgroundColor = mSymbol.getTextBackgroundColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextBackgroundColor(), false) : "";
776                //returns an svg with a geoTL and geoBR value to use to place the canvas on the map
777                jsonContent = MultiPointHandlerSVG.GeoSVGize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, backgroundColor, mSymbol.get_WasClipped());
778                jsonOutput.append(jsonContent);
779            }
780        } catch (Exception exc) {
781            String st = JavaRendererUtilities.getStackTrace(exc);
782            jsonOutput = new StringBuilder();
783            jsonOutput.append("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + ": " + "- ");
784            jsonOutput.append(exc.getMessage() + " - ");
785            jsonOutput.append(st);
786            jsonOutput.append("\"}");
787
788            ErrorLogger.LogException("MultiPointHandler", "RenderSymbol", exc);
789        }
790
791        boolean debug = false;
792        if (debug == true) {
793            System.out.println("Symbol Code: " + symbolCode);
794            System.out.println("Scale: " + scale);
795            System.out.println("BBOX: " + bbox);
796            if (controlPoints != null) {
797                System.out.println("Geo Points: " + controlPoints);
798            }
799            if (tgl != null && tgl.get_Pixels() != null)//pixels != null
800            {
801                System.out.println("Pixel: " + tgl.get_Pixels().toString());
802            }
803            if (bbox != null) {
804                System.out.println("geo bounds: " + bbox);
805            }
806            if (rect != null) {
807                System.out.println("pixel bounds: " + rect.toString());
808            }
809            if (jsonOutput != null) {
810                System.out.println(jsonOutput.toString());
811            }
812        }
813
814        ErrorLogger.LogMessage("MultiPointHandler", "RenderSymbol()", "exit RenderSymbol", Level.FINER);
815        return jsonOutput.toString();
816
817    }
818
819    /**
820     *
821     * @param id
822     * @param name
823     * @param description
824     * @param symbolCode
825     * @param controlPoints
826     * @param scale
827     * @param bbox
828     * @param symbolModifiers
829     * @param symbolAttributes
830     * @return
831     */
832    public static MilStdSymbol RenderSymbolAsMilStdSymbol(String id,
833            String name,
834            String description,
835            String symbolCode,
836            String controlPoints,
837            Double scale,
838            String bbox,
839            Map<String,String> symbolModifiers,
840            Map<String,String> symbolAttributes)//,
841    //ArrayList<ShapeInfo>shapes)
842    {
843        MilStdSymbol mSymbol = null;
844        //System.out.println("MultiPointHandler.RenderSymbol()");
845        boolean normalize = true;
846        Double controlLat = 0.0;
847        Double controlLong = 0.0;
848        //String jsonContent = "";
849
850        Rectangle rect = null;
851
852        //for symbol & line fill
853        ArrayList<POINT2> tgPoints = null;
854
855        String[] coordinates = controlPoints.split(" ");
856        TGLight tgl = new TGLight();
857        ArrayList<ShapeInfo> shapes = null;//new ArrayList<ShapeInfo>();
858        ArrayList<ShapeInfo> modifiers = null;//new ArrayList<ShapeInfo>();
859        //ArrayList<Point2D> pixels = new ArrayList<Point2D>();
860        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
861        int len = coordinates.length;
862
863        IPointConversion ipc = null;
864
865        //Deutch moved section 6-29-11
866        Double left = 0.0;
867        Double right = 0.0;
868        Double top = 0.0;
869        Double bottom = 0.0;
870        Point2D temp = null;
871        Point2D ptGeoUL = null;
872        int width = 0;
873        int height = 0;
874        int leftX = 0;
875        int topY = 0;
876        int bottomY = 0;
877        int rightX = 0;
878        int j = 0;
879        ArrayList<Point2D> bboxCoords = null;
880        if (bbox != null && bbox.equals("") == false) {
881            String[] bounds = null;
882            if (bbox.contains(" "))//trapezoid
883            {
884                bboxCoords = new ArrayList<Point2D>();
885                double x = 0;
886                double y = 0;
887                String[] coords = bbox.split(" ");
888                String[] arrCoord;
889                for (String coord : coords) {
890                    arrCoord = coord.split(",");
891                    x = Double.valueOf(arrCoord[0]);
892                    y = Double.valueOf(arrCoord[1]);
893                    bboxCoords.add(new Point2D.Double(x, y));
894                }
895                //use the upper left corner of the MBR containing geoCoords
896                //to set the converter
897                ptGeoUL = getGeoUL(bboxCoords);
898                left = ptGeoUL.getX();
899                top = ptGeoUL.getY();
900                ipc = new PointConverter(left, top, scale);
901                Point2D ptPixels = null;
902                Point2D ptGeo = null;
903                int n = bboxCoords.size();
904                //for (j = 0; j < bboxCoords.size(); j++) 
905                for (j = 0; j < n; j++) {
906                    ptGeo = bboxCoords.get(j);
907                    ptPixels = ipc.GeoToPixels(ptGeo);
908                    x = ptPixels.getX();
909                    y = ptPixels.getY();
910                    if (x < 20) {
911                        x = 20;
912                    }
913                    if (y < 20) {
914                        y = 20;
915                    }
916                    ptPixels.setLocation(x, y);
917                    //end section
918                    bboxCoords.set(j, (Point2D) ptPixels);
919                }
920            } else//rectangle
921            {
922                bounds = bbox.split(",");
923                left = Double.valueOf(bounds[0]);
924                right = Double.valueOf(bounds[2]);
925                top = Double.valueOf(bounds[3]);
926                bottom = Double.valueOf(bounds[1]);
927                scale = getReasonableScale(bbox, scale);
928                ipc = new PointConverter(left, top, scale);
929            }
930
931            Point2D pt2d = null;
932            if (bboxCoords == null) {
933                pt2d = new Point2D.Double(left, top);
934                temp = ipc.GeoToPixels(pt2d);
935
936                leftX = (int) temp.getX();
937                topY = (int) temp.getY();
938
939                pt2d = new Point2D.Double(right, bottom);
940                temp = ipc.GeoToPixels(pt2d);
941
942                bottomY = (int) temp.getY();
943                rightX = (int) temp.getX();
944                //diagnostic clipping does not work for large scales
945//                if (scale > 10e6) {
946//                    //get widest point in the AOI
947//                    double midLat = 0;
948//                    if (bottom < 0 && top > 0) {
949//                        midLat = 0;
950//                    } else if (bottom < 0 && top < 0) {
951//                        midLat = top;
952//                    } else if (bottom > 0 && top > 0) {
953//                        midLat = bottom;
954//                    }
955//
956//                    temp = ipc.GeoToPixels(new Point2D.Double(right, midLat));
957//                    rightX = (int) temp.getX();
958//                }
959                //end section
960
961                width = (int) Math.abs(rightX - leftX);
962                height = (int) Math.abs(bottomY - topY);
963
964                if(width==0 || height==0)
965                    rect=null;
966                else
967                    rect = new Rectangle(leftX, topY, width, height);
968            }
969        } else {
970            rect = null;
971        }
972        //end section
973
974        //check for required points & parameters
975        String symbolIsValid = canRenderMultiPoint(symbolCode, symbolModifiers, len);
976        if (!symbolIsValid.equals("true")) {
977            ErrorLogger.LogMessage("MultiPointHandler", "RenderSymbolAsMilStdSymbol", symbolIsValid, Level.WARNING);
978            return mSymbol;
979        }
980
981        if (MSLookup.getInstance().getMSLInfo(symbolCode).getDrawRule() != DrawRules.AREA10) // AREA10 can support infinite points
982            len = Math.min(len, MSLookup.getInstance().getMSLInfo(symbolCode).getMaxPointCount());
983        for (int i = 0; i < len; i++) {
984            String[] coordPair = coordinates[i].split(",");
985            Double latitude = Double.valueOf(coordPair[1].trim());
986            Double longitude = Double.valueOf(coordPair[0].trim());
987            geoCoords.add(new Point2D.Double(longitude, latitude));
988        }
989        if (ipc == null) {
990            Point2D ptCoordsUL = getGeoUL(geoCoords);
991            ipc = new PointConverter(ptCoordsUL.getX(), ptCoordsUL.getY(), scale);
992        }
993        //if (crossesIDL(geoCoords) == true) 
994//        if(Math.abs(right-left)>180)
995//        {
996//            normalize = true;
997//            ((PointConverter)ipc).set_normalize(true);
998//        } 
999//        else {
1000//            normalize = false;
1001//            ((PointConverter)ipc).set_normalize(false);
1002//        }
1003
1004        //seems to work ok at world view
1005//        if (normalize) {
1006//            NormalizeGECoordsToGEExtents(0, 360, geoCoords);
1007//        }
1008
1009        //M. Deutch 10-3-11
1010        //must shift the rect pixels to synch with the new ipc
1011        //the old ipc was in synch with the bbox, so rect x,y was always 0,0
1012        //the new ipc synchs with the upper left of the geocoords so the boox is shifted
1013        //and therefore the clipping rectangle must shift by the delta x,y between
1014        //the upper left corner of the original bbox and the upper left corner of the geocoords
1015        ArrayList<Point2D> geoCoords2 = new ArrayList<Point2D>();
1016        geoCoords2.add(new Point2D.Double(left, top));
1017        geoCoords2.add(new Point2D.Double(right, bottom));
1018
1019//        if (normalize) {
1020//            NormalizeGECoordsToGEExtents(0, 360, geoCoords2);
1021//        }
1022
1023        //disable clipping
1024        if (ShouldClipSymbol(symbolCode) == false) 
1025            if(crossesIDL(geoCoords)==false)
1026            {
1027                rect = null;
1028                bboxCoords=null;
1029            }
1030        tgl.set_SymbolId(symbolCode);// "GFGPSLA---****X" AMBUSH symbol code
1031        tgl.set_Pixels(null);
1032        
1033        try {
1034
1035            String fillColor = null;
1036            mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
1037
1038//            mSymbol.setUseDashArray(true);
1039
1040            if (symbolModifiers != null || symbolAttributes != null) {
1041                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
1042            } else {
1043                mSymbol.setFillColor(null);
1044            }
1045
1046            if (mSymbol.getFillColor() != null) {
1047                Color fc = mSymbol.getFillColor();
1048                //fillColor = Integer.toHexString(fc.getRGB());                
1049                fillColor = Integer.toHexString(fc.toARGB());
1050            }
1051
1052            if (bboxCoords == null) {
1053                clsRenderer.renderWithPolylines(mSymbol, ipc, rect);
1054            } else {
1055                clsRenderer.renderWithPolylines(mSymbol, ipc, bboxCoords);
1056            }
1057            shapes = mSymbol.getSymbolShapes();
1058            modifiers = mSymbol.getModifierShapes();
1059
1060            //convert points////////////////////////////////////////////////////
1061            ArrayList<ArrayList<Point2D>> polylines = null;
1062            ArrayList<ArrayList<Point2D>> newPolylines = null;
1063            ArrayList<Point2D> newLine = null;
1064            for (ShapeInfo shape : shapes) {
1065                polylines = shape.getPolylines();
1066                //System.out.println("pixel polylines: " + String.valueOf(polylines));
1067                newPolylines = ConvertPolylinePixelsToCoords(polylines, ipc, normalize);
1068                shape.setPolylines(newPolylines);
1069            }
1070
1071            for (ShapeInfo label : modifiers) {
1072                Point2D pixelCoord = label.getModifierPosition();
1073                if (pixelCoord == null) {
1074                    pixelCoord = label.getGlyphPosition();
1075                }
1076                Point2D geoCoord = ipc.PixelsToGeo(pixelCoord);
1077
1078                if (normalize) {
1079                    geoCoord = NormalizeCoordToGECoord(geoCoord);
1080                }
1081
1082                double latitude = geoCoord.getY();
1083                double longitude = geoCoord.getX();
1084                label.setModifierPosition(new Point2D.Double(longitude, latitude));
1085
1086            }   
1087
1088            ////////////////////////////////////////////////////////////////////
1089            mSymbol.setModifierShapes(modifiers);
1090            mSymbol.setSymbolShapes(shapes);
1091
1092        } catch (Exception exc) {
1093            System.out.println(exc.getMessage());
1094            System.out.println("Symbol Code: " + symbolCode);
1095            exc.printStackTrace();
1096        }
1097
1098        boolean debug = false;
1099        if (debug == true) {
1100            System.out.println("Symbol Code: " + symbolCode);
1101            System.out.println("Scale: " + scale);
1102            System.out.println("BBOX: " + bbox);
1103            if (controlPoints != null) {
1104                System.out.println("Geo Points: " + controlPoints);
1105            }
1106            if (tgl != null && tgl.get_Pixels() != null)//pixels != null
1107            {
1108                //System.out.println("Pixel: " + pixels.toString());
1109                System.out.println("Pixel: " + tgl.get_Pixels().toString());
1110            }
1111            if (bbox != null) {
1112                System.out.println("geo bounds: " + bbox);
1113            }
1114            if (rect != null) {
1115                System.out.println("pixel bounds: " + rect.toString());
1116            }
1117        }
1118
1119        return mSymbol;
1120
1121    }
1122
1123    private static ArrayList<ArrayList<Point2D>> ConvertPolylinePixelsToCoords(ArrayList<ArrayList<Point2D>> polylines, IPointConversion ipc, Boolean normalize) {
1124        ArrayList<ArrayList<Point2D>> newPolylines = new ArrayList<ArrayList<Point2D>>();
1125
1126        double latitude = 0;
1127        double longitude = 0;
1128        ArrayList<Point2D> newLine = null;
1129        try {
1130            for (ArrayList<Point2D> line : polylines) {
1131                newLine = new ArrayList<Point2D>();
1132                for (Point2D pt : line) {
1133                    Point2D geoCoord = ipc.PixelsToGeo(pt);
1134
1135                    if (normalize) {
1136                        geoCoord = NormalizeCoordToGECoord(geoCoord);
1137                    }
1138
1139                    latitude = geoCoord.getY();
1140                    longitude = geoCoord.getX();
1141                    newLine.add(new Point2D.Double(longitude, latitude));
1142                }
1143                newPolylines.add(newLine);
1144            }
1145        } catch (Exception exc) {
1146            System.out.println(exc.getMessage());
1147            exc.printStackTrace();
1148        }
1149        return newPolylines;
1150    }
1151
1152    /**
1153     * Multipoint Rendering on flat 2D maps
1154     *
1155     * @param id A unique ID for the symbol. only used in KML currently
1156     * @param name
1157     * @param description
1158     * @param symbolCode
1159     * @param controlPoints
1160     * @param pixelWidth pixel dimensions of the viewable map area
1161     * @param pixelHeight pixel dimensions of the viewable map area
1162     * @param bbox The viewable area of the map. Passed in the format of a
1163     * string "lowerLeftX,lowerLeftY,upperRightX,upperRightY." example:
1164     * "-50.4,23.6,-42.2,24.2"
1165     * @param symbolModifiers Modifier with multiple values should be comma
1166     * delimited
1167     * @param symbolAttributes
1168     * @param format An enumeration: 0 for KML, 1 for JSON.
1169     * @return A JSON or KML string representation of the graphic.
1170     */
1171    public static String RenderSymbol2D(String id,
1172            String name,
1173            String description,
1174            String symbolCode,
1175            String controlPoints,
1176            int pixelWidth,
1177            int pixelHeight,
1178            String bbox,
1179            Map<String,String> symbolModifiers,
1180            Map<String,String> symbolAttributes,
1181            int format) {
1182        StringBuilder jsonOutput = new StringBuilder();
1183        String jsonContent = "";
1184
1185        Rectangle rect = null;
1186
1187        ArrayList<POINT2> tgPoints = null;
1188
1189        String[] coordinates = controlPoints.split(" ");
1190        TGLight tgl = new TGLight();
1191        ArrayList<ShapeInfo> shapes = new ArrayList<ShapeInfo>();
1192        ArrayList<ShapeInfo> modifiers = new ArrayList<ShapeInfo>();
1193        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
1194        int len = coordinates.length;
1195        IPointConversion ipc = null;
1196
1197        //check for required points & parameters
1198        String symbolIsValid = canRenderMultiPoint(symbolCode, symbolModifiers, len);
1199        if (!symbolIsValid.equals("true")) {
1200            String ErrorOutput = "";
1201            ErrorOutput += ("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + " - ID: " + id + " - ");
1202            ErrorOutput += symbolIsValid; //reason for error
1203            ErrorOutput += ("\"}");
1204            ErrorLogger.LogMessage("MultiPointHandler", "RenderSymbol2D", symbolIsValid, Level.WARNING);
1205            return ErrorOutput;
1206        }
1207
1208        Double left = 0.0;
1209        Double right = 0.0;
1210        Double top = 0.0;
1211        Double bottom = 0.0;
1212        if (bbox != null && bbox.equals("") == false) {
1213            String[] bounds = bbox.split(",");
1214
1215            left = Double.valueOf(bounds[0]).doubleValue();
1216            right = Double.valueOf(bounds[2]).doubleValue();
1217            top = Double.valueOf(bounds[3]).doubleValue();
1218            bottom = Double.valueOf(bounds[1]).doubleValue();
1219
1220            ipc = new PointConversion(pixelWidth, pixelHeight, top, left, bottom, right);
1221        } else {
1222            System.out.println("Bad bbox value: " + bbox);
1223            System.out.println("bbox is viewable area of the map.  Passed in the format of a string \"lowerLeftX,lowerLeftY,upperRightX,upperRightY.\" example: \"-50.4,23.6,-42.2,24.2\"");
1224            return "ERROR - Bad bbox value: " + bbox;
1225        }
1226        //end section
1227
1228        //get coordinates
1229        if (MSLookup.getInstance().getMSLInfo(symbolCode).getDrawRule() != DrawRules.AREA10) // AREA10 can support infinite points
1230            len = Math.min(len, MSLookup.getInstance().getMSLInfo(symbolCode).getMaxPointCount());
1231        for (int i = 0; i < len; i++) {
1232            String[] coordPair = coordinates[i].split(",");
1233            Double latitude = Double.valueOf(coordPair[1].trim()).doubleValue();
1234            Double longitude = Double.valueOf(coordPair[0].trim()).doubleValue();
1235            geoCoords.add(new Point2D.Double(longitude, latitude));
1236        }
1237
1238        try {
1239            MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
1240
1241            if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG){
1242                // Use dash array and hatch pattern fill for SVG output
1243                symbolAttributes.put(MilStdAttributes.UseDashArray, "true");
1244                symbolAttributes.put(MilStdAttributes.UsePatternFill, "true");
1245            }
1246
1247            if (symbolModifiers != null && symbolModifiers.equals("") == false) {
1248                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
1249            } else {
1250                mSymbol.setFillColor(null);
1251            }
1252
1253            //build clipping bounds
1254            Point2D temp = null;
1255            int leftX;
1256            int topY;
1257            int bottomY;
1258            int rightX;
1259            int width;
1260            int height;
1261            boolean normalize = false;
1262//            if(Math.abs(right-left)>180)
1263//            {
1264//                ((PointConversion)ipc).set_normalize(true);                
1265//                normalize=true;
1266//            }
1267//            else      
1268//            {
1269//                ((PointConversion)ipc).set_normalize(false);
1270//            }
1271            if (ShouldClipSymbol(symbolCode)  || crossesIDL(geoCoords)) 
1272            {
1273                Point2D lt=new Point2D.Double(left,top);
1274                //temp = ipc.GeoToPixels(new Point2D.Double(left, top));
1275                temp = ipc.GeoToPixels(lt);
1276                leftX = (int) temp.getX();
1277                topY = (int) temp.getY();
1278
1279                Point2D rb=new Point2D.Double(right,bottom);
1280                //temp = ipc.GeoToPixels(new Point2D.Double(right, bottom));
1281                temp = ipc.GeoToPixels(rb);
1282                bottomY = (int) temp.getY();
1283                rightX = (int) temp.getX();
1284                //////////////////
1285
1286                width = (int) Math.abs(rightX - leftX);
1287                height = (int) Math.abs(bottomY - topY);
1288
1289                rect = new Rectangle(leftX, topY, width, height);
1290            }
1291
1292            //new interface
1293            //IMultiPointRenderer mpr = MultiPointRenderer.getInstance();
1294            clsRenderer.renderWithPolylines(mSymbol, ipc, rect);
1295            shapes = mSymbol.getSymbolShapes();
1296            modifiers = mSymbol.getModifierShapes();
1297
1298            //boolean normalize = false;
1299
1300            if (format == WebRenderer.OUTPUT_FORMAT_JSON) {
1301                jsonOutput.append("{\"type\":\"symbol\",");
1302                //jsonContent = JSONize(shapes, modifiers, ipc, normalize);
1303                jsonOutput.append(jsonContent);
1304                jsonOutput.append("}");
1305            } else if (format == WebRenderer.OUTPUT_FORMAT_KML) {
1306                Color textColor = mSymbol.getTextColor();
1307                if(textColor==null)
1308                    textColor=mSymbol.getLineColor();
1309
1310                jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor);
1311                jsonOutput.append(jsonContent);
1312            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOJSON) {
1313                jsonOutput.append("{\"type\":\"FeatureCollection\",\"features\":");
1314                jsonContent = GeoJSONize(shapes, modifiers, ipc, normalize, mSymbol.getTextColor(), mSymbol.getTextBackgroundColor());
1315                jsonOutput.append(jsonContent);
1316
1317                //moving meta data properties to the last feature with no coords as feature collection doesn't allow properties
1318                jsonOutput.replace(jsonOutput.toString().length()-1,jsonOutput.toString().length(),"" );
1319                jsonOutput.append(",{\"type\": \"Feature\",\"geometry\": { \"type\": \"Polygon\",\"coordinates\": [ ]}");
1320
1321                jsonOutput.append(",\"properties\":{\"id\":\"");
1322                jsonOutput.append(id);
1323                jsonOutput.append("\",\"name\":\"");
1324                jsonOutput.append(name);
1325                jsonOutput.append("\",\"description\":\"");
1326                jsonOutput.append(description);
1327                jsonOutput.append("\",\"symbolID\":\"");
1328                jsonOutput.append(symbolCode);
1329                jsonOutput.append("\",\"wasClipped\":\"");
1330                jsonOutput.append(String.valueOf(mSymbol.get_WasClipped()));
1331                //jsonOutput.append("\"}}");
1332
1333                jsonOutput.append("\"}}]}");
1334
1335            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG) {
1336                String textColor = mSymbol.getTextColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextColor(), false) : "";
1337                String backgroundColor = mSymbol.getTextBackgroundColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextBackgroundColor(), false) : "";
1338                //returns an svg with a geoTL and geoBR value to use to place the canvas on the map
1339                jsonContent = MultiPointHandlerSVG.GeoSVGize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, backgroundColor, mSymbol.get_WasClipped());
1340                jsonOutput.append(jsonContent);
1341            }
1342        } catch (Exception exc) {
1343            jsonOutput = new StringBuilder();
1344            jsonOutput.append("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + ": " + "- ");
1345            jsonOutput.append(exc.getMessage() + " - ");
1346            jsonOutput.append(ErrorLogger.getStackTrace(exc));
1347            jsonOutput.append("\"}");
1348        }
1349
1350        boolean debug = false;
1351        if (debug == true) {
1352            System.out.println("Symbol Code: " + symbolCode);
1353            System.out.println("BBOX: " + bbox);
1354            if (controlPoints != null) {
1355                System.out.println("Geo Points: " + controlPoints);
1356            }
1357            if (tgl != null && tgl.get_Pixels() != null)//pixels != null
1358            {
1359                //System.out.println("Pixel: " + pixels.toString());
1360                System.out.println("Pixel: " + tgl.get_Pixels().toString());
1361            }
1362            if (bbox != null) {
1363                System.out.println("geo bounds: " + bbox);
1364            }
1365            if (rect != null) {
1366                System.out.println("pixel bounds: " + rect.toString());
1367            }
1368            if (jsonOutput != null) {
1369                System.out.println(jsonOutput.toString());
1370            }
1371        }
1372
1373        return jsonOutput.toString();
1374
1375    }
1376
1377    /**
1378     * For Mike Deutch testing
1379     *
1380     * @param id
1381     * @param name
1382     * @param description
1383     * @param symbolCode
1384     * @param controlPoints
1385     * @param pixelWidth
1386     * @param pixelHeight
1387     * @param bbox
1388     * @param symbolModifiers
1389     * @param shapes
1390     * @param modifiers
1391     * @param format
1392     * @return
1393     * @deprecated
1394     */
1395    public static String RenderSymbol2DX(String id,
1396            String name,
1397            String description,
1398            String symbolCode,
1399            String controlPoints,
1400            int pixelWidth,
1401            int pixelHeight,
1402            String bbox,
1403            Map<String,String> symbolModifiers,
1404            Map<String,String> symbolAttributes,
1405            ArrayList<ShapeInfo> shapes,
1406            ArrayList<ShapeInfo> modifiers,
1407            int format)//,
1408    //ArrayList<ShapeInfo>shapes)
1409    {
1410
1411        StringBuilder jsonOutput = new StringBuilder();
1412        String jsonContent = "";
1413
1414        Rectangle rect = null;
1415
1416        String[] coordinates = controlPoints.split(" ");
1417        TGLight tgl = new TGLight();
1418        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
1419        IPointConversion ipc = null;
1420
1421        Double left = 0.0;
1422        Double right = 0.0;
1423        Double top = 0.0;
1424        Double bottom = 0.0;
1425        if (bbox != null && bbox.equals("") == false) {
1426            String[] bounds = bbox.split(",");
1427
1428            left = Double.valueOf(bounds[0]).doubleValue();
1429            right = Double.valueOf(bounds[2]).doubleValue();
1430            top = Double.valueOf(bounds[3]).doubleValue();
1431            bottom = Double.valueOf(bounds[1]).doubleValue();
1432
1433            ipc = new PointConversion(pixelWidth, pixelHeight, top, left, bottom, right);
1434        } else {
1435            System.out.println("Bad bbox value: " + bbox);
1436            System.out.println("bbox is viewable area of the map.  Passed in the format of a string \"lowerLeftX,lowerLeftY,upperRightX,upperRightY.\" example: \"-50.4,23.6,-42.2,24.2\"");
1437            return "ERROR - Bad bbox value: " + bbox;
1438        }
1439        //end section
1440
1441        //get coordinates
1442        int len = coordinates.length;
1443        for (int i = 0; i < len; i++) {
1444            String[] coordPair = coordinates[i].split(",");
1445            Double latitude = Double.valueOf(coordPair[1].trim()).doubleValue();
1446            Double longitude = Double.valueOf(coordPair[0].trim()).doubleValue();
1447            geoCoords.add(new Point2D.Double(longitude, latitude));
1448        }
1449
1450        try {
1451            MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
1452
1453            if (symbolModifiers != null && symbolModifiers.equals("") == false) {
1454                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
1455            } else {
1456                mSymbol.setFillColor(null);
1457            }
1458
1459            clsRenderer.renderWithPolylines(mSymbol, ipc, rect);
1460            shapes = mSymbol.getSymbolShapes();
1461            modifiers = mSymbol.getModifierShapes();
1462
1463            boolean normalize = false;
1464
1465            if (format == WebRenderer.OUTPUT_FORMAT_JSON) {
1466                jsonOutput.append("{\"type\":\"symbol\",");
1467                jsonContent = JSONize(shapes, modifiers, ipc, false, normalize);
1468                jsonOutput.append(jsonContent);
1469                jsonOutput.append("}");
1470            }
1471
1472        } catch (Exception exc) {
1473            jsonOutput = new StringBuilder();
1474            jsonOutput.append("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + ": " + "- ");
1475            jsonOutput.append(exc.getMessage() + " - ");
1476            jsonOutput.append("\"}");
1477        }
1478
1479        boolean debug = true;
1480        if (debug == true) {
1481            System.out.println("Symbol Code: " + symbolCode);
1482            System.out.println("BBOX: " + bbox);
1483            if (controlPoints != null) {
1484                System.out.println("Geo Points: " + controlPoints);
1485            }
1486            if (tgl != null && tgl.get_Pixels() != null)//pixels != null
1487            {
1488                //System.out.println("Pixel: " + pixels.toString());
1489                System.out.println("Pixel: " + tgl.get_Pixels().toString());
1490            }
1491            if (bbox != null) {
1492                System.out.println("geo bounds: " + bbox);
1493            }
1494            if (rect != null) {
1495                System.out.println("pixel bounds: " + rect.toString());
1496            }
1497            if (jsonOutput != null) {
1498                System.out.println(jsonOutput.toString());
1499            }
1500        }
1501        return jsonOutput.toString();
1502
1503    }
1504
1505    private static SymbolInfo MilStdSymbolToSymbolInfo(MilStdSymbol symbol) {
1506        SymbolInfo si = null;
1507
1508        ArrayList<TextInfo> tiList = new ArrayList<TextInfo>();
1509        ArrayList<LineInfo> liList = new ArrayList<LineInfo>();
1510
1511        TextInfo tiTemp = null;
1512        LineInfo liTemp = null;
1513        ShapeInfo siTemp = null;
1514
1515        ArrayList<ShapeInfo> lines = symbol.getSymbolShapes();
1516        ArrayList<ShapeInfo> modifiers = symbol.getModifierShapes();
1517
1518        int lineCount = lines.size();
1519        int modifierCount = modifiers.size();
1520        for (int i = 0; i < lineCount; i++) {
1521            siTemp = lines.get(i);
1522            if (siTemp.getPolylines() != null) {
1523                liTemp = new LineInfo();
1524                liTemp.setFillColor(siTemp.getFillColor());
1525                liTemp.setLineColor(siTemp.getLineColor());
1526                liTemp.setPolylines(siTemp.getPolylines());
1527                liTemp.setStroke(siTemp.getStroke());
1528                liList.add(liTemp);
1529            }
1530        }
1531
1532        for (int j = 0; j < modifierCount; j++) {
1533            tiTemp = new TextInfo();
1534            siTemp = modifiers.get(j);
1535            if (siTemp.getModifierString() != null) {
1536                tiTemp.setModifierString(siTemp.getModifierString());
1537                tiTemp.setModifierStringPosition(siTemp.getModifierPosition());
1538                tiTemp.setModifierStringAngle(siTemp.getModifierAngle());
1539                tiList.add(tiTemp);
1540            }
1541        }
1542        si = new SymbolInfo(tiList, liList);
1543        return si;
1544    }
1545
1546    /**
1547     * Populates a symbol with the modifiers from a JSON string. This function
1548     * will overwrite any previously populated modifier data.
1549     *
1550     *
1551     *
1552     * @param symbol An existing MilStdSymbol
1553     * @return
1554     */
1555    private static boolean populateModifiers(Map<String,String> saModifiers, Map<String,String> saAttributes, MilStdSymbol symbol) {
1556        Map<String,String> modifiers = new HashMap<>();
1557        Map<String,String> attributes = new HashMap<>();
1558        saAttributes.putAll(attributes);
1559
1560        // Stores array graphic modifiers for MilStdSymbol;
1561        ArrayList<Double> altitudes = null;
1562        ArrayList<Double> azimuths = null;
1563        ArrayList<Double> distances = null;
1564
1565        // Stores colors for symbol.
1566        String fillColor = null;
1567        String lineColor = null;
1568        String textColor = null;
1569        String textBackgroundColor = null;
1570
1571        int lineWidth = 0;
1572        String altMode = null;
1573        boolean useDashArray = symbol.getUseDashArray();
1574        boolean usePatternFill = symbol.getUseFillPattern();
1575        int patternFillType = 0;
1576        boolean hideOptionalLabels = false;
1577        DistanceUnit distanceUnit = null;
1578        DistanceUnit altitudeUnit = null;
1579        int pixelSize = 100;
1580        boolean keepUnitRatio = true;
1581        double patternScale = RendererSettings.getInstance().getPatternScale();
1582
1583        try {
1584
1585            // The following attirubtes are labels.  All of them
1586            // are strings and can be added on the creation of the
1587            // MilStdSymbol by adding to a Map and passing in the
1588            // modifiers parameter.
1589            if (saModifiers != null) {
1590                if (saModifiers.containsKey(Modifiers.C_QUANTITY)) {
1591                    modifiers.put(Modifiers.C_QUANTITY, String.valueOf(saModifiers.get(Modifiers.C_QUANTITY)));
1592                }
1593
1594                if (saModifiers.containsKey(Modifiers.H_ADDITIONAL_INFO_1)) {
1595                    modifiers.put(Modifiers.H_ADDITIONAL_INFO_1, String.valueOf(saModifiers.get(Modifiers.H_ADDITIONAL_INFO_1)));
1596                }
1597
1598                if (saModifiers.containsKey(Modifiers.H1_ADDITIONAL_INFO_2)) {
1599                    modifiers.put(Modifiers.H1_ADDITIONAL_INFO_2, String.valueOf(saModifiers.get(Modifiers.H1_ADDITIONAL_INFO_2)));
1600                }
1601
1602                if (saModifiers.containsKey(Modifiers.H2_ADDITIONAL_INFO_3)) {
1603                    modifiers.put(Modifiers.H2_ADDITIONAL_INFO_3, String.valueOf(saModifiers.get(Modifiers.H2_ADDITIONAL_INFO_3)));
1604                }
1605
1606                if (saModifiers.containsKey(Modifiers.N_HOSTILE)) {
1607                    if (saModifiers.get(Modifiers.N_HOSTILE) == null) {
1608                        modifiers.put(Modifiers.N_HOSTILE, "");
1609                    } else {
1610                        modifiers.put(Modifiers.N_HOSTILE, String.valueOf(saModifiers.get(Modifiers.N_HOSTILE)));
1611                    }
1612                }
1613
1614                if (saModifiers.containsKey(Modifiers.Q_DIRECTION_OF_MOVEMENT)) {
1615                    modifiers.put(Modifiers.Q_DIRECTION_OF_MOVEMENT, String.valueOf(saModifiers.get(Modifiers.Q_DIRECTION_OF_MOVEMENT)));
1616                }
1617
1618                if (saModifiers.containsKey(Modifiers.T_UNIQUE_DESIGNATION_1)) {
1619                    modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, String.valueOf(saModifiers.get(Modifiers.T_UNIQUE_DESIGNATION_1)));
1620                }
1621
1622                if (saModifiers.containsKey(Modifiers.T1_UNIQUE_DESIGNATION_2)) {
1623                    modifiers.put(Modifiers.T1_UNIQUE_DESIGNATION_2, String.valueOf(saModifiers.get(Modifiers.T1_UNIQUE_DESIGNATION_2)));
1624                }
1625
1626                if (saModifiers.containsKey(Modifiers.V_EQUIP_TYPE)) {
1627                    modifiers.put(Modifiers.V_EQUIP_TYPE, String.valueOf(saModifiers.get(Modifiers.V_EQUIP_TYPE)));
1628                }
1629
1630                if (saModifiers.containsKey(Modifiers.AS_COUNTRY)) {
1631                    modifiers.put(Modifiers.AS_COUNTRY, String.valueOf(saModifiers.get(Modifiers.AS_COUNTRY)));
1632                } else if (SymbolID.getCountryCode(symbol.getSymbolID()) > 0 && !GENCLookup.getInstance().get3CharCode(SymbolID.getCountryCode(symbol.getSymbolID())).equals("")) {
1633                    modifiers.put(Modifiers.AS_COUNTRY, GENCLookup.getInstance().get3CharCode(SymbolID.getCountryCode(symbol.getSymbolID())));
1634                }
1635
1636                if (saModifiers.containsKey(Modifiers.AP_TARGET_NUMBER)) {
1637                    modifiers.put(Modifiers.AP_TARGET_NUMBER, String.valueOf(saModifiers.get(Modifiers.AP_TARGET_NUMBER)));
1638                }
1639
1640                if (saModifiers.containsKey(Modifiers.W_DTG_1)) {
1641                    modifiers.put(Modifiers.W_DTG_1, String.valueOf(saModifiers.get(Modifiers.W_DTG_1)));
1642                }
1643
1644                if (saModifiers.containsKey(Modifiers.W1_DTG_2)) {
1645                    modifiers.put(Modifiers.W1_DTG_2, String.valueOf(saModifiers.get(Modifiers.W1_DTG_2)));
1646                }
1647
1648                if (saModifiers.containsKey(Modifiers.Y_LOCATION)) {
1649                    modifiers.put(Modifiers.Y_LOCATION, String.valueOf(saModifiers.get(Modifiers.Y_LOCATION)));
1650                }
1651
1652                //Required multipoint modifier arrays
1653                if (saModifiers.containsKey(Modifiers.X_ALTITUDE_DEPTH)) {
1654                    altitudes = new ArrayList<Double>();
1655                    String[] arrAltitudes = String.valueOf(saModifiers.get(Modifiers.X_ALTITUDE_DEPTH)).split(",");
1656                    for (String x : arrAltitudes) {
1657                        if (x.equals("") != true) {
1658                            altitudes.add(Double.parseDouble(x));
1659                        }
1660                    }
1661                }
1662
1663                if (saModifiers.containsKey(Modifiers.AM_DISTANCE)) {
1664                    distances = new ArrayList<Double>();
1665                    String[] arrDistances = String.valueOf(saModifiers.get(Modifiers.AM_DISTANCE)).split(",");
1666                    for (String am : arrDistances) {
1667                        if (am.equals("") != true) {
1668                            distances.add(Double.parseDouble(am));
1669                        }
1670                    }
1671                }
1672
1673                if (saModifiers.containsKey(Modifiers.AN_AZIMUTH)) {
1674                    azimuths = new ArrayList<Double>();
1675                    String[] arrAzimuths = String.valueOf(saModifiers.get(Modifiers.AN_AZIMUTH)).split(",");;
1676                    for (String an : arrAzimuths) {
1677                        if (an.equals("") != true) {
1678                            azimuths.add(Double.parseDouble(an));
1679                        }
1680                    }
1681                }
1682            }
1683            if (saAttributes != null) {
1684                // These properties are ints, not labels, they are colors.//////////////////
1685                if (saAttributes.containsKey(MilStdAttributes.FillColor)) {
1686                    fillColor = (String) saAttributes.get(MilStdAttributes.FillColor);
1687                }
1688
1689                if (saAttributes.containsKey(MilStdAttributes.LineColor)) {
1690                    lineColor = (String) saAttributes.get(MilStdAttributes.LineColor);
1691                }
1692
1693                if (saAttributes.containsKey(MilStdAttributes.LineWidth)) {
1694                    lineWidth = Integer.parseInt(saAttributes.get(MilStdAttributes.LineWidth));
1695                }
1696                
1697                if (saAttributes.containsKey(MilStdAttributes.TextColor)) {
1698                    textColor = (String) saAttributes.get(MilStdAttributes.TextColor);
1699                }
1700                
1701                if (saAttributes.containsKey(MilStdAttributes.TextBackgroundColor)) {
1702                    textBackgroundColor = (String) saAttributes.get(MilStdAttributes.TextBackgroundColor);
1703                }
1704
1705                if (saAttributes.containsKey(MilStdAttributes.AltitudeMode)) {
1706                    altMode = saAttributes.get(MilStdAttributes.AltitudeMode);
1707                }
1708
1709                if (saAttributes.containsKey(MilStdAttributes.UseDashArray)) {
1710                    useDashArray = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.UseDashArray));
1711                }
1712
1713                if (saAttributes.containsKey(MilStdAttributes.UsePatternFill)) {
1714                    usePatternFill = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.UsePatternFill));
1715                }
1716
1717                if (saAttributes.containsKey(MilStdAttributes.PatternFillType)) {
1718                    patternFillType = Integer.parseInt((saAttributes.get(MilStdAttributes.PatternFillType)));
1719                }
1720
1721                if (saAttributes.containsKey(MilStdAttributes.HideOptionalLabels)) {
1722                    hideOptionalLabels = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.HideOptionalLabels));
1723                }
1724
1725                if(saAttributes.containsKey(MilStdAttributes.AltitudeUnits)) {
1726                    altitudeUnit = DistanceUnit.parse(saAttributes.get(MilStdAttributes.AltitudeUnits));
1727                }
1728
1729                if(saAttributes.containsKey(MilStdAttributes.DistanceUnits)) {
1730                    distanceUnit = DistanceUnit.parse(saAttributes.get(MilStdAttributes.DistanceUnits));
1731                }
1732
1733                if(saAttributes.containsKey(MilStdAttributes.PixelSize)) {
1734                    pixelSize = Integer.parseInt(saAttributes.get(MilStdAttributes.PixelSize));
1735                    symbol.setUnitSize(pixelSize);
1736                }
1737
1738                if (saAttributes.containsKey(MilStdAttributes.KeepUnitRatio)) {
1739                    keepUnitRatio = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.KeepUnitRatio));
1740                    symbol.setKeepUnitRatio(keepUnitRatio);
1741                }
1742
1743                if(saAttributes.containsKey(MilStdAttributes.PatternScale)) {
1744                    patternScale = Double.parseDouble(saAttributes.get(MilStdAttributes.PatternScale));
1745                }
1746            }
1747
1748            symbol.setModifierMap(modifiers);
1749
1750            if (fillColor != null && fillColor.equals("") == false) {
1751                symbol.setFillColor(RendererUtilities.getColorFromHexString(fillColor));
1752            } 
1753
1754            if (lineColor != null && lineColor.equals("") == false) {
1755                symbol.setLineColor(RendererUtilities.getColorFromHexString(lineColor));
1756                symbol.setTextColor(RendererUtilities.getColorFromHexString(lineColor));
1757            }
1758            else if(symbol.getLineColor()==null)
1759                symbol.setLineColor(Color.black);
1760
1761            if (lineWidth > 0) {
1762                symbol.setLineWidth(lineWidth);
1763            }
1764            
1765            if (textColor != null && textColor.equals("") == false) {
1766                symbol.setTextColor(RendererUtilities.getColorFromHexString(textColor));
1767            } else if(symbol.getTextColor()==null)
1768                symbol.setTextColor(Color.black);
1769                
1770            if (textBackgroundColor != null && textBackgroundColor.equals("") == false) {
1771                symbol.setTextBackgroundColor(RendererUtilities.getColorFromHexString(textBackgroundColor));
1772            }
1773
1774            if (altMode != null) {
1775                symbol.setAltitudeMode(altMode);
1776            }
1777
1778            symbol.setUseDashArray(useDashArray);
1779            symbol.setUseFillPattern(usePatternFill);
1780            symbol.setHideOptionalLabels(hideOptionalLabels);
1781            symbol.setAltitudeUnit(altitudeUnit);
1782            symbol.setDistanceUnit(distanceUnit);
1783            symbol.setPatternScale(patternScale);
1784
1785            // Check grpahic modifiers variables.  If we set earlier, populate
1786            // the fields, otherwise, ignore.
1787            if (altitudes != null) {
1788                symbol.setModifiers_AM_AN_X(Modifiers.X_ALTITUDE_DEPTH, altitudes);
1789            }
1790            if (distances != null) {
1791                symbol.setModifiers_AM_AN_X(Modifiers.AM_DISTANCE, distances);
1792            }
1793
1794            if (azimuths != null) {
1795                symbol.setModifiers_AM_AN_X(Modifiers.AN_AZIMUTH, azimuths);
1796            }
1797
1798            //Check if sector range fan has required min range
1799            if (SymbolUtilities.getBasicSymbolID(symbol.getSymbolID()).equals("25242200")) {
1800                if (symbol.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH) != null
1801                        && symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE) != null) {
1802                    int anCount = symbol.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH).size();
1803                    int amCount = symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE).size();
1804                    ArrayList<Double> am = null;
1805                    if (amCount < ((anCount / 2) + 1)) {
1806                        am = symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
1807                        if (am.get(0) != 0.0) {
1808                            am.add(0, 0.0);
1809                        }
1810                    }
1811                }
1812            }
1813        } catch (Exception exc2) {
1814            Log.e("MPH.populateModifiers", exc2.getMessage(), exc2);
1815        }
1816        return true;
1817
1818    }
1819
1820    private static String KMLize(String id, String name,
1821            String description,
1822            String symbolCode,
1823            ArrayList<ShapeInfo> shapes,
1824            ArrayList<ShapeInfo> modifiers,
1825            IPointConversion ipc,
1826            boolean normalize, Color textColor) {
1827
1828        java.lang.StringBuilder kml = new java.lang.StringBuilder();
1829
1830        ShapeInfo tempModifier = null;
1831
1832        String cdataStart = "<![CDATA[";
1833        String cdataEnd = "]]>";
1834
1835        int len = shapes.size();
1836        kml.append("<Folder id=\"" + id + "\">");
1837        kml.append("<name>" + cdataStart + name + cdataEnd + "</name>");
1838        kml.append("<visibility>1</visibility>");
1839        for (int i = 0; i < len; i++) {
1840
1841            String shapesToAdd = ShapeToKMLString(name, description, symbolCode, shapes.get(i), ipc, normalize);
1842            kml.append(shapesToAdd);
1843        }
1844
1845        int len2 = modifiers.size();
1846
1847        for (int j = 0; j < len2; j++) {
1848
1849            tempModifier = modifiers.get(j);
1850
1851            //if(geMap)//if using google earth
1852            //assume kml text is going to be centered
1853            //AdjustModifierPointToCenter(tempModifier);
1854
1855            String labelsToAdd = LabelToKMLString(tempModifier, ipc, normalize, textColor);
1856            kml.append(labelsToAdd);
1857        }
1858
1859        kml.append("</Folder>");
1860        return kml.toString();
1861    }
1862
1863    /**
1864     * 
1865     * @param shapes
1866     * @param modifiers
1867     * @param ipc
1868     * @param geMap
1869     * @param normalize
1870     * @return 
1871     * @deprecated Use GeoJSONize()
1872     */
1873    private static String JSONize(ArrayList<ShapeInfo> shapes, ArrayList<ShapeInfo> modifiers, IPointConversion ipc, Boolean geMap, boolean normalize) {
1874        String polygons = "";
1875        String lines = "";
1876        String labels = "";
1877        String jstr = "";
1878        ShapeInfo tempModifier = null;
1879
1880        int len = shapes.size();
1881        for (int i = 0; i < len; i++) {
1882            if (jstr.length() > 0) {
1883                jstr += ",";
1884            }
1885            String shapesToAdd = ShapeToJSONString(shapes.get(i), ipc, geMap, normalize);
1886            if (shapesToAdd.length() > 0) {
1887                if (shapesToAdd.startsWith("line", 2)) {
1888                    if (lines.length() > 0) {
1889                        lines += ",";
1890                    }
1891
1892                    lines += shapesToAdd;
1893                } else if (shapesToAdd.startsWith("polygon", 2)) {
1894                    if (polygons.length() > 0) {
1895                        polygons += ",";
1896                    }
1897
1898                    polygons += shapesToAdd;
1899                }
1900            }
1901        }
1902
1903        jstr += "\"polygons\": [" + polygons + "],"
1904                + "\"lines\": [" + lines + "],";
1905        int len2 = modifiers.size();
1906        labels = "";
1907        for (int j = 0; j < len2; j++) {
1908            tempModifier = modifiers.get(j);
1909            if (geMap) {
1910                AdjustModifierPointToCenter(tempModifier);
1911            }
1912            String labelsToAdd = LabelToJSONString(tempModifier, ipc, normalize);
1913            if (labelsToAdd.length() > 0) {
1914                if (labels.length() > 0) {
1915                    labels += ",";
1916                }
1917
1918                labels += labelsToAdd;
1919
1920            }
1921        }
1922        jstr += "\"labels\": [" + labels + "]";
1923        return jstr;
1924    }
1925
1926    private static Color getIdealTextBackgroundColor(Color fgColor) {
1927        //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "in function", Level.SEVERE);
1928        try {
1929            //an array of three elements containing the
1930            //hue, saturation, and brightness (in that order),
1931            //of the color with the indicated red, green, and blue components/
1932            float hsbvals[] = new float[3];
1933
1934            if (fgColor != null) {/*
1935                 Color.RGBtoHSB(fgColor.getRed(), fgColor.getGreen(), fgColor.getBlue(), hsbvals);
1936
1937                 if(hsbvals != null)
1938                 {
1939                 //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "length: " + String.valueOf(hsbvals.length));
1940                 //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "H: " + String.valueOf(hsbvals[0]) + " S: " + String.valueOf(hsbvals[1]) + " B: " + String.valueOf(hsbvals[2]),Level.SEVERE);
1941                 if(hsbvals[2] > 0.6)
1942                 return Color.BLACK;
1943                 else
1944                 return Color.WHITE;
1945                 }*/
1946
1947                int nThreshold = RendererSettings.getInstance().getTextBackgroundAutoColorThreshold();//160;
1948                int bgDelta = (int) ((fgColor.getRed() * 0.299) + (fgColor.getGreen() * 0.587) + (fgColor.getBlue() * 0.114));
1949                //ErrorLogger.LogMessage("bgDelta: " + String.valueOf(255-bgDelta));
1950                //if less than threshold, black, otherwise white.
1951                //return (255 - bgDelta < nThreshold) ? Color.BLACK : Color.WHITE;//new Color(0, 0, 0, fgColor.getAlpha())
1952                return (255 - bgDelta < nThreshold) ? new Color(0, 0, 0, fgColor.getAlpha()) : new Color(255, 255, 255, fgColor.getAlpha());
1953            }
1954        } catch (Exception exc) {
1955            ErrorLogger.LogException("SymbolDraw", "getIdealtextBGColor", exc);
1956        }
1957        return Color.WHITE;
1958    }
1959
1960    private static String LabelToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize, Color textColor, Color textBackgroundColor) {
1961
1962        StringBuilder JSONed = new StringBuilder();
1963        StringBuilder properties = new StringBuilder();
1964        StringBuilder geometry = new StringBuilder();
1965
1966        Color outlineColor = getIdealTextBackgroundColor(textColor);
1967        if(textBackgroundColor != null)
1968                outlineColor = textBackgroundColor;
1969
1970        //AffineTransform at = shapeInfo.getAffineTransform();
1971        //Point2D coord = (Point2D)new Point2D.Double(at.getTranslateX(), at.getTranslateY());
1972        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
1973        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
1974        Point2D geoCoord = ipc.PixelsToGeo(coord);
1975        //M. Deutch 9-27-11
1976        if (normalize) {
1977            geoCoord = NormalizeCoordToGECoord(geoCoord);
1978        }
1979        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
1980        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
1981        double angle = shapeInfo.getModifierAngle();
1982        coord.setLocation(longitude, latitude);
1983
1984        //diagnostic M. Deutch 10-18-11
1985        shapeInfo.setGlyphPosition(coord);
1986
1987        String text = shapeInfo.getModifierString();
1988        
1989        int justify=shapeInfo.getTextJustify();
1990        String strJustify="left";
1991        if(justify==0)
1992            strJustify="left";
1993        else if(justify==1)
1994            strJustify="center";
1995        else if(justify==2)
1996            strJustify="right";
1997
1998        
1999        RendererSettings RS = RendererSettings.getInstance();
2000
2001        if (text != null && text.equals("") == false) {
2002
2003            JSONed.append("{\"type\":\"Feature\",\"properties\":{\"label\":\"");
2004            JSONed.append(text);
2005            JSONed.append("\",\"pointRadius\":0,\"fontColor\":\"");
2006            JSONed.append(RendererUtilities.colorToHexString(textColor, false));
2007            JSONed.append("\",\"fontSize\":\"");
2008            JSONed.append(String.valueOf(RS.getMPLabelFontSize()) + "pt\"");
2009            JSONed.append(",\"fontFamily\":\"");
2010            JSONed.append(RS.getMPLabelFontName());
2011            JSONed.append(", sans-serif");
2012
2013            if (RS.getMPLabelFontType() == Typeface.BOLD) {
2014                JSONed.append("\",\"fontWeight\":\"bold\"");
2015            } else {
2016                JSONed.append("\",\"fontWeight\":\"normal\"");
2017            }
2018
2019            //JSONed.append(",\"labelAlign\":\"lm\"");
2020            JSONed.append(",\"labelAlign\":\"");
2021            JSONed.append(strJustify);
2022            JSONed.append("\",\"labelBaseline\":\"alphabetic\"");
2023            JSONed.append(",\"labelXOffset\":0");
2024            JSONed.append(",\"labelYOffset\":0");
2025            JSONed.append(",\"labelOutlineColor\":\"");
2026            JSONed.append(RendererUtilities.colorToHexString(outlineColor, false));
2027            JSONed.append("\",\"labelOutlineWidth\":");
2028            JSONed.append("4");
2029            JSONed.append(",\"rotation\":");
2030            JSONed.append(angle);
2031            JSONed.append(",\"angle\":");
2032            JSONed.append(angle);
2033            JSONed.append("},");
2034
2035            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
2036            JSONed.append(longitude);
2037            JSONed.append(",");
2038            JSONed.append(latitude);
2039            JSONed.append("]");
2040            JSONed.append("}}");
2041
2042        } else {
2043            return "";
2044        }
2045
2046        return JSONed.toString();
2047    }
2048
2049    private static String ShapeToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2050        StringBuilder JSONed = new StringBuilder();
2051        StringBuilder properties = new StringBuilder();
2052        StringBuilder geometry = new StringBuilder();
2053        String geometryType = null;
2054        String sda = null;
2055        /*
2056         NOTE: Google Earth / KML colors are backwards.
2057         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2058         * */
2059        Color lineColor = shapeInfo.getLineColor();
2060        Color fillColor = shapeInfo.getFillColor();
2061
2062        if (shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_FILL || fillColor != null || shapeInfo.getPatternFillImage() != null) {
2063            geometryType = "\"Polygon\"";
2064        } else //if(shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_POLYLINE)
2065        {
2066            geometryType = "\"MultiLineString\"";
2067        }
2068
2069        BasicStroke stroke = null;
2070        stroke = shapeInfo.getStroke();
2071        int lineWidth = 4;
2072
2073        if (stroke != null) {
2074            lineWidth = (int) stroke.getLineWidth();
2075            //lineWidth++;
2076            //System.out.println("lineWidth: " + String.valueOf(lineWidth));
2077        }
2078
2079        //generate JSON properties for feature
2080        properties.append("\"properties\":{");
2081        properties.append("\"label\":\"\",");
2082        if (lineColor != null) {
2083            properties.append("\"strokeColor\":\"" + RendererUtilities.colorToHexString(lineColor, false) + "\",");
2084            properties.append("\"lineOpacity\":" + String.valueOf(lineColor.getAlpha() / 255f) + ",");
2085        }
2086        if (fillColor != null) {
2087            properties.append("\"fillColor\":\"" + RendererUtilities.colorToHexString(fillColor, false) + "\",");
2088            properties.append("\"fillOpacity\":" + String.valueOf(fillColor.getAlpha() / 255f) + ",");
2089        }
2090        if (shapeInfo.getPatternFillImage() != null) {
2091            properties.append("\"fillPattern\":\"" + bitmapToString(shapeInfo.getPatternFillImage()) + "\",");
2092        }
2093        if(stroke.getDashArray() != null)
2094        {
2095            float[] arrSDA = stroke.getDashArray();
2096            sda = "[";
2097            sda += String.valueOf(arrSDA[0]);
2098            if(arrSDA.length > 1)
2099            {
2100                for(int i = 1; i < arrSDA.length; i++)
2101                {
2102                    sda = sda + ", " + String.valueOf(arrSDA[i]);
2103                }
2104            }
2105            sda += "]";
2106            sda = "\"strokeDasharray\":" + sda + ",";
2107            properties.append(sda);
2108        }
2109
2110        int lineCap = stroke.getEndCap();
2111        properties.append("\"lineCap\":" + lineCap + ",");
2112
2113        String strokeWidth = String.valueOf(lineWidth);
2114        properties.append("\"strokeWidth\":" + strokeWidth + ",");
2115        properties.append("\"strokeWeight\":" + strokeWidth + "");
2116        properties.append("},");
2117
2118
2119        properties.append("\"style\":{");
2120        if (lineColor != null) {
2121            properties.append("\"stroke\":\"" + RendererUtilities.colorToHexString(lineColor, false) + "\",");
2122            properties.append("\"line-opacity\":" + String.valueOf(lineColor.getAlpha() / 255f) + ",");
2123        }
2124        if (fillColor != null) {
2125            properties.append("\"fill\":\"" + RendererUtilities.colorToHexString(fillColor, false) + "\",");
2126            properties.append("\"fill-opacity\":" + String.valueOf(fillColor.getAlpha() / 255f) + ",");
2127        }
2128        if(stroke.getDashArray() != null)
2129        {
2130            float[] da = stroke.getDashArray();
2131            sda = String.valueOf(da[0]);
2132            if(da.length > 1)
2133            {
2134                for(int i = 1; i < da.length; i++)
2135                {
2136                    sda = sda + " " + String.valueOf(da[i]);
2137                }
2138            }
2139            sda = "\"stroke-dasharray\":\"" + sda + "\",";
2140            properties.append(sda);
2141            sda = null;
2142        }
2143
2144        if(lineCap == BasicStroke.CAP_SQUARE)
2145            properties.append("\"stroke-linecap\":\"square\",");
2146        else if(lineCap == BasicStroke.CAP_ROUND)
2147            properties.append("\"stroke-linecap\":\"round\",");
2148        else if(lineCap == BasicStroke.CAP_BUTT)
2149            properties.append("\"stroke-linecap\":\"butt\",");
2150
2151        strokeWidth = String.valueOf(lineWidth);
2152        properties.append("\"stroke-width\":" + strokeWidth);
2153        properties.append("}");
2154
2155
2156        //generate JSON geometry for feature
2157        geometry.append("\"geometry\":{\"type\":");
2158        geometry.append(geometryType);
2159        geometry.append(",\"coordinates\":[");
2160
2161        ArrayList shapesArray = shapeInfo.getPolylines();
2162
2163        for (int i = 0; i < shapesArray.size(); i++) {
2164            ArrayList pointList = (ArrayList) shapesArray.get(i);
2165
2166            normalize = normalizePoints(pointList, ipc);
2167
2168            geometry.append("[");
2169
2170            //System.out.println("Pixel Coords:");
2171            for (int j = 0; j < pointList.size(); j++) {
2172                Point2D coord = (Point2D) pointList.get(j);
2173                Point2D geoCoord = ipc.PixelsToGeo(coord);
2174                //M. Deutch 9-27-11
2175                if (normalize) {
2176                    geoCoord = NormalizeCoordToGECoord(geoCoord);
2177                }
2178                double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2179                double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2180
2181                //fix for fill crossing DTL
2182                if (normalize && fillColor != null) {
2183                    if (longitude > 0) {
2184                        longitude -= 360;
2185                    }
2186                }
2187
2188                //diagnostic M. Deutch 10-18-11
2189                //set the point as geo so that the 
2190                //coord.setLocation(longitude, latitude);
2191                coord = new Point2D.Double(longitude, latitude);
2192                pointList.set(j, coord);
2193                //end section
2194
2195                geometry.append("[");
2196                geometry.append(longitude);
2197                geometry.append(",");
2198                geometry.append(latitude);
2199                geometry.append("]");
2200
2201                if (j < (pointList.size() - 1)) {
2202                    geometry.append(",");
2203                }
2204            }
2205
2206            geometry.append("]");
2207
2208            if (i < (shapesArray.size() - 1)) {
2209                geometry.append(",");
2210            }
2211        }
2212        geometry.append("]}");
2213
2214        JSONed.append("{\"type\":\"Feature\",");
2215        JSONed.append(properties.toString());
2216        JSONed.append(",");
2217        JSONed.append(geometry.toString());
2218        JSONed.append("}");
2219
2220        return JSONed.toString();
2221    }
2222
2223    private static String ImageToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2224
2225        StringBuilder JSONed = new StringBuilder();
2226        StringBuilder properties = new StringBuilder();
2227        StringBuilder geometry = new StringBuilder();
2228
2229        //AffineTransform at = shapeInfo.getAffineTransform();
2230        //Point2D coord = (Point2D)new Point2D.Double(at.getTranslateX(), at.getTranslateY());
2231        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2232        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
2233        Point2D geoCoord = ipc.PixelsToGeo(coord);
2234        //M. Deutch 9-27-11
2235        if (normalize) {
2236            geoCoord = NormalizeCoordToGECoord(geoCoord);
2237        }
2238        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2239        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2240        double angle = shapeInfo.getModifierAngle();
2241        coord.setLocation(longitude, latitude);
2242
2243        //diagnostic M. Deutch 10-18-11
2244        shapeInfo.setGlyphPosition(coord);
2245
2246        Bitmap image = shapeInfo.getModifierImage();
2247
2248        RendererSettings RS = RendererSettings.getInstance();
2249
2250        if (image != null) {
2251
2252            JSONed.append("{\"type\":\"Feature\",\"properties\":{\"image\":\"");
2253            JSONed.append(bitmapToString(image));
2254            JSONed.append("\",\"rotation\":");
2255            JSONed.append(angle);
2256            JSONed.append(",\"angle\":");
2257            JSONed.append(angle);
2258            JSONed.append("},");
2259            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
2260            JSONed.append(longitude);
2261            JSONed.append(",");
2262            JSONed.append(latitude);
2263            JSONed.append("]");
2264            JSONed.append("}}");
2265
2266        } else {
2267            return "";
2268        }
2269
2270        return JSONed.toString();
2271    }
2272
2273    protected static String bitmapToString(Bitmap bitmap) {
2274        final int COMPRESSION_QUALITY = 100;
2275        String encodedImage;
2276        ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream();
2277        bitmap.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY,
2278                byteArrayBitmapStream);
2279        byte[] b = byteArrayBitmapStream.toByteArray();
2280        encodedImage = Base64.encodeToString(b, Base64.DEFAULT);
2281        return "data:image/png;base64," + encodedImage;
2282    }
2283
2284    private static String GeoJSONize(ArrayList<ShapeInfo> shapes, ArrayList<ShapeInfo> modifiers, IPointConversion ipc, boolean normalize, Color textColor, Color textBackgroundColor) {
2285
2286        String jstr = "";
2287        ShapeInfo tempModifier = null;
2288        StringBuilder fc = new StringBuilder();//JSON feature collection
2289
2290        fc.append("[");
2291
2292        int len = shapes.size();
2293        for (int i = 0; i < len; i++) {
2294
2295            String shapesToAdd = ShapeToGeoJSONString(shapes.get(i), ipc, normalize);
2296            if (shapesToAdd.length() > 0) {
2297                fc.append(shapesToAdd);
2298            }
2299            if (i < len - 1) {
2300                fc.append(",");
2301            }
2302        }
2303
2304        int len2 = modifiers.size();
2305
2306        for (int j = 0; j < len2; j++) {
2307            tempModifier = modifiers.get(j);
2308
2309            String modifiersToAdd = null;
2310            if(modifiers.get(j).getModifierImage() != null) {
2311                modifiersToAdd = ImageToGeoJSONString(tempModifier, ipc, normalize);
2312            } else {
2313                modifiersToAdd = LabelToGeoJSONString(tempModifier, ipc, normalize, textColor, textBackgroundColor);
2314            }
2315            if (modifiersToAdd.length() > 0) {
2316                fc.append(",");
2317                fc.append(modifiersToAdd);
2318            }
2319        }
2320        fc.append("]");
2321        String GeoJSON = fc.toString();
2322        return GeoJSON;
2323    }
2324
2325    /**
2326     * 
2327     * @param shapes
2328     * @param modifiers
2329     * @param ipc
2330     * @param normalize
2331     * @deprecated
2332     */
2333    private static void MakeWWReady(
2334            ArrayList<ShapeInfo> shapes,
2335            ArrayList<ShapeInfo> modifiers,
2336            IPointConversion ipc,
2337            boolean normalize) {
2338        ShapeInfo temp = null;
2339        int len = shapes.size();
2340        for (int i = 0; i < len; i++) {
2341
2342            temp = ShapeToWWReady(shapes.get(i), ipc, normalize);
2343            shapes.set(i, temp);
2344
2345        }
2346
2347        int len2 = modifiers.size();
2348        ShapeInfo tempModifier = null;
2349        for (int j = 0; j < len2; j++) {
2350
2351            tempModifier = modifiers.get(j);
2352
2353            //Do we need this for World Wind?
2354            tempModifier = LabelToWWReady(tempModifier, ipc, normalize);
2355            modifiers.set(j, tempModifier);
2356
2357        }
2358
2359    }
2360
2361    private static Boolean normalizePoints(ArrayList<Point2D.Double> shape, IPointConversion ipc) {
2362        ArrayList geoCoords = new ArrayList();
2363        int n = shape.size();
2364        //for (int j = 0; j < shape.size(); j++) 
2365        for (int j = 0; j < n; j++) {
2366            Point2D coord = shape.get(j);
2367            Point2D geoCoord = ipc.PixelsToGeo(coord);
2368            geoCoord = NormalizeCoordToGECoord(geoCoord);
2369            double latitude = geoCoord.getY();
2370            double longitude = geoCoord.getX();
2371            Point2D pt2d = new Point2D.Double(longitude, latitude);
2372            geoCoords.add(pt2d);
2373        }
2374        Boolean normalize = crossesIDL(geoCoords);
2375        return normalize;
2376    }
2377
2378    private static Boolean IsOnePointSymbolCode(String symbolCode) {
2379        String basicCode = SymbolUtilities.getBasicSymbolID(symbolCode);
2380        //TODO: Revisit for basic shapes
2381        //some airspaces affected
2382        if (symbolCode.equals("CAKE-----------")) {
2383            return true;
2384        } else if (symbolCode.equals("CYLINDER-------")) {
2385            return true;
2386        } else if (symbolCode.equals("RADARC---------")) {
2387            return true;
2388        }
2389
2390        return false;
2391    }
2392
2393    private static String ShapeToKMLString(String name,
2394            String description,
2395            String symbolCode,
2396            ShapeInfo shapeInfo,
2397            IPointConversion ipc,
2398            boolean normalize) {
2399
2400        java.lang.StringBuilder kml = new java.lang.StringBuilder();
2401
2402        Color lineColor = null;
2403        Color fillColor = null;
2404        String googleLineColor = null;
2405        String googleFillColor = null;
2406
2407        //String lineStyleId = "lineColor";
2408
2409        BasicStroke stroke = null;
2410        int lineWidth = 4;
2411
2412        symbolCode = JavaRendererUtilities.normalizeSymbolCode(symbolCode);
2413
2414        String cdataStart = "<![CDATA[";
2415        String cdataEnd = "]]>";
2416
2417        kml.append("<Placemark>");//("<Placemark id=\"" + id + "_mg" + "\">");
2418        kml.append("<description>" + cdataStart + "<b>" + name + "</b><br/>" + "\n" + description + cdataEnd + "</description>");
2419        //kml.append("<Style id=\"" + lineStyleId + "\">");
2420        kml.append("<Style>");
2421
2422        lineColor = shapeInfo.getLineColor();
2423        if (lineColor != null) {
2424            googleLineColor = Integer.toHexString(shapeInfo.getLineColor().toARGB());
2425
2426            stroke = shapeInfo.getStroke();
2427
2428            if (stroke != null) {
2429                lineWidth = (int) stroke.getLineWidth();
2430            }
2431
2432            googleLineColor = JavaRendererUtilities.ARGBtoABGR(googleLineColor);
2433
2434            kml.append("<LineStyle>");
2435            kml.append("<color>" + googleLineColor + "</color>");
2436            kml.append("<colorMode>normal</colorMode>");
2437            kml.append("<width>" + String.valueOf(lineWidth) + "</width>");
2438            kml.append("</LineStyle>");
2439        }
2440
2441        fillColor = shapeInfo.getFillColor();
2442        Bitmap fillPattern = shapeInfo.getPatternFillImage();
2443        if (fillColor != null || fillPattern != null) {
2444            kml.append("<PolyStyle>");
2445
2446            if (fillColor != null) {
2447                googleFillColor = Integer.toHexString(shapeInfo.getFillColor().toARGB());
2448                googleFillColor = JavaRendererUtilities.ARGBtoABGR(googleFillColor);
2449                kml.append("<color>" + googleFillColor + "</color>");
2450                kml.append("<colorMode>normal</colorMode>");
2451            }
2452            if (fillPattern != null){
2453                kml.append("<shader>" + bitmapToString(fillPattern) + "</shader>");
2454            }
2455
2456            kml.append("<fill>1</fill>");
2457            if (lineColor != null) {
2458                kml.append("<outline>1</outline>");
2459            } else {
2460                kml.append("<outline>0</outline>");
2461            }
2462            kml.append("</PolyStyle>");
2463        }
2464
2465        kml.append("</Style>");
2466
2467        ArrayList shapesArray = shapeInfo.getPolylines();
2468        int len = shapesArray.size();
2469        kml.append("<MultiGeometry>");
2470
2471        for (int i = 0; i < len; i++) {
2472            ArrayList shape = (ArrayList) shapesArray.get(i);
2473            normalize = normalizePoints(shape, ipc);
2474            if (lineColor != null && fillColor == null) {
2475                kml.append("<LineString>");
2476                kml.append("<tessellate>1</tessellate>");
2477                kml.append("<altitudeMode>clampToGround</altitudeMode>");
2478                kml.append("<coordinates>");
2479                int n = shape.size();
2480                //for (int j = 0; j < shape.size(); j++) 
2481                for (int j = 0; j < n; j++) {
2482                    Point2D coord = (Point2D) shape.get(j);
2483                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2484                    if (normalize) {
2485                        geoCoord = NormalizeCoordToGECoord(geoCoord);
2486                    }
2487
2488                    double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2489                    double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2490
2491                    kml.append(longitude);
2492                    kml.append(",");
2493                    kml.append(latitude);
2494                    if(j<shape.size()-1)
2495                        kml.append(" ");
2496                }
2497
2498                kml.append("</coordinates>");
2499                kml.append("</LineString>");
2500            }
2501
2502            if (fillColor != null) {
2503
2504                if (i == 0) {
2505                    kml.append("<Polygon>");
2506                }
2507                //kml.append("<outerBoundaryIs>");
2508                if (i == 1 && len > 1) {
2509                    kml.append("<innerBoundaryIs>");
2510                } else {
2511                    kml.append("<outerBoundaryIs>");
2512                }
2513                kml.append("<LinearRing>");
2514                kml.append("<altitudeMode>clampToGround</altitudeMode>");
2515                kml.append("<tessellate>1</tessellate>");
2516                kml.append("<coordinates>");
2517
2518                //this section is a workaround for a google earth bug. Issue 417 was closed
2519                //for linestrings but they did not fix the smae issue for fills. If Google fixes the issue
2520                //for fills then this section will need to be commented or it will induce an error.
2521                double lastLongitude = Double.MIN_VALUE;
2522                if (normalize == false && IsOnePointSymbolCode(symbolCode)) {
2523                    int n = shape.size();
2524                    //for (int j = 0; j < shape.size(); j++) 
2525                    for (int j = 0; j < n; j++) {
2526                        Point2D coord = (Point2D) shape.get(j);
2527                        Point2D geoCoord = ipc.PixelsToGeo(coord);
2528                        double longitude = geoCoord.getX();
2529                        if (lastLongitude != Double.MIN_VALUE) {
2530                            if (Math.abs(longitude - lastLongitude) > 180d) {
2531                                normalize = true;
2532                                break;
2533                            }
2534                        }
2535                        lastLongitude = longitude;
2536                    }
2537                }
2538                int n = shape.size();
2539                //for (int j = 0; j < shape.size(); j++) 
2540                for (int j = 0; j < n; j++) {
2541                    Point2D coord = (Point2D) shape.get(j);
2542                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2543
2544                    double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2545                    double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2546
2547                    //fix for fill crossing DTL
2548                    if (normalize) {
2549                        if (longitude > 0) {
2550                            longitude -= 360;
2551                        }
2552                    }
2553
2554                    kml.append(longitude);
2555                    kml.append(",");
2556                    kml.append(latitude);
2557                    if(j<shape.size()-1)
2558                        kml.append(" ");
2559                }
2560
2561                kml.append("</coordinates>");
2562                kml.append("</LinearRing>");
2563                if (i == 1 && len > 1) {
2564                    kml.append("</innerBoundaryIs>");
2565                } else {
2566                    kml.append("</outerBoundaryIs>");
2567                }
2568                if (i == len - 1) {
2569                    kml.append("</Polygon>");
2570                }
2571            }
2572        }
2573
2574        kml.append("</MultiGeometry>");
2575        kml.append("</Placemark>");
2576
2577        return kml.toString();
2578    }
2579
2580    /**
2581     * 
2582     * @param shapeInfo
2583     * @param ipc
2584     * @param normalize
2585     * @return
2586     * @deprecated
2587     */
2588    private static ShapeInfo ShapeToWWReady(
2589            ShapeInfo shapeInfo,
2590            IPointConversion ipc,
2591            boolean normalize) {
2592
2593        ArrayList shapesArray = shapeInfo.getPolylines();
2594        int len = shapesArray.size();
2595
2596        for (int i = 0; i < len; i++) {
2597            ArrayList shape = (ArrayList) shapesArray.get(i);
2598
2599            if (shapeInfo.getLineColor() != null) {
2600                int n = shape.size();
2601                //for (int j = 0; j < shape.size(); j++) 
2602                for (int j = 0; j < n; j++) {
2603                    Point2D coord = (Point2D) shape.get(j);
2604                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2605                    //M. Deutch 9-26-11
2606                    if (normalize) {
2607                        geoCoord = NormalizeCoordToGECoord(geoCoord);
2608                    }
2609
2610                    shape.set(j, geoCoord);
2611
2612                }
2613
2614            }
2615
2616            if (shapeInfo.getFillColor() != null) {
2617                int n = shape.size();
2618                //for (int j = 0; j < shape.size(); j++) 
2619                for (int j = 0; j < n; j++) {
2620                    Point2D coord = (Point2D) shape.get(j);
2621                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2622                    //M. Deutch 9-26-11
2623                    //commenting these two lines seems to help with fill not go around the pole
2624                    //if(normalize)
2625                    //geoCoord=NormalizeCoordToGECoord(geoCoord);
2626
2627                    shape.set(j, geoCoord);
2628                }
2629            }
2630        }
2631
2632        return shapeInfo;
2633    }
2634
2635    private static ShapeInfo LabelToWWReady(ShapeInfo shapeInfo,
2636            IPointConversion ipc,
2637            boolean normalize) {
2638
2639        try {
2640            Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2641            Point2D geoCoord = ipc.PixelsToGeo(coord);
2642            //M. Deutch 9-26-11
2643            if (normalize) {
2644                geoCoord = NormalizeCoordToGECoord(geoCoord);
2645            }
2646            double latitude = geoCoord.getY();
2647            double longitude = geoCoord.getX();
2648            long angle = Math.round(shapeInfo.getModifierAngle());
2649
2650            String text = shapeInfo.getModifierString();
2651
2652            if (text != null && text.equals("") == false) {
2653                shapeInfo.setModifierPosition(geoCoord);
2654            } else {
2655                return null;
2656            }
2657        } catch (Exception exc) {
2658            System.err.println(exc.getMessage());
2659            exc.printStackTrace();
2660        }
2661
2662        return shapeInfo;
2663    }
2664
2665    /**
2666     * Google earth centers text on point rather than drawing from that point.
2667     * So we need to adjust the point to where the center of the text would be.
2668     *
2669     * @param modifier
2670     */
2671    private static void AdjustModifierPointToCenter(ShapeInfo modifier) {
2672        AffineTransform at = null;
2673        try {
2674            Rectangle bounds2 = modifier.getTextLayout().getBounds();
2675            Rectangle2D bounds = new Rectangle2D.Double(bounds2.x, bounds2.y, bounds2.width, bounds2.height);
2676        } catch (Exception exc) {
2677            System.err.println(exc.getMessage());
2678            exc.printStackTrace();
2679        }
2680    }
2681
2682    /**
2683     * 
2684     * @param shapeInfo
2685     * @param ipc
2686     * @param geMap
2687     * @param normalize
2688     * @return
2689     * @deprecated
2690     */
2691    private static String ShapeToJSONString(ShapeInfo shapeInfo, IPointConversion ipc, Boolean geMap, boolean normalize) {
2692        StringBuilder JSONed = new StringBuilder();
2693        /*
2694         NOTE: Google Earth / KML colors are backwards.
2695         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2696         * */
2697        String fillColor = null;
2698        String lineColor = null;
2699
2700        if (shapeInfo.getLineColor() != null) {
2701            lineColor = Integer.toHexString(shapeInfo.getLineColor().toARGB());
2702            if (geMap) {
2703                lineColor = JavaRendererUtilities.ARGBtoABGR(lineColor);
2704            }
2705
2706        }
2707        if (shapeInfo.getFillColor() != null) {
2708            fillColor = Integer.toHexString(shapeInfo.getFillColor().toARGB());
2709            if (geMap) {
2710                fillColor = JavaRendererUtilities.ARGBtoABGR(fillColor);
2711            }
2712        }
2713
2714        BasicStroke stroke = null;
2715        stroke = shapeInfo.getStroke();
2716        int lineWidth = 4;
2717
2718        if (stroke != null) {
2719            lineWidth = (int) stroke.getLineWidth();
2720        }
2721
2722        ArrayList shapesArray = shapeInfo.getPolylines();
2723        int n = shapesArray.size();
2724        //for (int i = 0; i < shapesArray.size(); i++) 
2725        for (int i = 0; i < n; i++) {
2726            ArrayList shape = (ArrayList) shapesArray.get(i);
2727
2728            if (fillColor != null) {
2729                JSONed.append("{\"polygon\":[");
2730            } else {
2731                JSONed.append("{\"line\":[");
2732            }
2733
2734            int t = shape.size();
2735            //for (int j = 0; j < shape.size(); j++) 
2736            for (int j = 0; j < t; j++) {
2737                Point2D coord = (Point2D) shape.get(j);
2738                Point2D geoCoord = ipc.PixelsToGeo(coord);
2739                //M. Deutch 9-27-11
2740                if (normalize) {
2741                    geoCoord = NormalizeCoordToGECoord(geoCoord);
2742                }
2743                double latitude = geoCoord.getY();
2744                double longitude = geoCoord.getX();
2745
2746                //diagnostic M. Deutch 10-18-11
2747                //set the point as geo so that the 
2748                coord = new Point2D.Double(longitude, latitude);
2749                shape.set(j, coord);
2750
2751                JSONed.append("[");
2752                JSONed.append(longitude);
2753                JSONed.append(",");
2754                JSONed.append(latitude);
2755                JSONed.append("]");
2756
2757                if (j < (shape.size() - 1)) {
2758                    JSONed.append(",");
2759                }
2760            }
2761
2762            JSONed.append("]");
2763            if (lineColor != null) {
2764                JSONed.append(",\"lineColor\":\"");
2765                JSONed.append(lineColor);
2766
2767                JSONed.append("\"");
2768            }
2769            if (fillColor != null) {
2770                JSONed.append(",\"fillColor\":\"");
2771                JSONed.append(fillColor);
2772                JSONed.append("\"");
2773            }
2774
2775            JSONed.append(",\"lineWidth\":\"");
2776            JSONed.append(String.valueOf(lineWidth));
2777            JSONed.append("\"");
2778
2779            JSONed.append("}");
2780
2781            if (i < (shapesArray.size() - 1)) {
2782                JSONed.append(",");
2783            }
2784        }
2785
2786        return JSONed.toString();
2787    }
2788
2789    private static String LabelToKMLString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize, Color textColor) {
2790        java.lang.StringBuilder kml = new java.lang.StringBuilder();
2791
2792        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2793        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
2794        Point2D geoCoord = ipc.PixelsToGeo(coord);
2795        //M. Deutch 9-26-11
2796        if (normalize) {
2797            geoCoord = NormalizeCoordToGECoord(geoCoord);
2798        }
2799        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2800        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2801        long angle = Math.round(shapeInfo.getModifierAngle());
2802
2803        String text = shapeInfo.getModifierString();
2804
2805        String cdataStart = "<![CDATA[";
2806        String cdataEnd = "]]>";
2807
2808        String color = Integer.toHexString(textColor.toARGB());
2809        color = JavaRendererUtilities.ARGBtoABGR(color);
2810        float kmlScale = RendererSettings.getInstance().getKMLLabelScale();
2811
2812        if (kmlScale > 0 && text != null && text.equals("") == false) {
2813            kml.append("<Placemark>");//("<Placemark id=\"" + id + "_lp" + i + "\">");
2814            kml.append("<name>" + cdataStart + text + cdataEnd + "</name>");
2815            kml.append("<Style>");
2816            kml.append("<IconStyle>");
2817            kml.append("<scale>" + kmlScale + "</scale>");
2818            kml.append("<heading>" + angle + "</heading>");
2819            kml.append("<Icon>");
2820            kml.append("<href></href>");
2821            kml.append("</Icon>");
2822            kml.append("</IconStyle>");
2823            kml.append("<LabelStyle>");
2824            kml.append("<color>" + color + "</color>");
2825            kml.append("<scale>" + String.valueOf(kmlScale) +"</scale>");
2826            kml.append("</LabelStyle>");
2827            kml.append("</Style>");
2828            kml.append("<Point>");
2829            kml.append("<extrude>1</extrude>");
2830            kml.append("<altitudeMode>relativeToGround</altitudeMode>");
2831            kml.append("<coordinates>");
2832            kml.append(longitude);
2833            kml.append(",");
2834            kml.append(latitude);
2835            kml.append("</coordinates>");
2836            kml.append("</Point>");
2837            kml.append("</Placemark>");
2838        } else {
2839            return "";
2840        }
2841
2842        return kml.toString();
2843    }
2844
2845    /**
2846     * 
2847     * @param shapeInfo
2848     * @param ipc
2849     * @param normalize
2850     * @return
2851     * @deprecated
2852     */
2853    private static String LabelToJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2854        StringBuilder JSONed = new StringBuilder();
2855        /*
2856         NOTE: Google Earth / KML colors are backwards.
2857         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2858         * */
2859        JSONed.append("{\"label\":");
2860
2861        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2862        Point2D geoCoord = ipc.PixelsToGeo(coord);
2863        if (normalize) {
2864            geoCoord = NormalizeCoordToGECoord(geoCoord);
2865        }
2866        double latitude = geoCoord.getY();
2867        double longitude = geoCoord.getX();
2868        double angle = shapeInfo.getModifierAngle();
2869        coord.setLocation(longitude, latitude);
2870
2871        shapeInfo.setGlyphPosition(coord);
2872
2873        String text = shapeInfo.getModifierString();
2874
2875        if (text != null && text.equals("") == false) {
2876            JSONed.append("[");
2877            JSONed.append(longitude);
2878            JSONed.append(",");
2879            JSONed.append(latitude);
2880            JSONed.append("]");
2881
2882            JSONed.append(",\"text\":\"");
2883            JSONed.append(text);
2884            JSONed.append("\"");
2885
2886            JSONed.append(",\"angle\":\"");
2887            JSONed.append(angle);
2888            JSONed.append("\"}");
2889        } else {
2890            return "";
2891        }
2892
2893        return JSONed.toString();
2894    }
2895
2896    public static String canRenderMultiPoint(String symbolID, Map<String,String> modifiers, int numPoints) {
2897        try {
2898            String basicID = SymbolUtilities.getBasicSymbolID(symbolID);
2899            MSInfo info = MSLookup.getInstance().getMSLInfo(symbolID);
2900
2901            if (info == null) {
2902                if (SymbolID.getVersion(symbolID) == SymbolID.Version_2525E) {
2903                    return"Basic ID: " + basicID + " not recognized in version E (13)";
2904                } else {
2905                    return "Basic ID: " + basicID + " not recognized in version D (11)";
2906                }
2907            }
2908
2909            int drawRule = info.getDrawRule();
2910
2911            if (drawRule == DrawRules.DONOTDRAW) {
2912                return "Basic ID: " + basicID + " has no draw rule";
2913            } else if (!SymbolUtilities.isMultiPoint(symbolID)) {
2914                return "Basic ID: " + basicID + " is not a multipoint symbol";
2915            } else if (numPoints < info.getMinPointCount()) {
2916                return "Basic ID: " + basicID + " requires a minimum of " + String.valueOf(info.getMinPointCount()) + " points. " + String.valueOf(numPoints) + " are present.";
2917            }
2918
2919            //now check for required modifiers
2920            ArrayList<Double> AM = new ArrayList();
2921            ArrayList<Double> AN = new ArrayList();
2922            if (modifiers.containsKey(Modifiers.AM_DISTANCE)) {
2923                String[] amArray = modifiers.get(Modifiers.AM_DISTANCE).split(",");
2924                for (String str : amArray) {
2925                    if (!str.equals("")) {
2926                        AM.add(Double.parseDouble(str));
2927                    }
2928                }
2929            }
2930            if (modifiers.containsKey(Modifiers.AN_AZIMUTH)) {
2931                String[] anArray = modifiers.get(Modifiers.AN_AZIMUTH).split(",");
2932                for (String str : anArray) {
2933                    if (!str.equals("")) {
2934                        AN.add(Double.parseDouble(str));
2935                    }
2936                }
2937            }
2938
2939            return hasRequiredModifiers(symbolID, drawRule, AM, AN);
2940        } catch (Exception exc) {
2941            ErrorLogger.LogException("MultiPointHandler", "canRenderMultiPoint", exc);
2942            return "false: " + exc.getMessage();
2943        }
2944    }
2945
2946    static private String hasRequiredModifiers(String symbolID, int drawRule, ArrayList<Double> AM, ArrayList<Double> AN) {
2947
2948        String message = symbolID;
2949        try {
2950            if (drawRule > 700) {
2951                if (drawRule == DrawRules.CIRCULAR1)
2952                {
2953                    if (AM != null && AM.size() > 0) {
2954                        return "true";
2955                    } else {
2956                        message += " requires a modifiers object that has 1 distance/AM value.";
2957                        return message;
2958                    }
2959                } else if (drawRule == DrawRules.RECTANGULAR2)
2960                {
2961                    if (AM != null && AM.size() >= 2
2962                            && AN != null && AN.size() >= 1) {
2963                        return "true";
2964                    } else {
2965                        message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
2966                        return message;
2967                    }
2968                } else if (drawRule == DrawRules.ARC1)
2969                {
2970                    if (AM != null && AM.size() >= 1
2971                            && AN != null && AN.size() >= 2) {
2972                        return "true";
2973                    } else {
2974                        message += (" requires a modifiers object that has 2 distance/AM values and 2 azimuth/AN values per sector.  The first sector can have just one AM value although it is recommended to always use 2 values for each sector.");
2975                        return message;
2976                    }
2977                } else if (drawRule == DrawRules.CIRCULAR2)
2978                {
2979                    if (AM != null && AM.size() > 0) {
2980                        return "true";
2981                    } else {
2982                        message += (" requires a modifiers object that has at least 1 distance/AM value");
2983                        return message;
2984                    }
2985                } else if (drawRule == DrawRules.RECTANGULAR1)
2986                {
2987                    if (AM != null && AM.size() > 0) {
2988                        return "true";
2989                    } else {
2990                        message += (" requires a modifiers object that has 1 distance/AM value.");
2991                        return message;
2992                    }
2993                } else if (drawRule == DrawRules.ELLIPSE1)
2994                {
2995                    if (AM != null && AM.size() >= 2
2996                            && AN != null && AN.size() >= 1) {
2997                        return "true";
2998                    } else {
2999                        message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
3000                        return message;
3001                    }
3002                }
3003                else if (drawRule == DrawRules.RECTANGULAR3)
3004                {
3005                    if (AM != null && AM.size() >= 1) {
3006                        return "true";
3007                    } else {
3008                        message += (" requires a modifiers object that has 1 distance/AM value.");
3009                        return message;
3010                    }
3011                } else {
3012                    //should never get here
3013                    return "true";
3014                }
3015            } else if (drawRule == DrawRules.POINT17) {
3016                if (AM != null && AM.size() >= 2
3017                        && AN != null && AN.size() >= 1) {
3018                    return "true";
3019                } else {
3020                    message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
3021                    return message;
3022                }
3023            } else if (drawRule == DrawRules.POINT18) {
3024                if (AM != null && AM.size() >= 2
3025                        && AN != null && AN.size() >= 2) {
3026                    return "true";
3027                } else {
3028                    message += (" requires a modifiers object that has 2 distance/AM values and 2 azimuth/AN values.");
3029                    return message;
3030                }
3031            } else if (drawRule == DrawRules.CORRIDOR1) {
3032                if (AM != null && AM.size() > 0) {
3033                    return "true";
3034                } else {
3035                    message += (" requires a modifiers object that has 1 distance/AM value.");
3036                    return message;
3037                }
3038            } else {
3039                //no required parameters
3040                return "true";
3041            }
3042        } catch (Exception exc) {
3043            ErrorLogger.LogException("MultiPointHandler", "hasRequiredModifiers", exc);
3044            return "true";
3045        }
3046    }
3047}