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
1582        try {
1583
1584            // The following attirubtes are labels.  All of them
1585            // are strings and can be added on the creation of the
1586            // MilStdSymbol by adding to a Map and passing in the
1587            // modifiers parameter.
1588            if (saModifiers != null) {
1589                if (saModifiers.containsKey(Modifiers.C_QUANTITY)) {
1590                    modifiers.put(Modifiers.C_QUANTITY, String.valueOf(saModifiers.get(Modifiers.C_QUANTITY)));
1591                }
1592
1593                if (saModifiers.containsKey(Modifiers.H_ADDITIONAL_INFO_1)) {
1594                    modifiers.put(Modifiers.H_ADDITIONAL_INFO_1, String.valueOf(saModifiers.get(Modifiers.H_ADDITIONAL_INFO_1)));
1595                }
1596
1597                if (saModifiers.containsKey(Modifiers.H1_ADDITIONAL_INFO_2)) {
1598                    modifiers.put(Modifiers.H1_ADDITIONAL_INFO_2, String.valueOf(saModifiers.get(Modifiers.H1_ADDITIONAL_INFO_2)));
1599                }
1600
1601                if (saModifiers.containsKey(Modifiers.H2_ADDITIONAL_INFO_3)) {
1602                    modifiers.put(Modifiers.H2_ADDITIONAL_INFO_3, String.valueOf(saModifiers.get(Modifiers.H2_ADDITIONAL_INFO_3)));
1603                }
1604
1605                if (saModifiers.containsKey(Modifiers.N_HOSTILE)) {
1606                    if (saModifiers.get(Modifiers.N_HOSTILE) == null) {
1607                        modifiers.put(Modifiers.N_HOSTILE, "");
1608                    } else {
1609                        modifiers.put(Modifiers.N_HOSTILE, String.valueOf(saModifiers.get(Modifiers.N_HOSTILE)));
1610                    }
1611                }
1612
1613                if (saModifiers.containsKey(Modifiers.Q_DIRECTION_OF_MOVEMENT)) {
1614                    modifiers.put(Modifiers.Q_DIRECTION_OF_MOVEMENT, String.valueOf(saModifiers.get(Modifiers.Q_DIRECTION_OF_MOVEMENT)));
1615                }
1616
1617                if (saModifiers.containsKey(Modifiers.T_UNIQUE_DESIGNATION_1)) {
1618                    modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, String.valueOf(saModifiers.get(Modifiers.T_UNIQUE_DESIGNATION_1)));
1619                }
1620
1621                if (saModifiers.containsKey(Modifiers.T1_UNIQUE_DESIGNATION_2)) {
1622                    modifiers.put(Modifiers.T1_UNIQUE_DESIGNATION_2, String.valueOf(saModifiers.get(Modifiers.T1_UNIQUE_DESIGNATION_2)));
1623                }
1624
1625                if (saModifiers.containsKey(Modifiers.V_EQUIP_TYPE)) {
1626                    modifiers.put(Modifiers.V_EQUIP_TYPE, String.valueOf(saModifiers.get(Modifiers.V_EQUIP_TYPE)));
1627                }
1628
1629                if (saModifiers.containsKey(Modifiers.AS_COUNTRY)) {
1630                    modifiers.put(Modifiers.AS_COUNTRY, String.valueOf(saModifiers.get(Modifiers.AS_COUNTRY)));
1631                } else if (SymbolID.getCountryCode(symbol.getSymbolID()) > 0 && !GENCLookup.getInstance().get3CharCode(SymbolID.getCountryCode(symbol.getSymbolID())).equals("")) {
1632                    modifiers.put(Modifiers.AS_COUNTRY, GENCLookup.getInstance().get3CharCode(SymbolID.getCountryCode(symbol.getSymbolID())));
1633                }
1634
1635                if (saModifiers.containsKey(Modifiers.AP_TARGET_NUMBER)) {
1636                    modifiers.put(Modifiers.AP_TARGET_NUMBER, String.valueOf(saModifiers.get(Modifiers.AP_TARGET_NUMBER)));
1637                }
1638
1639                if (saModifiers.containsKey(Modifiers.W_DTG_1)) {
1640                    modifiers.put(Modifiers.W_DTG_1, String.valueOf(saModifiers.get(Modifiers.W_DTG_1)));
1641                }
1642
1643                if (saModifiers.containsKey(Modifiers.W1_DTG_2)) {
1644                    modifiers.put(Modifiers.W1_DTG_2, String.valueOf(saModifiers.get(Modifiers.W1_DTG_2)));
1645                }
1646
1647                if (saModifiers.containsKey(Modifiers.Y_LOCATION)) {
1648                    modifiers.put(Modifiers.Y_LOCATION, String.valueOf(saModifiers.get(Modifiers.Y_LOCATION)));
1649                }
1650
1651                //Required multipoint modifier arrays
1652                if (saModifiers.containsKey(Modifiers.X_ALTITUDE_DEPTH)) {
1653                    altitudes = new ArrayList<Double>();
1654                    String[] arrAltitudes = String.valueOf(saModifiers.get(Modifiers.X_ALTITUDE_DEPTH)).split(",");
1655                    for (String x : arrAltitudes) {
1656                        if (x.equals("") != true) {
1657                            altitudes.add(Double.parseDouble(x));
1658                        }
1659                    }
1660                }
1661
1662                if (saModifiers.containsKey(Modifiers.AM_DISTANCE)) {
1663                    distances = new ArrayList<Double>();
1664                    String[] arrDistances = String.valueOf(saModifiers.get(Modifiers.AM_DISTANCE)).split(",");
1665                    for (String am : arrDistances) {
1666                        if (am.equals("") != true) {
1667                            distances.add(Double.parseDouble(am));
1668                        }
1669                    }
1670                }
1671
1672                if (saModifiers.containsKey(Modifiers.AN_AZIMUTH)) {
1673                    azimuths = new ArrayList<Double>();
1674                    String[] arrAzimuths = String.valueOf(saModifiers.get(Modifiers.AN_AZIMUTH)).split(",");;
1675                    for (String an : arrAzimuths) {
1676                        if (an.equals("") != true) {
1677                            azimuths.add(Double.parseDouble(an));
1678                        }
1679                    }
1680                }
1681            }
1682            if (saAttributes != null) {
1683                // These properties are ints, not labels, they are colors.//////////////////
1684                if (saAttributes.containsKey(MilStdAttributes.FillColor)) {
1685                    fillColor = (String) saAttributes.get(MilStdAttributes.FillColor);
1686                }
1687
1688                if (saAttributes.containsKey(MilStdAttributes.LineColor)) {
1689                    lineColor = (String) saAttributes.get(MilStdAttributes.LineColor);
1690                }
1691
1692                if (saAttributes.containsKey(MilStdAttributes.LineWidth)) {
1693                    lineWidth = Integer.parseInt(saAttributes.get(MilStdAttributes.LineWidth));
1694                }
1695                
1696                if (saAttributes.containsKey(MilStdAttributes.TextColor)) {
1697                    textColor = (String) saAttributes.get(MilStdAttributes.TextColor);
1698                }
1699                
1700                if (saAttributes.containsKey(MilStdAttributes.TextBackgroundColor)) {
1701                    textBackgroundColor = (String) saAttributes.get(MilStdAttributes.TextBackgroundColor);
1702                }
1703
1704                if (saAttributes.containsKey(MilStdAttributes.AltitudeMode)) {
1705                    altMode = saAttributes.get(MilStdAttributes.AltitudeMode);
1706                }
1707
1708                if (saAttributes.containsKey(MilStdAttributes.UseDashArray)) {
1709                    useDashArray = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.UseDashArray));
1710                }
1711
1712                if (saAttributes.containsKey(MilStdAttributes.UsePatternFill)) {
1713                    usePatternFill = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.UsePatternFill));
1714                }
1715
1716                if (saAttributes.containsKey(MilStdAttributes.PatternFillType)) {
1717                    patternFillType = Integer.parseInt((saAttributes.get(MilStdAttributes.PatternFillType)));
1718                }
1719
1720                if (saAttributes.containsKey(MilStdAttributes.HideOptionalLabels)) {
1721                    hideOptionalLabels = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.HideOptionalLabels));
1722                }
1723
1724                if(saAttributes.containsKey(MilStdAttributes.AltitudeUnits)) {
1725                    altitudeUnit = DistanceUnit.parse(saAttributes.get(MilStdAttributes.AltitudeUnits));
1726                }
1727
1728                if(saAttributes.containsKey(MilStdAttributes.DistanceUnits)) {
1729                    distanceUnit = DistanceUnit.parse(saAttributes.get(MilStdAttributes.DistanceUnits));
1730                }
1731
1732                if(saAttributes.containsKey(MilStdAttributes.PixelSize)) {
1733                    pixelSize = Integer.parseInt(saAttributes.get(MilStdAttributes.PixelSize));
1734                    symbol.setUnitSize(pixelSize);
1735                }
1736
1737                if (saAttributes.containsKey(MilStdAttributes.KeepUnitRatio)) {
1738                    keepUnitRatio = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.KeepUnitRatio));
1739                    symbol.setKeepUnitRatio(keepUnitRatio);
1740                }
1741            }
1742
1743            symbol.setModifierMap(modifiers);
1744
1745            if (fillColor != null && fillColor.equals("") == false) {
1746                symbol.setFillColor(RendererUtilities.getColorFromHexString(fillColor));
1747            } 
1748
1749            if (lineColor != null && lineColor.equals("") == false) {
1750                symbol.setLineColor(RendererUtilities.getColorFromHexString(lineColor));
1751                symbol.setTextColor(RendererUtilities.getColorFromHexString(lineColor));
1752            }
1753            else if(symbol.getLineColor()==null)
1754                symbol.setLineColor(Color.black);
1755
1756            if (lineWidth > 0) {
1757                symbol.setLineWidth(lineWidth);
1758            }
1759            
1760            if (textColor != null && textColor.equals("") == false) {
1761                symbol.setTextColor(RendererUtilities.getColorFromHexString(textColor));
1762            } else if(symbol.getTextColor()==null)
1763                symbol.setTextColor(Color.black);
1764                
1765            if (textBackgroundColor != null && textBackgroundColor.equals("") == false) {
1766                symbol.setTextBackgroundColor(RendererUtilities.getColorFromHexString(textBackgroundColor));
1767            }
1768
1769            if (altMode != null) {
1770                symbol.setAltitudeMode(altMode);
1771            }
1772
1773            symbol.setUseDashArray(useDashArray);
1774            symbol.setUseFillPattern(usePatternFill);
1775            symbol.setHideOptionalLabels(hideOptionalLabels);
1776            symbol.setAltitudeUnit(altitudeUnit);
1777            symbol.setDistanceUnit(distanceUnit);
1778
1779            // Check grpahic modifiers variables.  If we set earlier, populate
1780            // the fields, otherwise, ignore.
1781            if (altitudes != null) {
1782                symbol.setModifiers_AM_AN_X(Modifiers.X_ALTITUDE_DEPTH, altitudes);
1783            }
1784            if (distances != null) {
1785                symbol.setModifiers_AM_AN_X(Modifiers.AM_DISTANCE, distances);
1786            }
1787
1788            if (azimuths != null) {
1789                symbol.setModifiers_AM_AN_X(Modifiers.AN_AZIMUTH, azimuths);
1790            }
1791
1792            //Check if sector range fan has required min range
1793            if (SymbolUtilities.getBasicSymbolID(symbol.getSymbolID()).equals("25242200")) {
1794                if (symbol.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH) != null
1795                        && symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE) != null) {
1796                    int anCount = symbol.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH).size();
1797                    int amCount = symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE).size();
1798                    ArrayList<Double> am = null;
1799                    if (amCount < ((anCount / 2) + 1)) {
1800                        am = symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
1801                        if (am.get(0) != 0.0) {
1802                            am.add(0, 0.0);
1803                        }
1804                    }
1805                }
1806            }
1807        } catch (Exception exc2) {
1808            Log.e("MPH.populateModifiers", exc2.getMessage(), exc2);
1809        }
1810        return true;
1811
1812    }
1813
1814    private static String KMLize(String id, String name,
1815            String description,
1816            String symbolCode,
1817            ArrayList<ShapeInfo> shapes,
1818            ArrayList<ShapeInfo> modifiers,
1819            IPointConversion ipc,
1820            boolean normalize, Color textColor) {
1821
1822        java.lang.StringBuilder kml = new java.lang.StringBuilder();
1823
1824        ShapeInfo tempModifier = null;
1825
1826        String cdataStart = "<![CDATA[";
1827        String cdataEnd = "]]>";
1828
1829        int len = shapes.size();
1830        kml.append("<Folder id=\"" + id + "\">");
1831        kml.append("<name>" + cdataStart + name + cdataEnd + "</name>");
1832        kml.append("<visibility>1</visibility>");
1833        for (int i = 0; i < len; i++) {
1834
1835            String shapesToAdd = ShapeToKMLString(name, description, symbolCode, shapes.get(i), ipc, normalize);
1836            kml.append(shapesToAdd);
1837        }
1838
1839        int len2 = modifiers.size();
1840
1841        for (int j = 0; j < len2; j++) {
1842
1843            tempModifier = modifiers.get(j);
1844
1845            //if(geMap)//if using google earth
1846            //assume kml text is going to be centered
1847            //AdjustModifierPointToCenter(tempModifier);
1848
1849            String labelsToAdd = LabelToKMLString(tempModifier, ipc, normalize, textColor);
1850            kml.append(labelsToAdd);
1851        }
1852
1853        kml.append("</Folder>");
1854        return kml.toString();
1855    }
1856
1857    /**
1858     * 
1859     * @param shapes
1860     * @param modifiers
1861     * @param ipc
1862     * @param geMap
1863     * @param normalize
1864     * @return 
1865     * @deprecated Use GeoJSONize()
1866     */
1867    private static String JSONize(ArrayList<ShapeInfo> shapes, ArrayList<ShapeInfo> modifiers, IPointConversion ipc, Boolean geMap, boolean normalize) {
1868        String polygons = "";
1869        String lines = "";
1870        String labels = "";
1871        String jstr = "";
1872        ShapeInfo tempModifier = null;
1873
1874        int len = shapes.size();
1875        for (int i = 0; i < len; i++) {
1876            if (jstr.length() > 0) {
1877                jstr += ",";
1878            }
1879            String shapesToAdd = ShapeToJSONString(shapes.get(i), ipc, geMap, normalize);
1880            if (shapesToAdd.length() > 0) {
1881                if (shapesToAdd.startsWith("line", 2)) {
1882                    if (lines.length() > 0) {
1883                        lines += ",";
1884                    }
1885
1886                    lines += shapesToAdd;
1887                } else if (shapesToAdd.startsWith("polygon", 2)) {
1888                    if (polygons.length() > 0) {
1889                        polygons += ",";
1890                    }
1891
1892                    polygons += shapesToAdd;
1893                }
1894            }
1895        }
1896
1897        jstr += "\"polygons\": [" + polygons + "],"
1898                + "\"lines\": [" + lines + "],";
1899        int len2 = modifiers.size();
1900        labels = "";
1901        for (int j = 0; j < len2; j++) {
1902            tempModifier = modifiers.get(j);
1903            if (geMap) {
1904                AdjustModifierPointToCenter(tempModifier);
1905            }
1906            String labelsToAdd = LabelToJSONString(tempModifier, ipc, normalize);
1907            if (labelsToAdd.length() > 0) {
1908                if (labels.length() > 0) {
1909                    labels += ",";
1910                }
1911
1912                labels += labelsToAdd;
1913
1914            }
1915        }
1916        jstr += "\"labels\": [" + labels + "]";
1917        return jstr;
1918    }
1919
1920    private static Color getIdealTextBackgroundColor(Color fgColor) {
1921        //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "in function", Level.SEVERE);
1922        try {
1923            //an array of three elements containing the
1924            //hue, saturation, and brightness (in that order),
1925            //of the color with the indicated red, green, and blue components/
1926            float hsbvals[] = new float[3];
1927
1928            if (fgColor != null) {/*
1929                 Color.RGBtoHSB(fgColor.getRed(), fgColor.getGreen(), fgColor.getBlue(), hsbvals);
1930
1931                 if(hsbvals != null)
1932                 {
1933                 //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "length: " + String.valueOf(hsbvals.length));
1934                 //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "H: " + String.valueOf(hsbvals[0]) + " S: " + String.valueOf(hsbvals[1]) + " B: " + String.valueOf(hsbvals[2]),Level.SEVERE);
1935                 if(hsbvals[2] > 0.6)
1936                 return Color.BLACK;
1937                 else
1938                 return Color.WHITE;
1939                 }*/
1940
1941                int nThreshold = RendererSettings.getInstance().getTextBackgroundAutoColorThreshold();//160;
1942                int bgDelta = (int) ((fgColor.getRed() * 0.299) + (fgColor.getGreen() * 0.587) + (fgColor.getBlue() * 0.114));
1943                //ErrorLogger.LogMessage("bgDelta: " + String.valueOf(255-bgDelta));
1944                //if less than threshold, black, otherwise white.
1945                //return (255 - bgDelta < nThreshold) ? Color.BLACK : Color.WHITE;//new Color(0, 0, 0, fgColor.getAlpha())
1946                return (255 - bgDelta < nThreshold) ? new Color(0, 0, 0, fgColor.getAlpha()) : new Color(255, 255, 255, fgColor.getAlpha());
1947            }
1948        } catch (Exception exc) {
1949            ErrorLogger.LogException("SymbolDraw", "getIdealtextBGColor", exc);
1950        }
1951        return Color.WHITE;
1952    }
1953
1954    private static String LabelToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize, Color textColor, Color textBackgroundColor) {
1955
1956        StringBuilder JSONed = new StringBuilder();
1957        StringBuilder properties = new StringBuilder();
1958        StringBuilder geometry = new StringBuilder();
1959
1960        Color outlineColor = getIdealTextBackgroundColor(textColor);
1961        if(textBackgroundColor != null)
1962                outlineColor = textBackgroundColor;
1963
1964        //AffineTransform at = shapeInfo.getAffineTransform();
1965        //Point2D coord = (Point2D)new Point2D.Double(at.getTranslateX(), at.getTranslateY());
1966        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
1967        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
1968        Point2D geoCoord = ipc.PixelsToGeo(coord);
1969        //M. Deutch 9-27-11
1970        if (normalize) {
1971            geoCoord = NormalizeCoordToGECoord(geoCoord);
1972        }
1973        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
1974        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
1975        double angle = shapeInfo.getModifierAngle();
1976        coord.setLocation(longitude, latitude);
1977
1978        //diagnostic M. Deutch 10-18-11
1979        shapeInfo.setGlyphPosition(coord);
1980
1981        String text = shapeInfo.getModifierString();
1982        
1983        int justify=shapeInfo.getTextJustify();
1984        String strJustify="left";
1985        if(justify==0)
1986            strJustify="left";
1987        else if(justify==1)
1988            strJustify="center";
1989        else if(justify==2)
1990            strJustify="right";
1991
1992        
1993        RendererSettings RS = RendererSettings.getInstance();
1994
1995        if (text != null && text.equals("") == false) {
1996
1997            JSONed.append("{\"type\":\"Feature\",\"properties\":{\"label\":\"");
1998            JSONed.append(text);
1999            JSONed.append("\",\"pointRadius\":0,\"fontColor\":\"");
2000            JSONed.append(RendererUtilities.colorToHexString(textColor, false));
2001            JSONed.append("\",\"fontSize\":\"");
2002            JSONed.append(String.valueOf(RS.getMPLabelFontSize()) + "pt\"");
2003            JSONed.append(",\"fontFamily\":\"");
2004            JSONed.append(RS.getMPLabelFontName());
2005            JSONed.append(", sans-serif");
2006
2007            if (RS.getMPLabelFontType() == Typeface.BOLD) {
2008                JSONed.append("\",\"fontWeight\":\"bold\"");
2009            } else {
2010                JSONed.append("\",\"fontWeight\":\"normal\"");
2011            }
2012
2013            //JSONed.append(",\"labelAlign\":\"lm\"");
2014            JSONed.append(",\"labelAlign\":\"");
2015            JSONed.append(strJustify);
2016            JSONed.append("\",\"labelBaseline\":\"alphabetic\"");
2017            JSONed.append(",\"labelXOffset\":0");
2018            JSONed.append(",\"labelYOffset\":0");
2019            JSONed.append(",\"labelOutlineColor\":\"");
2020            JSONed.append(RendererUtilities.colorToHexString(outlineColor, false));
2021            JSONed.append("\",\"labelOutlineWidth\":");
2022            JSONed.append("4");
2023            JSONed.append(",\"rotation\":");
2024            JSONed.append(angle);
2025            JSONed.append(",\"angle\":");
2026            JSONed.append(angle);
2027            JSONed.append("},");
2028
2029            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
2030            JSONed.append(longitude);
2031            JSONed.append(",");
2032            JSONed.append(latitude);
2033            JSONed.append("]");
2034            JSONed.append("}}");
2035
2036        } else {
2037            return "";
2038        }
2039
2040        return JSONed.toString();
2041    }
2042
2043    private static String ShapeToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2044        StringBuilder JSONed = new StringBuilder();
2045        StringBuilder properties = new StringBuilder();
2046        StringBuilder geometry = new StringBuilder();
2047        String geometryType = null;
2048        String sda = null;
2049        /*
2050         NOTE: Google Earth / KML colors are backwards.
2051         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2052         * */
2053        Color lineColor = shapeInfo.getLineColor();
2054        Color fillColor = shapeInfo.getFillColor();
2055
2056        if (shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_FILL || fillColor != null || shapeInfo.getPatternFillImage() != null) {
2057            geometryType = "\"Polygon\"";
2058        } else //if(shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_POLYLINE)
2059        {
2060            geometryType = "\"MultiLineString\"";
2061        }
2062
2063        BasicStroke stroke = null;
2064        stroke = shapeInfo.getStroke();
2065        int lineWidth = 4;
2066
2067        if (stroke != null) {
2068            lineWidth = (int) stroke.getLineWidth();
2069            //lineWidth++;
2070            //System.out.println("lineWidth: " + String.valueOf(lineWidth));
2071        }
2072
2073        //generate JSON properties for feature
2074        properties.append("\"properties\":{");
2075        properties.append("\"label\":\"\",");
2076        if (lineColor != null) {
2077            properties.append("\"strokeColor\":\"" + RendererUtilities.colorToHexString(lineColor, false) + "\",");
2078            properties.append("\"lineOpacity\":" + String.valueOf(lineColor.getAlpha() / 255f) + ",");
2079        }
2080        if (fillColor != null) {
2081            properties.append("\"fillColor\":\"" + RendererUtilities.colorToHexString(fillColor, false) + "\",");
2082            properties.append("\"fillOpacity\":" + String.valueOf(fillColor.getAlpha() / 255f) + ",");
2083        }
2084        if (shapeInfo.getPatternFillImage() != null) {
2085            properties.append("\"fillPattern\":\"" + bitmapToString(shapeInfo.getPatternFillImage()) + "\",");
2086        }
2087        if(stroke.getDashArray() != null)
2088        {
2089            float[] arrSDA = stroke.getDashArray();
2090            sda = "[";
2091            sda += String.valueOf(arrSDA[0]);
2092            if(arrSDA.length > 1)
2093            {
2094                for(int i = 1; i < arrSDA.length; i++)
2095                {
2096                    sda = sda + ", " + String.valueOf(arrSDA[i]);
2097                }
2098            }
2099            sda += "]";
2100            sda = "\"strokeDasharray\":" + sda + ",";
2101            properties.append(sda);
2102        }
2103
2104        int lineCap = stroke.getEndCap();
2105        properties.append("\"lineCap\":" + lineCap + ",");
2106
2107        String strokeWidth = String.valueOf(lineWidth);
2108        properties.append("\"strokeWidth\":" + strokeWidth + ",");
2109        properties.append("\"strokeWeight\":" + strokeWidth + "");
2110        properties.append("},");
2111
2112
2113        properties.append("\"style\":{");
2114        if (lineColor != null) {
2115            properties.append("\"stroke\":\"" + RendererUtilities.colorToHexString(lineColor, false) + "\",");
2116            properties.append("\"line-opacity\":" + String.valueOf(lineColor.getAlpha() / 255f) + ",");
2117        }
2118        if (fillColor != null) {
2119            properties.append("\"fill\":\"" + RendererUtilities.colorToHexString(fillColor, false) + "\",");
2120            properties.append("\"fill-opacity\":" + String.valueOf(fillColor.getAlpha() / 255f) + ",");
2121        }
2122        if(stroke.getDashArray() != null)
2123        {
2124            float[] da = stroke.getDashArray();
2125            sda = String.valueOf(da[0]);
2126            if(da.length > 1)
2127            {
2128                for(int i = 1; i < da.length; i++)
2129                {
2130                    sda = sda + " " + String.valueOf(da[i]);
2131                }
2132            }
2133            sda = "\"stroke-dasharray\":\"" + sda + "\",";
2134            properties.append(sda);
2135            sda = null;
2136        }
2137
2138        if(lineCap == BasicStroke.CAP_SQUARE)
2139            properties.append("\"stroke-linecap\":\"square\",");
2140        else if(lineCap == BasicStroke.CAP_ROUND)
2141            properties.append("\"stroke-linecap\":\"round\",");
2142        else if(lineCap == BasicStroke.CAP_BUTT)
2143            properties.append("\"stroke-linecap\":\"butt\",");
2144
2145        strokeWidth = String.valueOf(lineWidth);
2146        properties.append("\"stroke-width\":" + strokeWidth);
2147        properties.append("}");
2148
2149
2150        //generate JSON geometry for feature
2151        geometry.append("\"geometry\":{\"type\":");
2152        geometry.append(geometryType);
2153        geometry.append(",\"coordinates\":[");
2154
2155        ArrayList shapesArray = shapeInfo.getPolylines();
2156
2157        for (int i = 0; i < shapesArray.size(); i++) {
2158            ArrayList pointList = (ArrayList) shapesArray.get(i);
2159
2160            normalize = normalizePoints(pointList, ipc);
2161
2162            geometry.append("[");
2163
2164            //System.out.println("Pixel Coords:");
2165            for (int j = 0; j < pointList.size(); j++) {
2166                Point2D coord = (Point2D) pointList.get(j);
2167                Point2D geoCoord = ipc.PixelsToGeo(coord);
2168                //M. Deutch 9-27-11
2169                if (normalize) {
2170                    geoCoord = NormalizeCoordToGECoord(geoCoord);
2171                }
2172                double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2173                double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2174
2175                //fix for fill crossing DTL
2176                if (normalize && fillColor != null) {
2177                    if (longitude > 0) {
2178                        longitude -= 360;
2179                    }
2180                }
2181
2182                //diagnostic M. Deutch 10-18-11
2183                //set the point as geo so that the 
2184                //coord.setLocation(longitude, latitude);
2185                coord = new Point2D.Double(longitude, latitude);
2186                pointList.set(j, coord);
2187                //end section
2188
2189                geometry.append("[");
2190                geometry.append(longitude);
2191                geometry.append(",");
2192                geometry.append(latitude);
2193                geometry.append("]");
2194
2195                if (j < (pointList.size() - 1)) {
2196                    geometry.append(",");
2197                }
2198            }
2199
2200            geometry.append("]");
2201
2202            if (i < (shapesArray.size() - 1)) {
2203                geometry.append(",");
2204            }
2205        }
2206        geometry.append("]}");
2207
2208        JSONed.append("{\"type\":\"Feature\",");
2209        JSONed.append(properties.toString());
2210        JSONed.append(",");
2211        JSONed.append(geometry.toString());
2212        JSONed.append("}");
2213
2214        return JSONed.toString();
2215    }
2216
2217    private static String ImageToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2218
2219        StringBuilder JSONed = new StringBuilder();
2220        StringBuilder properties = new StringBuilder();
2221        StringBuilder geometry = new StringBuilder();
2222
2223        //AffineTransform at = shapeInfo.getAffineTransform();
2224        //Point2D coord = (Point2D)new Point2D.Double(at.getTranslateX(), at.getTranslateY());
2225        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2226        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
2227        Point2D geoCoord = ipc.PixelsToGeo(coord);
2228        //M. Deutch 9-27-11
2229        if (normalize) {
2230            geoCoord = NormalizeCoordToGECoord(geoCoord);
2231        }
2232        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2233        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2234        double angle = shapeInfo.getModifierAngle();
2235        coord.setLocation(longitude, latitude);
2236
2237        //diagnostic M. Deutch 10-18-11
2238        shapeInfo.setGlyphPosition(coord);
2239
2240        Bitmap image = shapeInfo.getModifierImage();
2241
2242        RendererSettings RS = RendererSettings.getInstance();
2243
2244        if (image != null) {
2245
2246            JSONed.append("{\"type\":\"Feature\",\"properties\":{\"image\":\"");
2247            JSONed.append(bitmapToString(image));
2248            JSONed.append("\",\"rotation\":");
2249            JSONed.append(angle);
2250            JSONed.append(",\"angle\":");
2251            JSONed.append(angle);
2252            JSONed.append("},");
2253            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
2254            JSONed.append(longitude);
2255            JSONed.append(",");
2256            JSONed.append(latitude);
2257            JSONed.append("]");
2258            JSONed.append("}}");
2259
2260        } else {
2261            return "";
2262        }
2263
2264        return JSONed.toString();
2265    }
2266
2267    protected static String bitmapToString(Bitmap bitmap) {
2268        final int COMPRESSION_QUALITY = 100;
2269        String encodedImage;
2270        ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream();
2271        bitmap.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY,
2272                byteArrayBitmapStream);
2273        byte[] b = byteArrayBitmapStream.toByteArray();
2274        encodedImage = Base64.encodeToString(b, Base64.DEFAULT);
2275        return "data:image/png;base64," + encodedImage;
2276    }
2277
2278    private static String GeoJSONize(ArrayList<ShapeInfo> shapes, ArrayList<ShapeInfo> modifiers, IPointConversion ipc, boolean normalize, Color textColor, Color textBackgroundColor) {
2279
2280        String jstr = "";
2281        ShapeInfo tempModifier = null;
2282        StringBuilder fc = new StringBuilder();//JSON feature collection
2283
2284        fc.append("[");
2285
2286        int len = shapes.size();
2287        for (int i = 0; i < len; i++) {
2288
2289            String shapesToAdd = ShapeToGeoJSONString(shapes.get(i), ipc, normalize);
2290            if (shapesToAdd.length() > 0) {
2291                fc.append(shapesToAdd);
2292            }
2293            if (i < len - 1) {
2294                fc.append(",");
2295            }
2296        }
2297
2298        int len2 = modifiers.size();
2299
2300        for (int j = 0; j < len2; j++) {
2301            tempModifier = modifiers.get(j);
2302
2303            String modifiersToAdd = null;
2304            if(modifiers.get(j).getModifierImage() != null) {
2305                modifiersToAdd = ImageToGeoJSONString(tempModifier, ipc, normalize);
2306            } else {
2307                modifiersToAdd = LabelToGeoJSONString(tempModifier, ipc, normalize, textColor, textBackgroundColor);
2308            }
2309            if (modifiersToAdd.length() > 0) {
2310                fc.append(",");
2311                fc.append(modifiersToAdd);
2312            }
2313        }
2314        fc.append("]");
2315        String GeoJSON = fc.toString();
2316        return GeoJSON;
2317    }
2318
2319    /**
2320     * 
2321     * @param shapes
2322     * @param modifiers
2323     * @param ipc
2324     * @param normalize
2325     * @deprecated
2326     */
2327    private static void MakeWWReady(
2328            ArrayList<ShapeInfo> shapes,
2329            ArrayList<ShapeInfo> modifiers,
2330            IPointConversion ipc,
2331            boolean normalize) {
2332        ShapeInfo temp = null;
2333        int len = shapes.size();
2334        for (int i = 0; i < len; i++) {
2335
2336            temp = ShapeToWWReady(shapes.get(i), ipc, normalize);
2337            shapes.set(i, temp);
2338
2339        }
2340
2341        int len2 = modifiers.size();
2342        ShapeInfo tempModifier = null;
2343        for (int j = 0; j < len2; j++) {
2344
2345            tempModifier = modifiers.get(j);
2346
2347            //Do we need this for World Wind?
2348            tempModifier = LabelToWWReady(tempModifier, ipc, normalize);
2349            modifiers.set(j, tempModifier);
2350
2351        }
2352
2353    }
2354
2355    private static Boolean normalizePoints(ArrayList<Point2D.Double> shape, IPointConversion ipc) {
2356        ArrayList geoCoords = new ArrayList();
2357        int n = shape.size();
2358        //for (int j = 0; j < shape.size(); j++) 
2359        for (int j = 0; j < n; j++) {
2360            Point2D coord = shape.get(j);
2361            Point2D geoCoord = ipc.PixelsToGeo(coord);
2362            geoCoord = NormalizeCoordToGECoord(geoCoord);
2363            double latitude = geoCoord.getY();
2364            double longitude = geoCoord.getX();
2365            Point2D pt2d = new Point2D.Double(longitude, latitude);
2366            geoCoords.add(pt2d);
2367        }
2368        Boolean normalize = crossesIDL(geoCoords);
2369        return normalize;
2370    }
2371
2372    private static Boolean IsOnePointSymbolCode(String symbolCode) {
2373        String basicCode = SymbolUtilities.getBasicSymbolID(symbolCode);
2374        //TODO: Revisit for basic shapes
2375        //some airspaces affected
2376        if (symbolCode.equals("CAKE-----------")) {
2377            return true;
2378        } else if (symbolCode.equals("CYLINDER-------")) {
2379            return true;
2380        } else if (symbolCode.equals("RADARC---------")) {
2381            return true;
2382        }
2383
2384        return false;
2385    }
2386
2387    private static String ShapeToKMLString(String name,
2388            String description,
2389            String symbolCode,
2390            ShapeInfo shapeInfo,
2391            IPointConversion ipc,
2392            boolean normalize) {
2393
2394        java.lang.StringBuilder kml = new java.lang.StringBuilder();
2395
2396        Color lineColor = null;
2397        Color fillColor = null;
2398        String googleLineColor = null;
2399        String googleFillColor = null;
2400
2401        //String lineStyleId = "lineColor";
2402
2403        BasicStroke stroke = null;
2404        int lineWidth = 4;
2405
2406        symbolCode = JavaRendererUtilities.normalizeSymbolCode(symbolCode);
2407
2408        String cdataStart = "<![CDATA[";
2409        String cdataEnd = "]]>";
2410
2411        kml.append("<Placemark>");//("<Placemark id=\"" + id + "_mg" + "\">");
2412        kml.append("<description>" + cdataStart + "<b>" + name + "</b><br/>" + "\n" + description + cdataEnd + "</description>");
2413        //kml.append("<Style id=\"" + lineStyleId + "\">");
2414        kml.append("<Style>");
2415
2416        lineColor = shapeInfo.getLineColor();
2417        if (lineColor != null) {
2418            googleLineColor = Integer.toHexString(shapeInfo.getLineColor().toARGB());
2419
2420            stroke = shapeInfo.getStroke();
2421
2422            if (stroke != null) {
2423                lineWidth = (int) stroke.getLineWidth();
2424            }
2425
2426            googleLineColor = JavaRendererUtilities.ARGBtoABGR(googleLineColor);
2427
2428            kml.append("<LineStyle>");
2429            kml.append("<color>" + googleLineColor + "</color>");
2430            kml.append("<colorMode>normal</colorMode>");
2431            kml.append("<width>" + String.valueOf(lineWidth) + "</width>");
2432            kml.append("</LineStyle>");
2433        }
2434
2435        fillColor = shapeInfo.getFillColor();
2436        Bitmap fillPattern = shapeInfo.getPatternFillImage();
2437        if (fillColor != null || fillPattern != null) {
2438            kml.append("<PolyStyle>");
2439
2440            if (fillColor != null) {
2441                googleFillColor = Integer.toHexString(shapeInfo.getFillColor().toARGB());
2442                googleFillColor = JavaRendererUtilities.ARGBtoABGR(googleFillColor);
2443                kml.append("<color>" + googleFillColor + "</color>");
2444                kml.append("<colorMode>normal</colorMode>");
2445            }
2446            if (fillPattern != null){
2447                kml.append("<shader>" + bitmapToString(fillPattern) + "</shader>");
2448            }
2449
2450            kml.append("<fill>1</fill>");
2451            if (lineColor != null) {
2452                kml.append("<outline>1</outline>");
2453            } else {
2454                kml.append("<outline>0</outline>");
2455            }
2456            kml.append("</PolyStyle>");
2457        }
2458
2459        kml.append("</Style>");
2460
2461        ArrayList shapesArray = shapeInfo.getPolylines();
2462        int len = shapesArray.size();
2463        kml.append("<MultiGeometry>");
2464
2465        for (int i = 0; i < len; i++) {
2466            ArrayList shape = (ArrayList) shapesArray.get(i);
2467            normalize = normalizePoints(shape, ipc);
2468            if (lineColor != null && fillColor == null) {
2469                kml.append("<LineString>");
2470                kml.append("<tessellate>1</tessellate>");
2471                kml.append("<altitudeMode>clampToGround</altitudeMode>");
2472                kml.append("<coordinates>");
2473                int n = shape.size();
2474                //for (int j = 0; j < shape.size(); j++) 
2475                for (int j = 0; j < n; j++) {
2476                    Point2D coord = (Point2D) shape.get(j);
2477                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2478                    if (normalize) {
2479                        geoCoord = NormalizeCoordToGECoord(geoCoord);
2480                    }
2481
2482                    double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2483                    double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2484
2485                    kml.append(longitude);
2486                    kml.append(",");
2487                    kml.append(latitude);
2488                    if(j<shape.size()-1)
2489                        kml.append(" ");
2490                }
2491
2492                kml.append("</coordinates>");
2493                kml.append("</LineString>");
2494            }
2495
2496            if (fillColor != null) {
2497
2498                if (i == 0) {
2499                    kml.append("<Polygon>");
2500                }
2501                //kml.append("<outerBoundaryIs>");
2502                if (i == 1 && len > 1) {
2503                    kml.append("<innerBoundaryIs>");
2504                } else {
2505                    kml.append("<outerBoundaryIs>");
2506                }
2507                kml.append("<LinearRing>");
2508                kml.append("<altitudeMode>clampToGround</altitudeMode>");
2509                kml.append("<tessellate>1</tessellate>");
2510                kml.append("<coordinates>");
2511
2512                //this section is a workaround for a google earth bug. Issue 417 was closed
2513                //for linestrings but they did not fix the smae issue for fills. If Google fixes the issue
2514                //for fills then this section will need to be commented or it will induce an error.
2515                double lastLongitude = Double.MIN_VALUE;
2516                if (normalize == false && IsOnePointSymbolCode(symbolCode)) {
2517                    int n = shape.size();
2518                    //for (int j = 0; j < shape.size(); j++) 
2519                    for (int j = 0; j < n; j++) {
2520                        Point2D coord = (Point2D) shape.get(j);
2521                        Point2D geoCoord = ipc.PixelsToGeo(coord);
2522                        double longitude = geoCoord.getX();
2523                        if (lastLongitude != Double.MIN_VALUE) {
2524                            if (Math.abs(longitude - lastLongitude) > 180d) {
2525                                normalize = true;
2526                                break;
2527                            }
2528                        }
2529                        lastLongitude = longitude;
2530                    }
2531                }
2532                int n = shape.size();
2533                //for (int j = 0; j < shape.size(); j++) 
2534                for (int j = 0; j < n; j++) {
2535                    Point2D coord = (Point2D) shape.get(j);
2536                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2537
2538                    double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2539                    double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2540
2541                    //fix for fill crossing DTL
2542                    if (normalize) {
2543                        if (longitude > 0) {
2544                            longitude -= 360;
2545                        }
2546                    }
2547
2548                    kml.append(longitude);
2549                    kml.append(",");
2550                    kml.append(latitude);
2551                    if(j<shape.size()-1)
2552                        kml.append(" ");
2553                }
2554
2555                kml.append("</coordinates>");
2556                kml.append("</LinearRing>");
2557                if (i == 1 && len > 1) {
2558                    kml.append("</innerBoundaryIs>");
2559                } else {
2560                    kml.append("</outerBoundaryIs>");
2561                }
2562                if (i == len - 1) {
2563                    kml.append("</Polygon>");
2564                }
2565            }
2566        }
2567
2568        kml.append("</MultiGeometry>");
2569        kml.append("</Placemark>");
2570
2571        return kml.toString();
2572    }
2573
2574    /**
2575     * 
2576     * @param shapeInfo
2577     * @param ipc
2578     * @param normalize
2579     * @return
2580     * @deprecated
2581     */
2582    private static ShapeInfo ShapeToWWReady(
2583            ShapeInfo shapeInfo,
2584            IPointConversion ipc,
2585            boolean normalize) {
2586
2587        ArrayList shapesArray = shapeInfo.getPolylines();
2588        int len = shapesArray.size();
2589
2590        for (int i = 0; i < len; i++) {
2591            ArrayList shape = (ArrayList) shapesArray.get(i);
2592
2593            if (shapeInfo.getLineColor() != null) {
2594                int n = shape.size();
2595                //for (int j = 0; j < shape.size(); j++) 
2596                for (int j = 0; j < n; j++) {
2597                    Point2D coord = (Point2D) shape.get(j);
2598                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2599                    //M. Deutch 9-26-11
2600                    if (normalize) {
2601                        geoCoord = NormalizeCoordToGECoord(geoCoord);
2602                    }
2603
2604                    shape.set(j, geoCoord);
2605
2606                }
2607
2608            }
2609
2610            if (shapeInfo.getFillColor() != null) {
2611                int n = shape.size();
2612                //for (int j = 0; j < shape.size(); j++) 
2613                for (int j = 0; j < n; j++) {
2614                    Point2D coord = (Point2D) shape.get(j);
2615                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2616                    //M. Deutch 9-26-11
2617                    //commenting these two lines seems to help with fill not go around the pole
2618                    //if(normalize)
2619                    //geoCoord=NormalizeCoordToGECoord(geoCoord);
2620
2621                    shape.set(j, geoCoord);
2622                }
2623            }
2624        }
2625
2626        return shapeInfo;
2627    }
2628
2629    private static ShapeInfo LabelToWWReady(ShapeInfo shapeInfo,
2630            IPointConversion ipc,
2631            boolean normalize) {
2632
2633        try {
2634            Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2635            Point2D geoCoord = ipc.PixelsToGeo(coord);
2636            //M. Deutch 9-26-11
2637            if (normalize) {
2638                geoCoord = NormalizeCoordToGECoord(geoCoord);
2639            }
2640            double latitude = geoCoord.getY();
2641            double longitude = geoCoord.getX();
2642            long angle = Math.round(shapeInfo.getModifierAngle());
2643
2644            String text = shapeInfo.getModifierString();
2645
2646            if (text != null && text.equals("") == false) {
2647                shapeInfo.setModifierPosition(geoCoord);
2648            } else {
2649                return null;
2650            }
2651        } catch (Exception exc) {
2652            System.err.println(exc.getMessage());
2653            exc.printStackTrace();
2654        }
2655
2656        return shapeInfo;
2657    }
2658
2659    /**
2660     * Google earth centers text on point rather than drawing from that point.
2661     * So we need to adjust the point to where the center of the text would be.
2662     *
2663     * @param modifier
2664     */
2665    private static void AdjustModifierPointToCenter(ShapeInfo modifier) {
2666        AffineTransform at = null;
2667        try {
2668            Rectangle bounds2 = modifier.getTextLayout().getBounds();
2669            Rectangle2D bounds = new Rectangle2D.Double(bounds2.x, bounds2.y, bounds2.width, bounds2.height);
2670        } catch (Exception exc) {
2671            System.err.println(exc.getMessage());
2672            exc.printStackTrace();
2673        }
2674    }
2675
2676    /**
2677     * 
2678     * @param shapeInfo
2679     * @param ipc
2680     * @param geMap
2681     * @param normalize
2682     * @return
2683     * @deprecated
2684     */
2685    private static String ShapeToJSONString(ShapeInfo shapeInfo, IPointConversion ipc, Boolean geMap, boolean normalize) {
2686        StringBuilder JSONed = new StringBuilder();
2687        /*
2688         NOTE: Google Earth / KML colors are backwards.
2689         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2690         * */
2691        String fillColor = null;
2692        String lineColor = null;
2693
2694        if (shapeInfo.getLineColor() != null) {
2695            lineColor = Integer.toHexString(shapeInfo.getLineColor().toARGB());
2696            if (geMap) {
2697                lineColor = JavaRendererUtilities.ARGBtoABGR(lineColor);
2698            }
2699
2700        }
2701        if (shapeInfo.getFillColor() != null) {
2702            fillColor = Integer.toHexString(shapeInfo.getFillColor().toARGB());
2703            if (geMap) {
2704                fillColor = JavaRendererUtilities.ARGBtoABGR(fillColor);
2705            }
2706        }
2707
2708        BasicStroke stroke = null;
2709        stroke = shapeInfo.getStroke();
2710        int lineWidth = 4;
2711
2712        if (stroke != null) {
2713            lineWidth = (int) stroke.getLineWidth();
2714        }
2715
2716        ArrayList shapesArray = shapeInfo.getPolylines();
2717        int n = shapesArray.size();
2718        //for (int i = 0; i < shapesArray.size(); i++) 
2719        for (int i = 0; i < n; i++) {
2720            ArrayList shape = (ArrayList) shapesArray.get(i);
2721
2722            if (fillColor != null) {
2723                JSONed.append("{\"polygon\":[");
2724            } else {
2725                JSONed.append("{\"line\":[");
2726            }
2727
2728            int t = shape.size();
2729            //for (int j = 0; j < shape.size(); j++) 
2730            for (int j = 0; j < t; j++) {
2731                Point2D coord = (Point2D) shape.get(j);
2732                Point2D geoCoord = ipc.PixelsToGeo(coord);
2733                //M. Deutch 9-27-11
2734                if (normalize) {
2735                    geoCoord = NormalizeCoordToGECoord(geoCoord);
2736                }
2737                double latitude = geoCoord.getY();
2738                double longitude = geoCoord.getX();
2739
2740                //diagnostic M. Deutch 10-18-11
2741                //set the point as geo so that the 
2742                coord = new Point2D.Double(longitude, latitude);
2743                shape.set(j, coord);
2744
2745                JSONed.append("[");
2746                JSONed.append(longitude);
2747                JSONed.append(",");
2748                JSONed.append(latitude);
2749                JSONed.append("]");
2750
2751                if (j < (shape.size() - 1)) {
2752                    JSONed.append(",");
2753                }
2754            }
2755
2756            JSONed.append("]");
2757            if (lineColor != null) {
2758                JSONed.append(",\"lineColor\":\"");
2759                JSONed.append(lineColor);
2760
2761                JSONed.append("\"");
2762            }
2763            if (fillColor != null) {
2764                JSONed.append(",\"fillColor\":\"");
2765                JSONed.append(fillColor);
2766                JSONed.append("\"");
2767            }
2768
2769            JSONed.append(",\"lineWidth\":\"");
2770            JSONed.append(String.valueOf(lineWidth));
2771            JSONed.append("\"");
2772
2773            JSONed.append("}");
2774
2775            if (i < (shapesArray.size() - 1)) {
2776                JSONed.append(",");
2777            }
2778        }
2779
2780        return JSONed.toString();
2781    }
2782
2783    private static String LabelToKMLString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize, Color textColor) {
2784        java.lang.StringBuilder kml = new java.lang.StringBuilder();
2785
2786        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2787        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
2788        Point2D geoCoord = ipc.PixelsToGeo(coord);
2789        //M. Deutch 9-26-11
2790        if (normalize) {
2791            geoCoord = NormalizeCoordToGECoord(geoCoord);
2792        }
2793        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2794        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2795        long angle = Math.round(shapeInfo.getModifierAngle());
2796
2797        String text = shapeInfo.getModifierString();
2798
2799        String cdataStart = "<![CDATA[";
2800        String cdataEnd = "]]>";
2801
2802        String color = Integer.toHexString(textColor.toARGB());
2803        color = JavaRendererUtilities.ARGBtoABGR(color);
2804        float kmlScale = RendererSettings.getInstance().getKMLLabelScale();
2805
2806        if (kmlScale > 0 && text != null && text.equals("") == false) {
2807            kml.append("<Placemark>");//("<Placemark id=\"" + id + "_lp" + i + "\">");
2808            kml.append("<name>" + cdataStart + text + cdataEnd + "</name>");
2809            kml.append("<Style>");
2810            kml.append("<IconStyle>");
2811            kml.append("<scale>.7</scale>");
2812            kml.append("<heading>" + angle + "</heading>");
2813            kml.append("<Icon>");
2814            kml.append("<href></href>");
2815            kml.append("</Icon>");
2816            kml.append("</IconStyle>");
2817            kml.append("<LabelStyle>");
2818            kml.append("<color>" + color + "</color>");
2819            kml.append("<scale>" + String.valueOf(kmlScale) +"</scale>");
2820            kml.append("</LabelStyle>");
2821            kml.append("</Style>");
2822            kml.append("<Point>");
2823            kml.append("<extrude>1</extrude>");
2824            kml.append("<altitudeMode>relativeToGround</altitudeMode>");
2825            kml.append("<coordinates>");
2826            kml.append(longitude);
2827            kml.append(",");
2828            kml.append(latitude);
2829            kml.append("</coordinates>");
2830            kml.append("</Point>");
2831            kml.append("</Placemark>");
2832        } else {
2833            return "";
2834        }
2835
2836        return kml.toString();
2837    }
2838
2839    /**
2840     * 
2841     * @param shapeInfo
2842     * @param ipc
2843     * @param normalize
2844     * @return
2845     * @deprecated
2846     */
2847    private static String LabelToJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2848        StringBuilder JSONed = new StringBuilder();
2849        /*
2850         NOTE: Google Earth / KML colors are backwards.
2851         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2852         * */
2853        JSONed.append("{\"label\":");
2854
2855        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2856        Point2D geoCoord = ipc.PixelsToGeo(coord);
2857        if (normalize) {
2858            geoCoord = NormalizeCoordToGECoord(geoCoord);
2859        }
2860        double latitude = geoCoord.getY();
2861        double longitude = geoCoord.getX();
2862        double angle = shapeInfo.getModifierAngle();
2863        coord.setLocation(longitude, latitude);
2864
2865        shapeInfo.setGlyphPosition(coord);
2866
2867        String text = shapeInfo.getModifierString();
2868
2869        if (text != null && text.equals("") == false) {
2870            JSONed.append("[");
2871            JSONed.append(longitude);
2872            JSONed.append(",");
2873            JSONed.append(latitude);
2874            JSONed.append("]");
2875
2876            JSONed.append(",\"text\":\"");
2877            JSONed.append(text);
2878            JSONed.append("\"");
2879
2880            JSONed.append(",\"angle\":\"");
2881            JSONed.append(angle);
2882            JSONed.append("\"}");
2883        } else {
2884            return "";
2885        }
2886
2887        return JSONed.toString();
2888    }
2889
2890    public static String canRenderMultiPoint(String symbolID, Map<String,String> modifiers, int numPoints) {
2891        try {
2892            String basicID = SymbolUtilities.getBasicSymbolID(symbolID);
2893            MSInfo info = MSLookup.getInstance().getMSLInfo(symbolID);
2894
2895            if (info == null) {
2896                if (SymbolID.getVersion(symbolID) == SymbolID.Version_2525E) {
2897                    return"Basic ID: " + basicID + " not recognized in version E (13)";
2898                } else {
2899                    return "Basic ID: " + basicID + " not recognized in version D (11)";
2900                }
2901            }
2902
2903            int drawRule = info.getDrawRule();
2904
2905            if (drawRule == DrawRules.DONOTDRAW) {
2906                return "Basic ID: " + basicID + " has no draw rule";
2907            } else if (!SymbolUtilities.isMultiPoint(symbolID)) {
2908                return "Basic ID: " + basicID + " is not a multipoint symbol";
2909            } else if (numPoints < info.getMinPointCount()) {
2910                return "Basic ID: " + basicID + " requires a minimum of " + String.valueOf(info.getMinPointCount()) + " points. " + String.valueOf(numPoints) + " are present.";
2911            }
2912
2913            //now check for required modifiers
2914            ArrayList<Double> AM = new ArrayList();
2915            ArrayList<Double> AN = new ArrayList();
2916            if (modifiers.containsKey(Modifiers.AM_DISTANCE)) {
2917                String[] amArray = modifiers.get(Modifiers.AM_DISTANCE).split(",");
2918                for (String str : amArray) {
2919                    if (!str.equals("")) {
2920                        AM.add(Double.parseDouble(str));
2921                    }
2922                }
2923            }
2924            if (modifiers.containsKey(Modifiers.AN_AZIMUTH)) {
2925                String[] anArray = modifiers.get(Modifiers.AN_AZIMUTH).split(",");
2926                for (String str : anArray) {
2927                    if (!str.equals("")) {
2928                        AN.add(Double.parseDouble(str));
2929                    }
2930                }
2931            }
2932
2933            return hasRequiredModifiers(symbolID, drawRule, AM, AN);
2934        } catch (Exception exc) {
2935            ErrorLogger.LogException("MultiPointHandler", "canRenderMultiPoint", exc);
2936            return "false: " + exc.getMessage();
2937        }
2938    }
2939
2940    static private String hasRequiredModifiers(String symbolID, int drawRule, ArrayList<Double> AM, ArrayList<Double> AN) {
2941
2942        String message = symbolID;
2943        try {
2944            if (drawRule > 700) {
2945                if (drawRule == DrawRules.CIRCULAR1)
2946                {
2947                    if (AM != null && AM.size() > 0) {
2948                        return "true";
2949                    } else {
2950                        message += " requires a modifiers object that has 1 distance/AM value.";
2951                        return message;
2952                    }
2953                } else if (drawRule == DrawRules.RECTANGULAR2)
2954                {
2955                    if (AM != null && AM.size() >= 2
2956                            && AN != null && AN.size() >= 1) {
2957                        return "true";
2958                    } else {
2959                        message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
2960                        return message;
2961                    }
2962                } else if (drawRule == DrawRules.ARC1)
2963                {
2964                    if (AM != null && AM.size() >= 1
2965                            && AN != null && AN.size() >= 2) {
2966                        return "true";
2967                    } else {
2968                        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.");
2969                        return message;
2970                    }
2971                } else if (drawRule == DrawRules.CIRCULAR2)
2972                {
2973                    if (AM != null && AM.size() > 0) {
2974                        return "true";
2975                    } else {
2976                        message += (" requires a modifiers object that has at least 1 distance/AM value");
2977                        return message;
2978                    }
2979                } else if (drawRule == DrawRules.RECTANGULAR1)
2980                {
2981                    if (AM != null && AM.size() > 0) {
2982                        return "true";
2983                    } else {
2984                        message += (" requires a modifiers object that has 1 distance/AM value.");
2985                        return message;
2986                    }
2987                } else if (drawRule == DrawRules.ELLIPSE1)
2988                {
2989                    if (AM != null && AM.size() >= 2
2990                            && AN != null && AN.size() >= 1) {
2991                        return "true";
2992                    } else {
2993                        message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
2994                        return message;
2995                    }
2996                }
2997                else if (drawRule == DrawRules.RECTANGULAR3)
2998                {
2999                    if (AM != null && AM.size() >= 1) {
3000                        return "true";
3001                    } else {
3002                        message += (" requires a modifiers object that has 1 distance/AM value.");
3003                        return message;
3004                    }
3005                } else {
3006                    //should never get here
3007                    return "true";
3008                }
3009            } else if (drawRule == DrawRules.POINT17) {
3010                if (AM != null && AM.size() >= 2
3011                        && AN != null && AN.size() >= 1) {
3012                    return "true";
3013                } else {
3014                    message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
3015                    return message;
3016                }
3017            } else if (drawRule == DrawRules.POINT18) {
3018                if (AM != null && AM.size() >= 2
3019                        && AN != null && AN.size() >= 2) {
3020                    return "true";
3021                } else {
3022                    message += (" requires a modifiers object that has 2 distance/AM values and 2 azimuth/AN values.");
3023                    return message;
3024                }
3025            } else if (drawRule == DrawRules.CORRIDOR1) {
3026                if (AM != null && AM.size() > 0) {
3027                    return "true";
3028                } else {
3029                    message += (" requires a modifiers object that has 1 distance/AM value.");
3030                    return message;
3031                }
3032            } else {
3033                //no required parameters
3034                return "true";
3035            }
3036        } catch (Exception exc) {
3037            ErrorLogger.LogException("MultiPointHandler", "hasRequiredModifiers", exc);
3038            return "true";
3039        }
3040    }
3041}