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                if (jsonContent.length() > 2)
759                    jsonOutput.append(",");
760                jsonOutput.append("{\"type\": \"Feature\",\"geometry\": { \"type\": \"Polygon\",\"coordinates\": [ ]}");
761
762                jsonOutput.append(",\"properties\":{\"id\":\"");
763                jsonOutput.append(id);
764                jsonOutput.append("\",\"name\":\"");
765                jsonOutput.append(name);
766                jsonOutput.append("\",\"description\":\"");
767                jsonOutput.append(description);
768                jsonOutput.append("\",\"symbolID\":\"");
769                jsonOutput.append(symbolCode);
770                jsonOutput.append("\",\"wasClipped\":\"");
771                jsonOutput.append(String.valueOf(mSymbol.get_WasClipped()));
772                //jsonOutput.append("\"}}");
773
774                jsonOutput.append("\"}}]}");
775            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG) {
776                String textColor = mSymbol.getTextColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextColor(), false) : "";
777                String backgroundColor = mSymbol.getTextBackgroundColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextBackgroundColor(), false) : "";
778                //returns an svg with a geoTL and geoBR value to use to place the canvas on the map
779                jsonContent = MultiPointHandlerSVG.GeoSVGize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, backgroundColor, mSymbol.get_WasClipped());
780                jsonOutput.append(jsonContent);
781            }
782        } catch (Exception exc) {
783            String st = JavaRendererUtilities.getStackTrace(exc);
784            jsonOutput = new StringBuilder();
785            jsonOutput.append("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + ": " + "- ");
786            jsonOutput.append(exc.getMessage() + " - ");
787            jsonOutput.append(st);
788            jsonOutput.append("\"}");
789
790            ErrorLogger.LogException("MultiPointHandler", "RenderSymbol", exc);
791        }
792
793        boolean debug = false;
794        if (debug == true) {
795            System.out.println("Symbol Code: " + symbolCode);
796            System.out.println("Scale: " + scale);
797            System.out.println("BBOX: " + bbox);
798            if (controlPoints != null) {
799                System.out.println("Geo Points: " + controlPoints);
800            }
801            if (tgl != null && tgl.get_Pixels() != null)//pixels != null
802            {
803                System.out.println("Pixel: " + tgl.get_Pixels().toString());
804            }
805            if (bbox != null) {
806                System.out.println("geo bounds: " + bbox);
807            }
808            if (rect != null) {
809                System.out.println("pixel bounds: " + rect.toString());
810            }
811            if (jsonOutput != null) {
812                System.out.println(jsonOutput.toString());
813            }
814        }
815
816        ErrorLogger.LogMessage("MultiPointHandler", "RenderSymbol()", "exit RenderSymbol", Level.FINER);
817        return jsonOutput.toString();
818
819    }
820
821    /**
822     *
823     * @param id
824     * @param name
825     * @param description
826     * @param symbolCode
827     * @param controlPoints
828     * @param scale
829     * @param bbox
830     * @param symbolModifiers
831     * @param symbolAttributes
832     * @return
833     */
834    public static MilStdSymbol RenderSymbolAsMilStdSymbol(String id,
835            String name,
836            String description,
837            String symbolCode,
838            String controlPoints,
839            Double scale,
840            String bbox,
841            Map<String,String> symbolModifiers,
842            Map<String,String> symbolAttributes)//,
843    //ArrayList<ShapeInfo>shapes)
844    {
845        MilStdSymbol mSymbol = null;
846        //System.out.println("MultiPointHandler.RenderSymbol()");
847        boolean normalize = true;
848        Double controlLat = 0.0;
849        Double controlLong = 0.0;
850        //String jsonContent = "";
851
852        Rectangle rect = null;
853
854        //for symbol & line fill
855        ArrayList<POINT2> tgPoints = null;
856
857        String[] coordinates = controlPoints.split(" ");
858        TGLight tgl = new TGLight();
859        ArrayList<ShapeInfo> shapes = null;//new ArrayList<ShapeInfo>();
860        ArrayList<ShapeInfo> modifiers = null;//new ArrayList<ShapeInfo>();
861        //ArrayList<Point2D> pixels = new ArrayList<Point2D>();
862        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
863        int len = coordinates.length;
864
865        IPointConversion ipc = null;
866
867        //Deutch moved section 6-29-11
868        Double left = 0.0;
869        Double right = 0.0;
870        Double top = 0.0;
871        Double bottom = 0.0;
872        Point2D temp = null;
873        Point2D ptGeoUL = null;
874        int width = 0;
875        int height = 0;
876        int leftX = 0;
877        int topY = 0;
878        int bottomY = 0;
879        int rightX = 0;
880        int j = 0;
881        ArrayList<Point2D> bboxCoords = null;
882        if (bbox != null && bbox.equals("") == false) {
883            String[] bounds = null;
884            if (bbox.contains(" "))//trapezoid
885            {
886                bboxCoords = new ArrayList<Point2D>();
887                double x = 0;
888                double y = 0;
889                String[] coords = bbox.split(" ");
890                String[] arrCoord;
891                for (String coord : coords) {
892                    arrCoord = coord.split(",");
893                    x = Double.valueOf(arrCoord[0]);
894                    y = Double.valueOf(arrCoord[1]);
895                    bboxCoords.add(new Point2D.Double(x, y));
896                }
897                //use the upper left corner of the MBR containing geoCoords
898                //to set the converter
899                ptGeoUL = getGeoUL(bboxCoords);
900                left = ptGeoUL.getX();
901                top = ptGeoUL.getY();
902                ipc = new PointConverter(left, top, scale);
903                Point2D ptPixels = null;
904                Point2D ptGeo = null;
905                int n = bboxCoords.size();
906                //for (j = 0; j < bboxCoords.size(); j++) 
907                for (j = 0; j < n; j++) {
908                    ptGeo = bboxCoords.get(j);
909                    ptPixels = ipc.GeoToPixels(ptGeo);
910                    x = ptPixels.getX();
911                    y = ptPixels.getY();
912                    if (x < 20) {
913                        x = 20;
914                    }
915                    if (y < 20) {
916                        y = 20;
917                    }
918                    ptPixels.setLocation(x, y);
919                    //end section
920                    bboxCoords.set(j, (Point2D) ptPixels);
921                }
922            } else//rectangle
923            {
924                bounds = bbox.split(",");
925                left = Double.valueOf(bounds[0]);
926                right = Double.valueOf(bounds[2]);
927                top = Double.valueOf(bounds[3]);
928                bottom = Double.valueOf(bounds[1]);
929                scale = getReasonableScale(bbox, scale);
930                ipc = new PointConverter(left, top, scale);
931            }
932
933            Point2D pt2d = null;
934            if (bboxCoords == null) {
935                pt2d = new Point2D.Double(left, top);
936                temp = ipc.GeoToPixels(pt2d);
937
938                leftX = (int) temp.getX();
939                topY = (int) temp.getY();
940
941                pt2d = new Point2D.Double(right, bottom);
942                temp = ipc.GeoToPixels(pt2d);
943
944                bottomY = (int) temp.getY();
945                rightX = (int) temp.getX();
946                //diagnostic clipping does not work for large scales
947//                if (scale > 10e6) {
948//                    //get widest point in the AOI
949//                    double midLat = 0;
950//                    if (bottom < 0 && top > 0) {
951//                        midLat = 0;
952//                    } else if (bottom < 0 && top < 0) {
953//                        midLat = top;
954//                    } else if (bottom > 0 && top > 0) {
955//                        midLat = bottom;
956//                    }
957//
958//                    temp = ipc.GeoToPixels(new Point2D.Double(right, midLat));
959//                    rightX = (int) temp.getX();
960//                }
961                //end section
962
963                width = (int) Math.abs(rightX - leftX);
964                height = (int) Math.abs(bottomY - topY);
965
966                if(width==0 || height==0)
967                    rect=null;
968                else
969                    rect = new Rectangle(leftX, topY, width, height);
970            }
971        } else {
972            rect = null;
973        }
974        //end section
975
976        //check for required points & parameters
977        String symbolIsValid = canRenderMultiPoint(symbolCode, symbolModifiers, len);
978        if (!symbolIsValid.equals("true")) {
979            ErrorLogger.LogMessage("MultiPointHandler", "RenderSymbolAsMilStdSymbol", symbolIsValid, Level.WARNING);
980            return mSymbol;
981        }
982
983        if (MSLookup.getInstance().getMSLInfo(symbolCode).getDrawRule() != DrawRules.AREA10) // AREA10 can support infinite points
984            len = Math.min(len, MSLookup.getInstance().getMSLInfo(symbolCode).getMaxPointCount());
985        for (int i = 0; i < len; i++) {
986            String[] coordPair = coordinates[i].split(",");
987            Double latitude = Double.valueOf(coordPair[1].trim());
988            Double longitude = Double.valueOf(coordPair[0].trim());
989            geoCoords.add(new Point2D.Double(longitude, latitude));
990        }
991        if (ipc == null) {
992            Point2D ptCoordsUL = getGeoUL(geoCoords);
993            ipc = new PointConverter(ptCoordsUL.getX(), ptCoordsUL.getY(), scale);
994        }
995        //if (crossesIDL(geoCoords) == true) 
996//        if(Math.abs(right-left)>180)
997//        {
998//            normalize = true;
999//            ((PointConverter)ipc).set_normalize(true);
1000//        } 
1001//        else {
1002//            normalize = false;
1003//            ((PointConverter)ipc).set_normalize(false);
1004//        }
1005
1006        //seems to work ok at world view
1007//        if (normalize) {
1008//            NormalizeGECoordsToGEExtents(0, 360, geoCoords);
1009//        }
1010
1011        //M. Deutch 10-3-11
1012        //must shift the rect pixels to synch with the new ipc
1013        //the old ipc was in synch with the bbox, so rect x,y was always 0,0
1014        //the new ipc synchs with the upper left of the geocoords so the boox is shifted
1015        //and therefore the clipping rectangle must shift by the delta x,y between
1016        //the upper left corner of the original bbox and the upper left corner of the geocoords
1017        ArrayList<Point2D> geoCoords2 = new ArrayList<Point2D>();
1018        geoCoords2.add(new Point2D.Double(left, top));
1019        geoCoords2.add(new Point2D.Double(right, bottom));
1020
1021//        if (normalize) {
1022//            NormalizeGECoordsToGEExtents(0, 360, geoCoords2);
1023//        }
1024
1025        //disable clipping
1026        if (ShouldClipSymbol(symbolCode) == false) 
1027            if(crossesIDL(geoCoords)==false)
1028            {
1029                rect = null;
1030                bboxCoords=null;
1031            }
1032        tgl.set_SymbolId(symbolCode);// "GFGPSLA---****X" AMBUSH symbol code
1033        tgl.set_Pixels(null);
1034        
1035        try {
1036
1037            String fillColor = null;
1038            mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
1039
1040//            mSymbol.setUseDashArray(true);
1041
1042            if (symbolModifiers != null || symbolAttributes != null) {
1043                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
1044            } else {
1045                mSymbol.setFillColor(null);
1046            }
1047
1048            if (mSymbol.getFillColor() != null) {
1049                Color fc = mSymbol.getFillColor();
1050                //fillColor = Integer.toHexString(fc.getRGB());                
1051                fillColor = Integer.toHexString(fc.toARGB());
1052            }
1053
1054            if (bboxCoords == null) {
1055                clsRenderer.renderWithPolylines(mSymbol, ipc, rect);
1056            } else {
1057                clsRenderer.renderWithPolylines(mSymbol, ipc, bboxCoords);
1058            }
1059            shapes = mSymbol.getSymbolShapes();
1060            modifiers = mSymbol.getModifierShapes();
1061
1062            //convert points////////////////////////////////////////////////////
1063            ArrayList<ArrayList<Point2D>> polylines = null;
1064            ArrayList<ArrayList<Point2D>> newPolylines = null;
1065            ArrayList<Point2D> newLine = null;
1066            for (ShapeInfo shape : shapes) {
1067                polylines = shape.getPolylines();
1068                //System.out.println("pixel polylines: " + String.valueOf(polylines));
1069                newPolylines = ConvertPolylinePixelsToCoords(polylines, ipc, normalize);
1070                shape.setPolylines(newPolylines);
1071            }
1072
1073            for (ShapeInfo label : modifiers) {
1074                Point2D pixelCoord = label.getModifierPosition();
1075                if (pixelCoord == null) {
1076                    pixelCoord = label.getGlyphPosition();
1077                }
1078                Point2D geoCoord = ipc.PixelsToGeo(pixelCoord);
1079
1080                if (normalize) {
1081                    geoCoord = NormalizeCoordToGECoord(geoCoord);
1082                }
1083
1084                double latitude = geoCoord.getY();
1085                double longitude = geoCoord.getX();
1086                label.setModifierPosition(new Point2D.Double(longitude, latitude));
1087
1088            }   
1089
1090            ////////////////////////////////////////////////////////////////////
1091            mSymbol.setModifierShapes(modifiers);
1092            mSymbol.setSymbolShapes(shapes);
1093
1094        } catch (Exception exc) {
1095            System.out.println(exc.getMessage());
1096            System.out.println("Symbol Code: " + symbolCode);
1097            exc.printStackTrace();
1098        }
1099
1100        boolean debug = false;
1101        if (debug == true) {
1102            System.out.println("Symbol Code: " + symbolCode);
1103            System.out.println("Scale: " + scale);
1104            System.out.println("BBOX: " + bbox);
1105            if (controlPoints != null) {
1106                System.out.println("Geo Points: " + controlPoints);
1107            }
1108            if (tgl != null && tgl.get_Pixels() != null)//pixels != null
1109            {
1110                //System.out.println("Pixel: " + pixels.toString());
1111                System.out.println("Pixel: " + tgl.get_Pixels().toString());
1112            }
1113            if (bbox != null) {
1114                System.out.println("geo bounds: " + bbox);
1115            }
1116            if (rect != null) {
1117                System.out.println("pixel bounds: " + rect.toString());
1118            }
1119        }
1120
1121        return mSymbol;
1122
1123    }
1124
1125    private static ArrayList<ArrayList<Point2D>> ConvertPolylinePixelsToCoords(ArrayList<ArrayList<Point2D>> polylines, IPointConversion ipc, Boolean normalize) {
1126        ArrayList<ArrayList<Point2D>> newPolylines = new ArrayList<ArrayList<Point2D>>();
1127
1128        double latitude = 0;
1129        double longitude = 0;
1130        ArrayList<Point2D> newLine = null;
1131        try {
1132            for (ArrayList<Point2D> line : polylines) {
1133                newLine = new ArrayList<Point2D>();
1134                for (Point2D pt : line) {
1135                    Point2D geoCoord = ipc.PixelsToGeo(pt);
1136
1137                    if (normalize) {
1138                        geoCoord = NormalizeCoordToGECoord(geoCoord);
1139                    }
1140
1141                    latitude = geoCoord.getY();
1142                    longitude = geoCoord.getX();
1143                    newLine.add(new Point2D.Double(longitude, latitude));
1144                }
1145                newPolylines.add(newLine);
1146            }
1147        } catch (Exception exc) {
1148            System.out.println(exc.getMessage());
1149            exc.printStackTrace();
1150        }
1151        return newPolylines;
1152    }
1153
1154    /**
1155     * Multipoint Rendering on flat 2D maps
1156     *
1157     * @param id A unique ID for the symbol. only used in KML currently
1158     * @param name
1159     * @param description
1160     * @param symbolCode
1161     * @param controlPoints
1162     * @param pixelWidth pixel dimensions of the viewable map area
1163     * @param pixelHeight pixel dimensions of the viewable map area
1164     * @param bbox The viewable area of the map. Passed in the format of a
1165     * string "lowerLeftX,lowerLeftY,upperRightX,upperRightY." example:
1166     * "-50.4,23.6,-42.2,24.2"
1167     * @param symbolModifiers Modifier with multiple values should be comma
1168     * delimited
1169     * @param symbolAttributes
1170     * @param format An enumeration: 0 for KML, 1 for JSON.
1171     * @return A JSON or KML string representation of the graphic.
1172     */
1173    public static String RenderSymbol2D(String id,
1174            String name,
1175            String description,
1176            String symbolCode,
1177            String controlPoints,
1178            int pixelWidth,
1179            int pixelHeight,
1180            String bbox,
1181            Map<String,String> symbolModifiers,
1182            Map<String,String> symbolAttributes,
1183            int format) {
1184        StringBuilder jsonOutput = new StringBuilder();
1185        String jsonContent = "";
1186
1187        Rectangle rect = null;
1188
1189        ArrayList<POINT2> tgPoints = null;
1190
1191        String[] coordinates = controlPoints.split(" ");
1192        TGLight tgl = new TGLight();
1193        ArrayList<ShapeInfo> shapes = new ArrayList<ShapeInfo>();
1194        ArrayList<ShapeInfo> modifiers = new ArrayList<ShapeInfo>();
1195        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
1196        int len = coordinates.length;
1197        IPointConversion ipc = null;
1198
1199        //check for required points & parameters
1200        String symbolIsValid = canRenderMultiPoint(symbolCode, symbolModifiers, len);
1201        if (!symbolIsValid.equals("true")) {
1202            String ErrorOutput = "";
1203            ErrorOutput += ("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + " - ID: " + id + " - ");
1204            ErrorOutput += symbolIsValid; //reason for error
1205            ErrorOutput += ("\"}");
1206            ErrorLogger.LogMessage("MultiPointHandler", "RenderSymbol2D", symbolIsValid, Level.WARNING);
1207            return ErrorOutput;
1208        }
1209
1210        Double left = 0.0;
1211        Double right = 0.0;
1212        Double top = 0.0;
1213        Double bottom = 0.0;
1214        if (bbox != null && bbox.equals("") == false) {
1215            String[] bounds = bbox.split(",");
1216
1217            left = Double.valueOf(bounds[0]).doubleValue();
1218            right = Double.valueOf(bounds[2]).doubleValue();
1219            top = Double.valueOf(bounds[3]).doubleValue();
1220            bottom = Double.valueOf(bounds[1]).doubleValue();
1221
1222            ipc = new PointConversion(pixelWidth, pixelHeight, top, left, bottom, right);
1223        } else {
1224            System.out.println("Bad bbox value: " + bbox);
1225            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\"");
1226            return "ERROR - Bad bbox value: " + bbox;
1227        }
1228        //end section
1229
1230        //get coordinates
1231        if (MSLookup.getInstance().getMSLInfo(symbolCode).getDrawRule() != DrawRules.AREA10) // AREA10 can support infinite points
1232            len = Math.min(len, MSLookup.getInstance().getMSLInfo(symbolCode).getMaxPointCount());
1233        for (int i = 0; i < len; i++) {
1234            String[] coordPair = coordinates[i].split(",");
1235            Double latitude = Double.valueOf(coordPair[1].trim()).doubleValue();
1236            Double longitude = Double.valueOf(coordPair[0].trim()).doubleValue();
1237            geoCoords.add(new Point2D.Double(longitude, latitude));
1238        }
1239
1240        try {
1241            MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
1242
1243            if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG){
1244                // Use dash array and hatch pattern fill for SVG output
1245                symbolAttributes.put(MilStdAttributes.UseDashArray, "true");
1246                symbolAttributes.put(MilStdAttributes.UsePatternFill, "true");
1247            }
1248
1249            if (symbolModifiers != null && symbolModifiers.equals("") == false) {
1250                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
1251            } else {
1252                mSymbol.setFillColor(null);
1253            }
1254
1255            //build clipping bounds
1256            Point2D temp = null;
1257            int leftX;
1258            int topY;
1259            int bottomY;
1260            int rightX;
1261            int width;
1262            int height;
1263            boolean normalize = false;
1264//            if(Math.abs(right-left)>180)
1265//            {
1266//                ((PointConversion)ipc).set_normalize(true);                
1267//                normalize=true;
1268//            }
1269//            else      
1270//            {
1271//                ((PointConversion)ipc).set_normalize(false);
1272//            }
1273            if (ShouldClipSymbol(symbolCode)  || crossesIDL(geoCoords)) 
1274            {
1275                Point2D lt=new Point2D.Double(left,top);
1276                //temp = ipc.GeoToPixels(new Point2D.Double(left, top));
1277                temp = ipc.GeoToPixels(lt);
1278                leftX = (int) temp.getX();
1279                topY = (int) temp.getY();
1280
1281                Point2D rb=new Point2D.Double(right,bottom);
1282                //temp = ipc.GeoToPixels(new Point2D.Double(right, bottom));
1283                temp = ipc.GeoToPixels(rb);
1284                bottomY = (int) temp.getY();
1285                rightX = (int) temp.getX();
1286                //////////////////
1287
1288                width = (int) Math.abs(rightX - leftX);
1289                height = (int) Math.abs(bottomY - topY);
1290
1291                rect = new Rectangle(leftX, topY, width, height);
1292            }
1293
1294            //new interface
1295            //IMultiPointRenderer mpr = MultiPointRenderer.getInstance();
1296            clsRenderer.renderWithPolylines(mSymbol, ipc, rect);
1297            shapes = mSymbol.getSymbolShapes();
1298            modifiers = mSymbol.getModifierShapes();
1299
1300            //boolean normalize = false;
1301
1302            if (format == WebRenderer.OUTPUT_FORMAT_JSON) {
1303                jsonOutput.append("{\"type\":\"symbol\",");
1304                //jsonContent = JSONize(shapes, modifiers, ipc, normalize);
1305                jsonOutput.append(jsonContent);
1306                jsonOutput.append("}");
1307            } else if (format == WebRenderer.OUTPUT_FORMAT_KML) {
1308                Color textColor = mSymbol.getTextColor();
1309                if(textColor==null)
1310                    textColor=mSymbol.getLineColor();
1311
1312                jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor);
1313                jsonOutput.append(jsonContent);
1314            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOJSON) {
1315                jsonOutput.append("{\"type\":\"FeatureCollection\",\"features\":");
1316                jsonContent = GeoJSONize(shapes, modifiers, ipc, normalize, mSymbol.getTextColor(), mSymbol.getTextBackgroundColor());
1317                jsonOutput.append(jsonContent);
1318
1319                //moving meta data properties to the last feature with no coords as feature collection doesn't allow properties
1320                jsonOutput.replace(jsonOutput.toString().length()-1,jsonOutput.toString().length(),"" );
1321                if (jsonContent.length() > 2)
1322                    jsonOutput.append(",");
1323                jsonOutput.append("{\"type\": \"Feature\",\"geometry\": { \"type\": \"Polygon\",\"coordinates\": [ ]}");
1324
1325                jsonOutput.append(",\"properties\":{\"id\":\"");
1326                jsonOutput.append(id);
1327                jsonOutput.append("\",\"name\":\"");
1328                jsonOutput.append(name);
1329                jsonOutput.append("\",\"description\":\"");
1330                jsonOutput.append(description);
1331                jsonOutput.append("\",\"symbolID\":\"");
1332                jsonOutput.append(symbolCode);
1333                jsonOutput.append("\",\"wasClipped\":\"");
1334                jsonOutput.append(String.valueOf(mSymbol.get_WasClipped()));
1335                //jsonOutput.append("\"}}");
1336
1337                jsonOutput.append("\"}}]}");
1338
1339            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG) {
1340                String textColor = mSymbol.getTextColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextColor(), false) : "";
1341                String backgroundColor = mSymbol.getTextBackgroundColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextBackgroundColor(), false) : "";
1342                //returns an svg with a geoTL and geoBR value to use to place the canvas on the map
1343                jsonContent = MultiPointHandlerSVG.GeoSVGize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, backgroundColor, mSymbol.get_WasClipped());
1344                jsonOutput.append(jsonContent);
1345            }
1346        } catch (Exception exc) {
1347            jsonOutput = new StringBuilder();
1348            jsonOutput.append("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + ": " + "- ");
1349            jsonOutput.append(exc.getMessage() + " - ");
1350            jsonOutput.append(ErrorLogger.getStackTrace(exc));
1351            jsonOutput.append("\"}");
1352        }
1353
1354        boolean debug = false;
1355        if (debug == true) {
1356            System.out.println("Symbol Code: " + symbolCode);
1357            System.out.println("BBOX: " + bbox);
1358            if (controlPoints != null) {
1359                System.out.println("Geo Points: " + controlPoints);
1360            }
1361            if (tgl != null && tgl.get_Pixels() != null)//pixels != null
1362            {
1363                //System.out.println("Pixel: " + pixels.toString());
1364                System.out.println("Pixel: " + tgl.get_Pixels().toString());
1365            }
1366            if (bbox != null) {
1367                System.out.println("geo bounds: " + bbox);
1368            }
1369            if (rect != null) {
1370                System.out.println("pixel bounds: " + rect.toString());
1371            }
1372            if (jsonOutput != null) {
1373                System.out.println(jsonOutput.toString());
1374            }
1375        }
1376
1377        return jsonOutput.toString();
1378
1379    }
1380
1381    /**
1382     * For Mike Deutch testing
1383     *
1384     * @param id
1385     * @param name
1386     * @param description
1387     * @param symbolCode
1388     * @param controlPoints
1389     * @param pixelWidth
1390     * @param pixelHeight
1391     * @param bbox
1392     * @param symbolModifiers
1393     * @param shapes
1394     * @param modifiers
1395     * @param format
1396     * @return
1397     * @deprecated
1398     */
1399    public static String RenderSymbol2DX(String id,
1400            String name,
1401            String description,
1402            String symbolCode,
1403            String controlPoints,
1404            int pixelWidth,
1405            int pixelHeight,
1406            String bbox,
1407            Map<String,String> symbolModifiers,
1408            Map<String,String> symbolAttributes,
1409            ArrayList<ShapeInfo> shapes,
1410            ArrayList<ShapeInfo> modifiers,
1411            int format)//,
1412    //ArrayList<ShapeInfo>shapes)
1413    {
1414
1415        StringBuilder jsonOutput = new StringBuilder();
1416        String jsonContent = "";
1417
1418        Rectangle rect = null;
1419
1420        String[] coordinates = controlPoints.split(" ");
1421        TGLight tgl = new TGLight();
1422        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
1423        IPointConversion ipc = null;
1424
1425        Double left = 0.0;
1426        Double right = 0.0;
1427        Double top = 0.0;
1428        Double bottom = 0.0;
1429        if (bbox != null && bbox.equals("") == false) {
1430            String[] bounds = bbox.split(",");
1431
1432            left = Double.valueOf(bounds[0]).doubleValue();
1433            right = Double.valueOf(bounds[2]).doubleValue();
1434            top = Double.valueOf(bounds[3]).doubleValue();
1435            bottom = Double.valueOf(bounds[1]).doubleValue();
1436
1437            ipc = new PointConversion(pixelWidth, pixelHeight, top, left, bottom, right);
1438        } else {
1439            System.out.println("Bad bbox value: " + bbox);
1440            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\"");
1441            return "ERROR - Bad bbox value: " + bbox;
1442        }
1443        //end section
1444
1445        //get coordinates
1446        int len = coordinates.length;
1447        for (int i = 0; i < len; i++) {
1448            String[] coordPair = coordinates[i].split(",");
1449            Double latitude = Double.valueOf(coordPair[1].trim()).doubleValue();
1450            Double longitude = Double.valueOf(coordPair[0].trim()).doubleValue();
1451            geoCoords.add(new Point2D.Double(longitude, latitude));
1452        }
1453
1454        try {
1455            MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
1456
1457            if (symbolModifiers != null && symbolModifiers.equals("") == false) {
1458                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
1459            } else {
1460                mSymbol.setFillColor(null);
1461            }
1462
1463            clsRenderer.renderWithPolylines(mSymbol, ipc, rect);
1464            shapes = mSymbol.getSymbolShapes();
1465            modifiers = mSymbol.getModifierShapes();
1466
1467            boolean normalize = false;
1468
1469            if (format == WebRenderer.OUTPUT_FORMAT_JSON) {
1470                jsonOutput.append("{\"type\":\"symbol\",");
1471                jsonContent = JSONize(shapes, modifiers, ipc, false, normalize);
1472                jsonOutput.append(jsonContent);
1473                jsonOutput.append("}");
1474            }
1475
1476        } catch (Exception exc) {
1477            jsonOutput = new StringBuilder();
1478            jsonOutput.append("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + ": " + "- ");
1479            jsonOutput.append(exc.getMessage() + " - ");
1480            jsonOutput.append("\"}");
1481        }
1482
1483        boolean debug = true;
1484        if (debug == true) {
1485            System.out.println("Symbol Code: " + symbolCode);
1486            System.out.println("BBOX: " + bbox);
1487            if (controlPoints != null) {
1488                System.out.println("Geo Points: " + controlPoints);
1489            }
1490            if (tgl != null && tgl.get_Pixels() != null)//pixels != null
1491            {
1492                //System.out.println("Pixel: " + pixels.toString());
1493                System.out.println("Pixel: " + tgl.get_Pixels().toString());
1494            }
1495            if (bbox != null) {
1496                System.out.println("geo bounds: " + bbox);
1497            }
1498            if (rect != null) {
1499                System.out.println("pixel bounds: " + rect.toString());
1500            }
1501            if (jsonOutput != null) {
1502                System.out.println(jsonOutput.toString());
1503            }
1504        }
1505        return jsonOutput.toString();
1506
1507    }
1508
1509    private static SymbolInfo MilStdSymbolToSymbolInfo(MilStdSymbol symbol) {
1510        SymbolInfo si = null;
1511
1512        ArrayList<TextInfo> tiList = new ArrayList<TextInfo>();
1513        ArrayList<LineInfo> liList = new ArrayList<LineInfo>();
1514
1515        TextInfo tiTemp = null;
1516        LineInfo liTemp = null;
1517        ShapeInfo siTemp = null;
1518
1519        ArrayList<ShapeInfo> lines = symbol.getSymbolShapes();
1520        ArrayList<ShapeInfo> modifiers = symbol.getModifierShapes();
1521
1522        int lineCount = lines.size();
1523        int modifierCount = modifiers.size();
1524        for (int i = 0; i < lineCount; i++) {
1525            siTemp = lines.get(i);
1526            if (siTemp.getPolylines() != null) {
1527                liTemp = new LineInfo();
1528                liTemp.setFillColor(siTemp.getFillColor());
1529                liTemp.setLineColor(siTemp.getLineColor());
1530                liTemp.setPolylines(siTemp.getPolylines());
1531                liTemp.setStroke(siTemp.getStroke());
1532                liList.add(liTemp);
1533            }
1534        }
1535
1536        for (int j = 0; j < modifierCount; j++) {
1537            tiTemp = new TextInfo();
1538            siTemp = modifiers.get(j);
1539            if (siTemp.getModifierString() != null) {
1540                tiTemp.setModifierString(siTemp.getModifierString());
1541                tiTemp.setModifierStringPosition(siTemp.getModifierPosition());
1542                tiTemp.setModifierStringAngle(siTemp.getModifierAngle());
1543                tiList.add(tiTemp);
1544            }
1545        }
1546        si = new SymbolInfo(tiList, liList);
1547        return si;
1548    }
1549
1550    /**
1551     * Populates a symbol with the modifiers from a JSON string. This function
1552     * will overwrite any previously populated modifier data.
1553     *
1554     *
1555     *
1556     * @param symbol An existing MilStdSymbol
1557     * @return
1558     */
1559    private static boolean populateModifiers(Map<String,String> saModifiers, Map<String,String> saAttributes, MilStdSymbol symbol) {
1560        Map<String,String> modifiers = new HashMap<>();
1561        Map<String,String> attributes = new HashMap<>();
1562        saAttributes.putAll(attributes);
1563
1564        // Stores array graphic modifiers for MilStdSymbol;
1565        ArrayList<Double> altitudes = null;
1566        ArrayList<Double> azimuths = null;
1567        ArrayList<Double> distances = null;
1568
1569        // Stores colors for symbol.
1570        String fillColor = null;
1571        String lineColor = null;
1572        String textColor = null;
1573        String textBackgroundColor = null;
1574
1575        int lineWidth = 0;
1576        String altMode = null;
1577        boolean useDashArray = symbol.getUseDashArray();
1578        boolean usePatternFill = symbol.getUseFillPattern();
1579        int patternFillType = 0;
1580        boolean hideOptionalLabels = false;
1581        DistanceUnit distanceUnit = null;
1582        DistanceUnit altitudeUnit = null;
1583        int pixelSize = 100;
1584        boolean keepUnitRatio = true;
1585        double patternScale = RendererSettings.getInstance().getPatternScale();
1586
1587        try {
1588
1589            // The following attirubtes are labels.  All of them
1590            // are strings and can be added on the creation of the
1591            // MilStdSymbol by adding to a Map and passing in the
1592            // modifiers parameter.
1593            if (saModifiers != null) {
1594                if (saModifiers.containsKey(Modifiers.C_QUANTITY)) {
1595                    modifiers.put(Modifiers.C_QUANTITY, String.valueOf(saModifiers.get(Modifiers.C_QUANTITY)));
1596                }
1597
1598                if (saModifiers.containsKey(Modifiers.H_ADDITIONAL_INFO_1)) {
1599                    modifiers.put(Modifiers.H_ADDITIONAL_INFO_1, String.valueOf(saModifiers.get(Modifiers.H_ADDITIONAL_INFO_1)));
1600                }
1601
1602                if (saModifiers.containsKey(Modifiers.H1_ADDITIONAL_INFO_2)) {
1603                    modifiers.put(Modifiers.H1_ADDITIONAL_INFO_2, String.valueOf(saModifiers.get(Modifiers.H1_ADDITIONAL_INFO_2)));
1604                }
1605
1606                if (saModifiers.containsKey(Modifiers.H2_ADDITIONAL_INFO_3)) {
1607                    modifiers.put(Modifiers.H2_ADDITIONAL_INFO_3, String.valueOf(saModifiers.get(Modifiers.H2_ADDITIONAL_INFO_3)));
1608                }
1609
1610                if (saModifiers.containsKey(Modifiers.N_HOSTILE)) {
1611                    if (saModifiers.get(Modifiers.N_HOSTILE) == null) {
1612                        modifiers.put(Modifiers.N_HOSTILE, "");
1613                    } else {
1614                        modifiers.put(Modifiers.N_HOSTILE, String.valueOf(saModifiers.get(Modifiers.N_HOSTILE)));
1615                    }
1616                }
1617
1618                if (saModifiers.containsKey(Modifiers.Q_DIRECTION_OF_MOVEMENT)) {
1619                    modifiers.put(Modifiers.Q_DIRECTION_OF_MOVEMENT, String.valueOf(saModifiers.get(Modifiers.Q_DIRECTION_OF_MOVEMENT)));
1620                }
1621
1622                if (saModifiers.containsKey(Modifiers.T_UNIQUE_DESIGNATION_1)) {
1623                    modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, String.valueOf(saModifiers.get(Modifiers.T_UNIQUE_DESIGNATION_1)));
1624                }
1625
1626                if (saModifiers.containsKey(Modifiers.T1_UNIQUE_DESIGNATION_2)) {
1627                    modifiers.put(Modifiers.T1_UNIQUE_DESIGNATION_2, String.valueOf(saModifiers.get(Modifiers.T1_UNIQUE_DESIGNATION_2)));
1628                }
1629
1630                if (saModifiers.containsKey(Modifiers.V_EQUIP_TYPE)) {
1631                    modifiers.put(Modifiers.V_EQUIP_TYPE, String.valueOf(saModifiers.get(Modifiers.V_EQUIP_TYPE)));
1632                }
1633
1634                if (saModifiers.containsKey(Modifiers.AS_COUNTRY)) {
1635                    modifiers.put(Modifiers.AS_COUNTRY, String.valueOf(saModifiers.get(Modifiers.AS_COUNTRY)));
1636                } else if (SymbolID.getCountryCode(symbol.getSymbolID()) > 0 && !GENCLookup.getInstance().get3CharCode(SymbolID.getCountryCode(symbol.getSymbolID())).equals("")) {
1637                    modifiers.put(Modifiers.AS_COUNTRY, GENCLookup.getInstance().get3CharCode(SymbolID.getCountryCode(symbol.getSymbolID())));
1638                }
1639
1640                if (saModifiers.containsKey(Modifiers.AP_TARGET_NUMBER)) {
1641                    modifiers.put(Modifiers.AP_TARGET_NUMBER, String.valueOf(saModifiers.get(Modifiers.AP_TARGET_NUMBER)));
1642                }
1643
1644                if (saModifiers.containsKey(Modifiers.W_DTG_1)) {
1645                    modifiers.put(Modifiers.W_DTG_1, String.valueOf(saModifiers.get(Modifiers.W_DTG_1)));
1646                }
1647
1648                if (saModifiers.containsKey(Modifiers.W1_DTG_2)) {
1649                    modifiers.put(Modifiers.W1_DTG_2, String.valueOf(saModifiers.get(Modifiers.W1_DTG_2)));
1650                }
1651
1652                if (saModifiers.containsKey(Modifiers.Y_LOCATION)) {
1653                    modifiers.put(Modifiers.Y_LOCATION, String.valueOf(saModifiers.get(Modifiers.Y_LOCATION)));
1654                }
1655
1656                //Required multipoint modifier arrays
1657                if (saModifiers.containsKey(Modifiers.X_ALTITUDE_DEPTH)) {
1658                    altitudes = new ArrayList<Double>();
1659                    String[] arrAltitudes = String.valueOf(saModifiers.get(Modifiers.X_ALTITUDE_DEPTH)).split(",");
1660                    for (String x : arrAltitudes) {
1661                        if (x.equals("") != true) {
1662                            altitudes.add(Double.parseDouble(x));
1663                        }
1664                    }
1665                }
1666
1667                if (saModifiers.containsKey(Modifiers.AM_DISTANCE)) {
1668                    distances = new ArrayList<Double>();
1669                    String[] arrDistances = String.valueOf(saModifiers.get(Modifiers.AM_DISTANCE)).split(",");
1670                    for (String am : arrDistances) {
1671                        if (am.equals("") != true) {
1672                            distances.add(Double.parseDouble(am));
1673                        }
1674                    }
1675                }
1676
1677                if (saModifiers.containsKey(Modifiers.AN_AZIMUTH)) {
1678                    azimuths = new ArrayList<Double>();
1679                    String[] arrAzimuths = String.valueOf(saModifiers.get(Modifiers.AN_AZIMUTH)).split(",");;
1680                    for (String an : arrAzimuths) {
1681                        if (an.equals("") != true) {
1682                            azimuths.add(Double.parseDouble(an));
1683                        }
1684                    }
1685                }
1686            }
1687            if (saAttributes != null) {
1688                // These properties are ints, not labels, they are colors.//////////////////
1689                if (saAttributes.containsKey(MilStdAttributes.FillColor)) {
1690                    fillColor = (String) saAttributes.get(MilStdAttributes.FillColor);
1691                }
1692
1693                if (saAttributes.containsKey(MilStdAttributes.LineColor)) {
1694                    lineColor = (String) saAttributes.get(MilStdAttributes.LineColor);
1695                }
1696
1697                if (saAttributes.containsKey(MilStdAttributes.LineWidth)) {
1698                    lineWidth = Integer.parseInt(saAttributes.get(MilStdAttributes.LineWidth));
1699                }
1700                
1701                if (saAttributes.containsKey(MilStdAttributes.TextColor)) {
1702                    textColor = (String) saAttributes.get(MilStdAttributes.TextColor);
1703                }
1704                
1705                if (saAttributes.containsKey(MilStdAttributes.TextBackgroundColor)) {
1706                    textBackgroundColor = (String) saAttributes.get(MilStdAttributes.TextBackgroundColor);
1707                }
1708
1709                if (saAttributes.containsKey(MilStdAttributes.AltitudeMode)) {
1710                    altMode = saAttributes.get(MilStdAttributes.AltitudeMode);
1711                }
1712
1713                if (saAttributes.containsKey(MilStdAttributes.UseDashArray)) {
1714                    useDashArray = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.UseDashArray));
1715                }
1716
1717                if (saAttributes.containsKey(MilStdAttributes.UsePatternFill)) {
1718                    usePatternFill = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.UsePatternFill));
1719                }
1720
1721                if (saAttributes.containsKey(MilStdAttributes.PatternFillType)) {
1722                    patternFillType = Integer.parseInt((saAttributes.get(MilStdAttributes.PatternFillType)));
1723                }
1724
1725                if (saAttributes.containsKey(MilStdAttributes.HideOptionalLabels)) {
1726                    hideOptionalLabels = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.HideOptionalLabels));
1727                }
1728
1729                if(saAttributes.containsKey(MilStdAttributes.AltitudeUnits)) {
1730                    altitudeUnit = DistanceUnit.parse(saAttributes.get(MilStdAttributes.AltitudeUnits));
1731                }
1732
1733                if(saAttributes.containsKey(MilStdAttributes.DistanceUnits)) {
1734                    distanceUnit = DistanceUnit.parse(saAttributes.get(MilStdAttributes.DistanceUnits));
1735                }
1736
1737                if(saAttributes.containsKey(MilStdAttributes.PixelSize)) {
1738                    pixelSize = Integer.parseInt(saAttributes.get(MilStdAttributes.PixelSize));
1739                    symbol.setUnitSize(pixelSize);
1740                }
1741
1742                if (saAttributes.containsKey(MilStdAttributes.KeepUnitRatio)) {
1743                    keepUnitRatio = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.KeepUnitRatio));
1744                    symbol.setKeepUnitRatio(keepUnitRatio);
1745                }
1746
1747                if(saAttributes.containsKey(MilStdAttributes.PatternScale)) {
1748                    patternScale = Double.parseDouble(saAttributes.get(MilStdAttributes.PatternScale));
1749                }
1750            }
1751
1752            symbol.setModifierMap(modifiers);
1753
1754            if (fillColor != null && fillColor.equals("") == false) {
1755                symbol.setFillColor(RendererUtilities.getColorFromHexString(fillColor));
1756            } 
1757
1758            if (lineColor != null && lineColor.equals("") == false) {
1759                symbol.setLineColor(RendererUtilities.getColorFromHexString(lineColor));
1760                symbol.setTextColor(RendererUtilities.getColorFromHexString(lineColor));
1761            }
1762            else if(symbol.getLineColor()==null)
1763                symbol.setLineColor(Color.black);
1764
1765            if (lineWidth > 0) {
1766                symbol.setLineWidth(lineWidth);
1767            }
1768            
1769            if (textColor != null && textColor.equals("") == false) {
1770                symbol.setTextColor(RendererUtilities.getColorFromHexString(textColor));
1771            } else if(symbol.getTextColor()==null)
1772                symbol.setTextColor(Color.black);
1773                
1774            if (textBackgroundColor != null && textBackgroundColor.equals("") == false) {
1775                symbol.setTextBackgroundColor(RendererUtilities.getColorFromHexString(textBackgroundColor));
1776            }
1777
1778            if (altMode != null) {
1779                symbol.setAltitudeMode(altMode);
1780            }
1781
1782            symbol.setUseDashArray(useDashArray);
1783            symbol.setUseFillPattern(usePatternFill);
1784            symbol.setHideOptionalLabels(hideOptionalLabels);
1785            symbol.setAltitudeUnit(altitudeUnit);
1786            symbol.setDistanceUnit(distanceUnit);
1787            symbol.setPatternScale(patternScale);
1788
1789            // Check grpahic modifiers variables.  If we set earlier, populate
1790            // the fields, otherwise, ignore.
1791            if (altitudes != null) {
1792                symbol.setModifiers_AM_AN_X(Modifiers.X_ALTITUDE_DEPTH, altitudes);
1793            }
1794            if (distances != null) {
1795                symbol.setModifiers_AM_AN_X(Modifiers.AM_DISTANCE, distances);
1796            }
1797
1798            if (azimuths != null) {
1799                symbol.setModifiers_AM_AN_X(Modifiers.AN_AZIMUTH, azimuths);
1800            }
1801
1802            //Check if sector range fan has required min range
1803            if (SymbolUtilities.getBasicSymbolID(symbol.getSymbolID()).equals("25242200")) {
1804                if (symbol.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH) != null
1805                        && symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE) != null) {
1806                    int anCount = symbol.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH).size();
1807                    int amCount = symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE).size();
1808                    ArrayList<Double> am = null;
1809                    if (amCount < ((anCount / 2) + 1)) {
1810                        am = symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
1811                        if (am.get(0) != 0.0) {
1812                            am.add(0, 0.0);
1813                        }
1814                    }
1815                }
1816            }
1817        } catch (Exception exc2) {
1818            Log.e("MPH.populateModifiers", exc2.getMessage(), exc2);
1819        }
1820        return true;
1821
1822    }
1823
1824    private static String KMLize(String id, String name,
1825            String description,
1826            String symbolCode,
1827            ArrayList<ShapeInfo> shapes,
1828            ArrayList<ShapeInfo> modifiers,
1829            IPointConversion ipc,
1830            boolean normalize, Color textColor) {
1831
1832        java.lang.StringBuilder kml = new java.lang.StringBuilder();
1833
1834        ShapeInfo tempModifier = null;
1835
1836        String cdataStart = "<![CDATA[";
1837        String cdataEnd = "]]>";
1838
1839        int len = shapes.size();
1840        kml.append("<Folder id=\"" + id + "\">");
1841        kml.append("<name>" + cdataStart + name + cdataEnd + "</name>");
1842        kml.append("<visibility>1</visibility>");
1843        for (int i = 0; i < len; i++) {
1844
1845            String shapesToAdd = ShapeToKMLString(name, description, symbolCode, shapes.get(i), ipc, normalize);
1846            kml.append(shapesToAdd);
1847        }
1848
1849        int len2 = modifiers.size();
1850
1851        for (int j = 0; j < len2; j++) {
1852
1853            tempModifier = modifiers.get(j);
1854
1855            //if(geMap)//if using google earth
1856            //assume kml text is going to be centered
1857            //AdjustModifierPointToCenter(tempModifier);
1858
1859            String labelsToAdd = LabelToKMLString(tempModifier, ipc, normalize, textColor);
1860            kml.append(labelsToAdd);
1861        }
1862
1863        kml.append("</Folder>");
1864        return kml.toString();
1865    }
1866
1867    /**
1868     * 
1869     * @param shapes
1870     * @param modifiers
1871     * @param ipc
1872     * @param geMap
1873     * @param normalize
1874     * @return 
1875     * @deprecated Use GeoJSONize()
1876     */
1877    private static String JSONize(ArrayList<ShapeInfo> shapes, ArrayList<ShapeInfo> modifiers, IPointConversion ipc, Boolean geMap, boolean normalize) {
1878        String polygons = "";
1879        String lines = "";
1880        String labels = "";
1881        String jstr = "";
1882        ShapeInfo tempModifier = null;
1883
1884        int len = shapes.size();
1885        for (int i = 0; i < len; i++) {
1886            if (jstr.length() > 0) {
1887                jstr += ",";
1888            }
1889            String shapesToAdd = ShapeToJSONString(shapes.get(i), ipc, geMap, normalize);
1890            if (shapesToAdd.length() > 0) {
1891                if (shapesToAdd.startsWith("line", 2)) {
1892                    if (lines.length() > 0) {
1893                        lines += ",";
1894                    }
1895
1896                    lines += shapesToAdd;
1897                } else if (shapesToAdd.startsWith("polygon", 2)) {
1898                    if (polygons.length() > 0) {
1899                        polygons += ",";
1900                    }
1901
1902                    polygons += shapesToAdd;
1903                }
1904            }
1905        }
1906
1907        jstr += "\"polygons\": [" + polygons + "],"
1908                + "\"lines\": [" + lines + "],";
1909        int len2 = modifiers.size();
1910        labels = "";
1911        for (int j = 0; j < len2; j++) {
1912            tempModifier = modifiers.get(j);
1913            if (geMap) {
1914                AdjustModifierPointToCenter(tempModifier);
1915            }
1916            String labelsToAdd = LabelToJSONString(tempModifier, ipc, normalize);
1917            if (labelsToAdd.length() > 0) {
1918                if (labels.length() > 0) {
1919                    labels += ",";
1920                }
1921
1922                labels += labelsToAdd;
1923
1924            }
1925        }
1926        jstr += "\"labels\": [" + labels + "]";
1927        return jstr;
1928    }
1929
1930    private static Color getIdealTextBackgroundColor(Color fgColor) {
1931        //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "in function", Level.SEVERE);
1932        try {
1933            //an array of three elements containing the
1934            //hue, saturation, and brightness (in that order),
1935            //of the color with the indicated red, green, and blue components/
1936            float hsbvals[] = new float[3];
1937
1938            if (fgColor != null) {/*
1939                 Color.RGBtoHSB(fgColor.getRed(), fgColor.getGreen(), fgColor.getBlue(), hsbvals);
1940
1941                 if(hsbvals != null)
1942                 {
1943                 //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "length: " + String.valueOf(hsbvals.length));
1944                 //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "H: " + String.valueOf(hsbvals[0]) + " S: " + String.valueOf(hsbvals[1]) + " B: " + String.valueOf(hsbvals[2]),Level.SEVERE);
1945                 if(hsbvals[2] > 0.6)
1946                 return Color.BLACK;
1947                 else
1948                 return Color.WHITE;
1949                 }*/
1950
1951                int nThreshold = RendererSettings.getInstance().getTextBackgroundAutoColorThreshold();//160;
1952                int bgDelta = (int) ((fgColor.getRed() * 0.299) + (fgColor.getGreen() * 0.587) + (fgColor.getBlue() * 0.114));
1953                //ErrorLogger.LogMessage("bgDelta: " + String.valueOf(255-bgDelta));
1954                //if less than threshold, black, otherwise white.
1955                //return (255 - bgDelta < nThreshold) ? Color.BLACK : Color.WHITE;//new Color(0, 0, 0, fgColor.getAlpha())
1956                return (255 - bgDelta < nThreshold) ? new Color(0, 0, 0, fgColor.getAlpha()) : new Color(255, 255, 255, fgColor.getAlpha());
1957            }
1958        } catch (Exception exc) {
1959            ErrorLogger.LogException("SymbolDraw", "getIdealtextBGColor", exc);
1960        }
1961        return Color.WHITE;
1962    }
1963
1964    private static String LabelToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize, Color textColor, Color textBackgroundColor) {
1965
1966        StringBuilder JSONed = new StringBuilder();
1967        StringBuilder properties = new StringBuilder();
1968        StringBuilder geometry = new StringBuilder();
1969
1970        Color outlineColor = getIdealTextBackgroundColor(textColor);
1971        if(textBackgroundColor != null)
1972                outlineColor = textBackgroundColor;
1973
1974        //AffineTransform at = shapeInfo.getAffineTransform();
1975        //Point2D coord = (Point2D)new Point2D.Double(at.getTranslateX(), at.getTranslateY());
1976        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
1977        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
1978        Point2D geoCoord = ipc.PixelsToGeo(coord);
1979        //M. Deutch 9-27-11
1980        if (normalize) {
1981            geoCoord = NormalizeCoordToGECoord(geoCoord);
1982        }
1983        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
1984        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
1985        double angle = shapeInfo.getModifierAngle();
1986        coord.setLocation(longitude, latitude);
1987
1988        //diagnostic M. Deutch 10-18-11
1989        shapeInfo.setGlyphPosition(coord);
1990
1991        String text = shapeInfo.getModifierString();
1992        
1993        int justify=shapeInfo.getTextJustify();
1994        String strJustify="left";
1995        if(justify==0)
1996            strJustify="left";
1997        else if(justify==1)
1998            strJustify="center";
1999        else if(justify==2)
2000            strJustify="right";
2001
2002        
2003        RendererSettings RS = RendererSettings.getInstance();
2004
2005        if (text != null && text.equals("") == false) {
2006
2007            JSONed.append("{\"type\":\"Feature\",\"properties\":{\"label\":\"");
2008            JSONed.append(text);
2009            JSONed.append("\",\"pointRadius\":0,\"fontColor\":\"");
2010            JSONed.append(RendererUtilities.colorToHexString(textColor, false));
2011            JSONed.append("\",\"fontSize\":\"");
2012            JSONed.append(String.valueOf(RS.getMPLabelFontSize()) + "pt\"");
2013            JSONed.append(",\"fontFamily\":\"");
2014            JSONed.append(RS.getMPLabelFontName());
2015            JSONed.append(", sans-serif");
2016
2017            if (RS.getMPLabelFontType() == Typeface.BOLD) {
2018                JSONed.append("\",\"fontWeight\":\"bold\"");
2019            } else {
2020                JSONed.append("\",\"fontWeight\":\"normal\"");
2021            }
2022
2023            //JSONed.append(",\"labelAlign\":\"lm\"");
2024            JSONed.append(",\"labelAlign\":\"");
2025            JSONed.append(strJustify);
2026            JSONed.append("\",\"labelBaseline\":\"alphabetic\"");
2027            JSONed.append(",\"labelXOffset\":0");
2028            JSONed.append(",\"labelYOffset\":0");
2029            JSONed.append(",\"labelOutlineColor\":\"");
2030            JSONed.append(RendererUtilities.colorToHexString(outlineColor, false));
2031            JSONed.append("\",\"labelOutlineWidth\":");
2032            JSONed.append("4");
2033            JSONed.append(",\"rotation\":");
2034            JSONed.append(angle);
2035            JSONed.append(",\"angle\":");
2036            JSONed.append(angle);
2037            JSONed.append("},");
2038
2039            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
2040            JSONed.append(longitude);
2041            JSONed.append(",");
2042            JSONed.append(latitude);
2043            JSONed.append("]");
2044            JSONed.append("}}");
2045
2046        } else {
2047            return "";
2048        }
2049
2050        return JSONed.toString();
2051    }
2052
2053    private static String ShapeToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2054        StringBuilder JSONed = new StringBuilder();
2055        StringBuilder properties = new StringBuilder();
2056        StringBuilder geometry = new StringBuilder();
2057        String geometryType = null;
2058        String sda = null;
2059        /*
2060         NOTE: Google Earth / KML colors are backwards.
2061         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2062         * */
2063        Color lineColor = shapeInfo.getLineColor();
2064        Color fillColor = shapeInfo.getFillColor();
2065
2066        if (shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_FILL || fillColor != null || shapeInfo.getPatternFillImage() != null) {
2067            geometryType = "\"Polygon\"";
2068        } else //if(shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_POLYLINE)
2069        {
2070            geometryType = "\"MultiLineString\"";
2071        }
2072
2073        BasicStroke stroke = null;
2074        stroke = shapeInfo.getStroke();
2075        int lineWidth = 4;
2076
2077        if (stroke != null) {
2078            lineWidth = (int) stroke.getLineWidth();
2079            //lineWidth++;
2080            //System.out.println("lineWidth: " + String.valueOf(lineWidth));
2081        }
2082
2083        //generate JSON properties for feature
2084        properties.append("\"properties\":{");
2085        properties.append("\"label\":\"\",");
2086        if (lineColor != null) {
2087            properties.append("\"strokeColor\":\"" + RendererUtilities.colorToHexString(lineColor, false) + "\",");
2088            properties.append("\"lineOpacity\":" + String.valueOf(lineColor.getAlpha() / 255f) + ",");
2089        }
2090        if (fillColor != null) {
2091            properties.append("\"fillColor\":\"" + RendererUtilities.colorToHexString(fillColor, false) + "\",");
2092            properties.append("\"fillOpacity\":" + String.valueOf(fillColor.getAlpha() / 255f) + ",");
2093        }
2094        if (shapeInfo.getPatternFillImage() != null) {
2095            properties.append("\"fillPattern\":\"" + bitmapToString(shapeInfo.getPatternFillImage()) + "\",");
2096        }
2097        if(stroke.getDashArray() != null)
2098        {
2099            float[] arrSDA = stroke.getDashArray();
2100            sda = "[";
2101            sda += String.valueOf(arrSDA[0]);
2102            if(arrSDA.length > 1)
2103            {
2104                for(int i = 1; i < arrSDA.length; i++)
2105                {
2106                    sda = sda + ", " + String.valueOf(arrSDA[i]);
2107                }
2108            }
2109            sda += "]";
2110            sda = "\"strokeDasharray\":" + sda + ",";
2111            properties.append(sda);
2112        }
2113
2114        int lineCap = stroke.getEndCap();
2115        properties.append("\"lineCap\":" + lineCap + ",");
2116
2117        String strokeWidth = String.valueOf(lineWidth);
2118        properties.append("\"strokeWidth\":" + strokeWidth + ",");
2119        properties.append("\"strokeWeight\":" + strokeWidth + "");
2120        properties.append("},");
2121
2122
2123        properties.append("\"style\":{");
2124        if (lineColor != null) {
2125            properties.append("\"stroke\":\"" + RendererUtilities.colorToHexString(lineColor, false) + "\",");
2126            properties.append("\"line-opacity\":" + String.valueOf(lineColor.getAlpha() / 255f) + ",");
2127        }
2128        if (fillColor != null) {
2129            properties.append("\"fill\":\"" + RendererUtilities.colorToHexString(fillColor, false) + "\",");
2130            properties.append("\"fill-opacity\":" + String.valueOf(fillColor.getAlpha() / 255f) + ",");
2131        }
2132        if(stroke.getDashArray() != null)
2133        {
2134            float[] da = stroke.getDashArray();
2135            sda = String.valueOf(da[0]);
2136            if(da.length > 1)
2137            {
2138                for(int i = 1; i < da.length; i++)
2139                {
2140                    sda = sda + " " + String.valueOf(da[i]);
2141                }
2142            }
2143            sda = "\"stroke-dasharray\":\"" + sda + "\",";
2144            properties.append(sda);
2145            sda = null;
2146        }
2147
2148        if(lineCap == BasicStroke.CAP_SQUARE)
2149            properties.append("\"stroke-linecap\":\"square\",");
2150        else if(lineCap == BasicStroke.CAP_ROUND)
2151            properties.append("\"stroke-linecap\":\"round\",");
2152        else if(lineCap == BasicStroke.CAP_BUTT)
2153            properties.append("\"stroke-linecap\":\"butt\",");
2154
2155        strokeWidth = String.valueOf(lineWidth);
2156        properties.append("\"stroke-width\":" + strokeWidth);
2157        properties.append("}");
2158
2159
2160        //generate JSON geometry for feature
2161        geometry.append("\"geometry\":{\"type\":");
2162        geometry.append(geometryType);
2163        geometry.append(",\"coordinates\":[");
2164
2165        ArrayList shapesArray = shapeInfo.getPolylines();
2166
2167        for (int i = 0; i < shapesArray.size(); i++) {
2168            ArrayList pointList = (ArrayList) shapesArray.get(i);
2169
2170            normalize = normalizePoints(pointList, ipc);
2171
2172            geometry.append("[");
2173
2174            //System.out.println("Pixel Coords:");
2175            for (int j = 0; j < pointList.size(); j++) {
2176                Point2D coord = (Point2D) pointList.get(j);
2177                Point2D geoCoord = ipc.PixelsToGeo(coord);
2178                //M. Deutch 9-27-11
2179                if (normalize) {
2180                    geoCoord = NormalizeCoordToGECoord(geoCoord);
2181                }
2182                double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2183                double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2184
2185                //fix for fill crossing DTL
2186                if (normalize && fillColor != null) {
2187                    if (longitude > 0) {
2188                        longitude -= 360;
2189                    }
2190                }
2191
2192                //diagnostic M. Deutch 10-18-11
2193                //set the point as geo so that the 
2194                //coord.setLocation(longitude, latitude);
2195                coord = new Point2D.Double(longitude, latitude);
2196                pointList.set(j, coord);
2197                //end section
2198
2199                geometry.append("[");
2200                geometry.append(longitude);
2201                geometry.append(",");
2202                geometry.append(latitude);
2203                geometry.append("]");
2204
2205                if (j < (pointList.size() - 1)) {
2206                    geometry.append(",");
2207                }
2208            }
2209
2210            geometry.append("]");
2211
2212            if (i < (shapesArray.size() - 1)) {
2213                geometry.append(",");
2214            }
2215        }
2216        geometry.append("]}");
2217
2218        JSONed.append("{\"type\":\"Feature\",");
2219        JSONed.append(properties.toString());
2220        JSONed.append(",");
2221        JSONed.append(geometry.toString());
2222        JSONed.append("}");
2223
2224        return JSONed.toString();
2225    }
2226
2227    private static String ImageToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2228
2229        StringBuilder JSONed = new StringBuilder();
2230        StringBuilder properties = new StringBuilder();
2231        StringBuilder geometry = new StringBuilder();
2232
2233        //AffineTransform at = shapeInfo.getAffineTransform();
2234        //Point2D coord = (Point2D)new Point2D.Double(at.getTranslateX(), at.getTranslateY());
2235        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2236        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
2237        Point2D geoCoord = ipc.PixelsToGeo(coord);
2238        //M. Deutch 9-27-11
2239        if (normalize) {
2240            geoCoord = NormalizeCoordToGECoord(geoCoord);
2241        }
2242        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2243        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2244        double angle = shapeInfo.getModifierAngle();
2245        coord.setLocation(longitude, latitude);
2246
2247        //diagnostic M. Deutch 10-18-11
2248        shapeInfo.setGlyphPosition(coord);
2249
2250        Bitmap image = shapeInfo.getModifierImage();
2251
2252        RendererSettings RS = RendererSettings.getInstance();
2253
2254        if (image != null) {
2255
2256            JSONed.append("{\"type\":\"Feature\",\"properties\":{\"image\":\"");
2257            JSONed.append(bitmapToString(image));
2258            JSONed.append("\",\"rotation\":");
2259            JSONed.append(angle);
2260            JSONed.append(",\"angle\":");
2261            JSONed.append(angle);
2262            JSONed.append("},");
2263            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
2264            JSONed.append(longitude);
2265            JSONed.append(",");
2266            JSONed.append(latitude);
2267            JSONed.append("]");
2268            JSONed.append("}}");
2269
2270        } else {
2271            return "";
2272        }
2273
2274        return JSONed.toString();
2275    }
2276
2277    protected static String bitmapToString(Bitmap bitmap) {
2278        final int COMPRESSION_QUALITY = 100;
2279        String encodedImage;
2280        ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream();
2281        bitmap.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY,
2282                byteArrayBitmapStream);
2283        byte[] b = byteArrayBitmapStream.toByteArray();
2284        encodedImage = Base64.encodeToString(b, Base64.DEFAULT);
2285        return "data:image/png;base64," + encodedImage;
2286    }
2287
2288    private static String GeoJSONize(ArrayList<ShapeInfo> shapes, ArrayList<ShapeInfo> modifiers, IPointConversion ipc, boolean normalize, Color textColor, Color textBackgroundColor) {
2289
2290        String jstr = "";
2291        ShapeInfo tempModifier = null;
2292        StringBuilder fc = new StringBuilder();//JSON feature collection
2293
2294        fc.append("[");
2295
2296        int len = shapes.size();
2297        for (int i = 0; i < len; i++) {
2298
2299            String shapesToAdd = ShapeToGeoJSONString(shapes.get(i), ipc, normalize);
2300            if (shapesToAdd.length() > 0) {
2301                fc.append(shapesToAdd);
2302                if (i < len - 1) {
2303                    fc.append(",");
2304                }
2305            }
2306        }
2307
2308        int len2 = modifiers.size();
2309
2310        for (int j = 0; j < len2; j++) {
2311            tempModifier = modifiers.get(j);
2312
2313            String modifiersToAdd = null;
2314            if(modifiers.get(j).getModifierImage() != null) {
2315                modifiersToAdd = ImageToGeoJSONString(tempModifier, ipc, normalize);
2316            } else {
2317                modifiersToAdd = LabelToGeoJSONString(tempModifier, ipc, normalize, textColor, textBackgroundColor);
2318            }
2319            if (modifiersToAdd.length() > 0) {
2320                if (fc.length() > 1)
2321                    fc.append(",");
2322                fc.append(modifiersToAdd);
2323            }
2324        }
2325        fc.append("]");
2326        String GeoJSON = fc.toString();
2327        return GeoJSON;
2328    }
2329
2330    /**
2331     * 
2332     * @param shapes
2333     * @param modifiers
2334     * @param ipc
2335     * @param normalize
2336     * @deprecated
2337     */
2338    private static void MakeWWReady(
2339            ArrayList<ShapeInfo> shapes,
2340            ArrayList<ShapeInfo> modifiers,
2341            IPointConversion ipc,
2342            boolean normalize) {
2343        ShapeInfo temp = null;
2344        int len = shapes.size();
2345        for (int i = 0; i < len; i++) {
2346
2347            temp = ShapeToWWReady(shapes.get(i), ipc, normalize);
2348            shapes.set(i, temp);
2349
2350        }
2351
2352        int len2 = modifiers.size();
2353        ShapeInfo tempModifier = null;
2354        for (int j = 0; j < len2; j++) {
2355
2356            tempModifier = modifiers.get(j);
2357
2358            //Do we need this for World Wind?
2359            tempModifier = LabelToWWReady(tempModifier, ipc, normalize);
2360            modifiers.set(j, tempModifier);
2361
2362        }
2363
2364    }
2365
2366    private static Boolean normalizePoints(ArrayList<Point2D.Double> shape, IPointConversion ipc) {
2367        ArrayList geoCoords = new ArrayList();
2368        int n = shape.size();
2369        //for (int j = 0; j < shape.size(); j++) 
2370        for (int j = 0; j < n; j++) {
2371            Point2D coord = shape.get(j);
2372            Point2D geoCoord = ipc.PixelsToGeo(coord);
2373            geoCoord = NormalizeCoordToGECoord(geoCoord);
2374            double latitude = geoCoord.getY();
2375            double longitude = geoCoord.getX();
2376            Point2D pt2d = new Point2D.Double(longitude, latitude);
2377            geoCoords.add(pt2d);
2378        }
2379        Boolean normalize = crossesIDL(geoCoords);
2380        return normalize;
2381    }
2382
2383    private static Boolean IsOnePointSymbolCode(String symbolCode) {
2384        String basicCode = SymbolUtilities.getBasicSymbolID(symbolCode);
2385        //TODO: Revisit for basic shapes
2386        //some airspaces affected
2387        if (symbolCode.equals("CAKE-----------")) {
2388            return true;
2389        } else if (symbolCode.equals("CYLINDER-------")) {
2390            return true;
2391        } else if (symbolCode.equals("RADARC---------")) {
2392            return true;
2393        }
2394
2395        return false;
2396    }
2397
2398    private static String ShapeToKMLString(String name,
2399            String description,
2400            String symbolCode,
2401            ShapeInfo shapeInfo,
2402            IPointConversion ipc,
2403            boolean normalize) {
2404
2405        java.lang.StringBuilder kml = new java.lang.StringBuilder();
2406
2407        Color lineColor = null;
2408        Color fillColor = null;
2409        String googleLineColor = null;
2410        String googleFillColor = null;
2411
2412        //String lineStyleId = "lineColor";
2413
2414        BasicStroke stroke = null;
2415        int lineWidth = 4;
2416
2417        symbolCode = JavaRendererUtilities.normalizeSymbolCode(symbolCode);
2418
2419        String cdataStart = "<![CDATA[";
2420        String cdataEnd = "]]>";
2421
2422        kml.append("<Placemark>");//("<Placemark id=\"" + id + "_mg" + "\">");
2423        kml.append("<description>" + cdataStart + "<b>" + name + "</b><br/>" + "\n" + description + cdataEnd + "</description>");
2424        //kml.append("<Style id=\"" + lineStyleId + "\">");
2425        kml.append("<Style>");
2426
2427        lineColor = shapeInfo.getLineColor();
2428        if (lineColor != null) {
2429            googleLineColor = Integer.toHexString(shapeInfo.getLineColor().toARGB());
2430
2431            stroke = shapeInfo.getStroke();
2432
2433            if (stroke != null) {
2434                lineWidth = (int) stroke.getLineWidth();
2435            }
2436
2437            googleLineColor = JavaRendererUtilities.ARGBtoABGR(googleLineColor);
2438
2439            kml.append("<LineStyle>");
2440            kml.append("<color>" + googleLineColor + "</color>");
2441            kml.append("<colorMode>normal</colorMode>");
2442            kml.append("<width>" + String.valueOf(lineWidth) + "</width>");
2443            kml.append("</LineStyle>");
2444        }
2445
2446        fillColor = shapeInfo.getFillColor();
2447        Bitmap fillPattern = shapeInfo.getPatternFillImage();
2448        if (fillColor != null || fillPattern != null) {
2449            kml.append("<PolyStyle>");
2450
2451            if (fillColor != null) {
2452                googleFillColor = Integer.toHexString(shapeInfo.getFillColor().toARGB());
2453                googleFillColor = JavaRendererUtilities.ARGBtoABGR(googleFillColor);
2454                kml.append("<color>" + googleFillColor + "</color>");
2455                kml.append("<colorMode>normal</colorMode>");
2456            }
2457            if (fillPattern != null){
2458                kml.append("<shader>" + bitmapToString(fillPattern) + "</shader>");
2459            }
2460
2461            kml.append("<fill>1</fill>");
2462            if (lineColor != null) {
2463                kml.append("<outline>1</outline>");
2464            } else {
2465                kml.append("<outline>0</outline>");
2466            }
2467            kml.append("</PolyStyle>");
2468        }
2469
2470        kml.append("</Style>");
2471
2472        ArrayList shapesArray = shapeInfo.getPolylines();
2473        int len = shapesArray.size();
2474        kml.append("<MultiGeometry>");
2475
2476        for (int i = 0; i < len; i++) {
2477            ArrayList shape = (ArrayList) shapesArray.get(i);
2478            normalize = normalizePoints(shape, ipc);
2479            if (lineColor != null && fillColor == null) {
2480                kml.append("<LineString>");
2481                kml.append("<tessellate>1</tessellate>");
2482                kml.append("<altitudeMode>clampToGround</altitudeMode>");
2483                kml.append("<coordinates>");
2484                int n = shape.size();
2485                //for (int j = 0; j < shape.size(); j++) 
2486                for (int j = 0; j < n; j++) {
2487                    Point2D coord = (Point2D) shape.get(j);
2488                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2489                    if (normalize) {
2490                        geoCoord = NormalizeCoordToGECoord(geoCoord);
2491                    }
2492
2493                    double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2494                    double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2495
2496                    kml.append(longitude);
2497                    kml.append(",");
2498                    kml.append(latitude);
2499                    if(j<shape.size()-1)
2500                        kml.append(" ");
2501                }
2502
2503                kml.append("</coordinates>");
2504                kml.append("</LineString>");
2505            }
2506
2507            if (fillColor != null) {
2508
2509                if (i == 0) {
2510                    kml.append("<Polygon>");
2511                }
2512                //kml.append("<outerBoundaryIs>");
2513                if (i == 1 && len > 1) {
2514                    kml.append("<innerBoundaryIs>");
2515                } else {
2516                    kml.append("<outerBoundaryIs>");
2517                }
2518                kml.append("<LinearRing>");
2519                kml.append("<altitudeMode>clampToGround</altitudeMode>");
2520                kml.append("<tessellate>1</tessellate>");
2521                kml.append("<coordinates>");
2522
2523                //this section is a workaround for a google earth bug. Issue 417 was closed
2524                //for linestrings but they did not fix the smae issue for fills. If Google fixes the issue
2525                //for fills then this section will need to be commented or it will induce an error.
2526                double lastLongitude = Double.MIN_VALUE;
2527                if (normalize == false && IsOnePointSymbolCode(symbolCode)) {
2528                    int n = shape.size();
2529                    //for (int j = 0; j < shape.size(); j++) 
2530                    for (int j = 0; j < n; j++) {
2531                        Point2D coord = (Point2D) shape.get(j);
2532                        Point2D geoCoord = ipc.PixelsToGeo(coord);
2533                        double longitude = geoCoord.getX();
2534                        if (lastLongitude != Double.MIN_VALUE) {
2535                            if (Math.abs(longitude - lastLongitude) > 180d) {
2536                                normalize = true;
2537                                break;
2538                            }
2539                        }
2540                        lastLongitude = longitude;
2541                    }
2542                }
2543                int n = shape.size();
2544                //for (int j = 0; j < shape.size(); j++) 
2545                for (int j = 0; j < n; j++) {
2546                    Point2D coord = (Point2D) shape.get(j);
2547                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2548
2549                    double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2550                    double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2551
2552                    //fix for fill crossing DTL
2553                    if (normalize) {
2554                        if (longitude > 0) {
2555                            longitude -= 360;
2556                        }
2557                    }
2558
2559                    kml.append(longitude);
2560                    kml.append(",");
2561                    kml.append(latitude);
2562                    if(j<shape.size()-1)
2563                        kml.append(" ");
2564                }
2565
2566                kml.append("</coordinates>");
2567                kml.append("</LinearRing>");
2568                if (i == 1 && len > 1) {
2569                    kml.append("</innerBoundaryIs>");
2570                } else {
2571                    kml.append("</outerBoundaryIs>");
2572                }
2573                if (i == len - 1) {
2574                    kml.append("</Polygon>");
2575                }
2576            }
2577        }
2578
2579        kml.append("</MultiGeometry>");
2580        kml.append("</Placemark>");
2581
2582        return kml.toString();
2583    }
2584
2585    /**
2586     * 
2587     * @param shapeInfo
2588     * @param ipc
2589     * @param normalize
2590     * @return
2591     * @deprecated
2592     */
2593    private static ShapeInfo ShapeToWWReady(
2594            ShapeInfo shapeInfo,
2595            IPointConversion ipc,
2596            boolean normalize) {
2597
2598        ArrayList shapesArray = shapeInfo.getPolylines();
2599        int len = shapesArray.size();
2600
2601        for (int i = 0; i < len; i++) {
2602            ArrayList shape = (ArrayList) shapesArray.get(i);
2603
2604            if (shapeInfo.getLineColor() != null) {
2605                int n = shape.size();
2606                //for (int j = 0; j < shape.size(); j++) 
2607                for (int j = 0; j < n; j++) {
2608                    Point2D coord = (Point2D) shape.get(j);
2609                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2610                    //M. Deutch 9-26-11
2611                    if (normalize) {
2612                        geoCoord = NormalizeCoordToGECoord(geoCoord);
2613                    }
2614
2615                    shape.set(j, geoCoord);
2616
2617                }
2618
2619            }
2620
2621            if (shapeInfo.getFillColor() != null) {
2622                int n = shape.size();
2623                //for (int j = 0; j < shape.size(); j++) 
2624                for (int j = 0; j < n; j++) {
2625                    Point2D coord = (Point2D) shape.get(j);
2626                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2627                    //M. Deutch 9-26-11
2628                    //commenting these two lines seems to help with fill not go around the pole
2629                    //if(normalize)
2630                    //geoCoord=NormalizeCoordToGECoord(geoCoord);
2631
2632                    shape.set(j, geoCoord);
2633                }
2634            }
2635        }
2636
2637        return shapeInfo;
2638    }
2639
2640    private static ShapeInfo LabelToWWReady(ShapeInfo shapeInfo,
2641            IPointConversion ipc,
2642            boolean normalize) {
2643
2644        try {
2645            Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2646            Point2D geoCoord = ipc.PixelsToGeo(coord);
2647            //M. Deutch 9-26-11
2648            if (normalize) {
2649                geoCoord = NormalizeCoordToGECoord(geoCoord);
2650            }
2651            double latitude = geoCoord.getY();
2652            double longitude = geoCoord.getX();
2653            long angle = Math.round(shapeInfo.getModifierAngle());
2654
2655            String text = shapeInfo.getModifierString();
2656
2657            if (text != null && text.equals("") == false) {
2658                shapeInfo.setModifierPosition(geoCoord);
2659            } else {
2660                return null;
2661            }
2662        } catch (Exception exc) {
2663            System.err.println(exc.getMessage());
2664            exc.printStackTrace();
2665        }
2666
2667        return shapeInfo;
2668    }
2669
2670    /**
2671     * Google earth centers text on point rather than drawing from that point.
2672     * So we need to adjust the point to where the center of the text would be.
2673     *
2674     * @param modifier
2675     */
2676    private static void AdjustModifierPointToCenter(ShapeInfo modifier) {
2677        AffineTransform at = null;
2678        try {
2679            Rectangle bounds2 = modifier.getTextLayout().getBounds();
2680            Rectangle2D bounds = new Rectangle2D.Double(bounds2.x, bounds2.y, bounds2.width, bounds2.height);
2681        } catch (Exception exc) {
2682            System.err.println(exc.getMessage());
2683            exc.printStackTrace();
2684        }
2685    }
2686
2687    /**
2688     * 
2689     * @param shapeInfo
2690     * @param ipc
2691     * @param geMap
2692     * @param normalize
2693     * @return
2694     * @deprecated
2695     */
2696    private static String ShapeToJSONString(ShapeInfo shapeInfo, IPointConversion ipc, Boolean geMap, boolean normalize) {
2697        StringBuilder JSONed = new StringBuilder();
2698        /*
2699         NOTE: Google Earth / KML colors are backwards.
2700         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2701         * */
2702        String fillColor = null;
2703        String lineColor = null;
2704
2705        if (shapeInfo.getLineColor() != null) {
2706            lineColor = Integer.toHexString(shapeInfo.getLineColor().toARGB());
2707            if (geMap) {
2708                lineColor = JavaRendererUtilities.ARGBtoABGR(lineColor);
2709            }
2710
2711        }
2712        if (shapeInfo.getFillColor() != null) {
2713            fillColor = Integer.toHexString(shapeInfo.getFillColor().toARGB());
2714            if (geMap) {
2715                fillColor = JavaRendererUtilities.ARGBtoABGR(fillColor);
2716            }
2717        }
2718
2719        BasicStroke stroke = null;
2720        stroke = shapeInfo.getStroke();
2721        int lineWidth = 4;
2722
2723        if (stroke != null) {
2724            lineWidth = (int) stroke.getLineWidth();
2725        }
2726
2727        ArrayList shapesArray = shapeInfo.getPolylines();
2728        int n = shapesArray.size();
2729        //for (int i = 0; i < shapesArray.size(); i++) 
2730        for (int i = 0; i < n; i++) {
2731            ArrayList shape = (ArrayList) shapesArray.get(i);
2732
2733            if (fillColor != null) {
2734                JSONed.append("{\"polygon\":[");
2735            } else {
2736                JSONed.append("{\"line\":[");
2737            }
2738
2739            int t = shape.size();
2740            //for (int j = 0; j < shape.size(); j++) 
2741            for (int j = 0; j < t; j++) {
2742                Point2D coord = (Point2D) shape.get(j);
2743                Point2D geoCoord = ipc.PixelsToGeo(coord);
2744                //M. Deutch 9-27-11
2745                if (normalize) {
2746                    geoCoord = NormalizeCoordToGECoord(geoCoord);
2747                }
2748                double latitude = geoCoord.getY();
2749                double longitude = geoCoord.getX();
2750
2751                //diagnostic M. Deutch 10-18-11
2752                //set the point as geo so that the 
2753                coord = new Point2D.Double(longitude, latitude);
2754                shape.set(j, coord);
2755
2756                JSONed.append("[");
2757                JSONed.append(longitude);
2758                JSONed.append(",");
2759                JSONed.append(latitude);
2760                JSONed.append("]");
2761
2762                if (j < (shape.size() - 1)) {
2763                    JSONed.append(",");
2764                }
2765            }
2766
2767            JSONed.append("]");
2768            if (lineColor != null) {
2769                JSONed.append(",\"lineColor\":\"");
2770                JSONed.append(lineColor);
2771
2772                JSONed.append("\"");
2773            }
2774            if (fillColor != null) {
2775                JSONed.append(",\"fillColor\":\"");
2776                JSONed.append(fillColor);
2777                JSONed.append("\"");
2778            }
2779
2780            JSONed.append(",\"lineWidth\":\"");
2781            JSONed.append(String.valueOf(lineWidth));
2782            JSONed.append("\"");
2783
2784            JSONed.append("}");
2785
2786            if (i < (shapesArray.size() - 1)) {
2787                JSONed.append(",");
2788            }
2789        }
2790
2791        return JSONed.toString();
2792    }
2793
2794    private static String LabelToKMLString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize, Color textColor) {
2795        java.lang.StringBuilder kml = new java.lang.StringBuilder();
2796
2797        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2798        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
2799        Point2D geoCoord = ipc.PixelsToGeo(coord);
2800        //M. Deutch 9-26-11
2801        if (normalize) {
2802            geoCoord = NormalizeCoordToGECoord(geoCoord);
2803        }
2804        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2805        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2806        long angle = Math.round(shapeInfo.getModifierAngle());
2807
2808        String text = shapeInfo.getModifierString();
2809
2810        String cdataStart = "<![CDATA[";
2811        String cdataEnd = "]]>";
2812
2813        String color = Integer.toHexString(textColor.toARGB());
2814        color = JavaRendererUtilities.ARGBtoABGR(color);
2815        float kmlScale = RendererSettings.getInstance().getKMLLabelScale();
2816
2817        if (kmlScale > 0 && text != null && text.equals("") == false) {
2818            kml.append("<Placemark>");//("<Placemark id=\"" + id + "_lp" + i + "\">");
2819            kml.append("<name>" + cdataStart + text + cdataEnd + "</name>");
2820            kml.append("<Style>");
2821            kml.append("<IconStyle>");
2822            kml.append("<scale>" + kmlScale + "</scale>");
2823            kml.append("<heading>" + angle + "</heading>");
2824            kml.append("<Icon>");
2825            kml.append("<href></href>");
2826            kml.append("</Icon>");
2827            kml.append("</IconStyle>");
2828            kml.append("<LabelStyle>");
2829            kml.append("<color>" + color + "</color>");
2830            kml.append("<scale>" + String.valueOf(kmlScale) +"</scale>");
2831            kml.append("</LabelStyle>");
2832            kml.append("</Style>");
2833            kml.append("<Point>");
2834            kml.append("<extrude>1</extrude>");
2835            kml.append("<altitudeMode>relativeToGround</altitudeMode>");
2836            kml.append("<coordinates>");
2837            kml.append(longitude);
2838            kml.append(",");
2839            kml.append(latitude);
2840            kml.append("</coordinates>");
2841            kml.append("</Point>");
2842            kml.append("</Placemark>");
2843        } else {
2844            return "";
2845        }
2846
2847        return kml.toString();
2848    }
2849
2850    /**
2851     * 
2852     * @param shapeInfo
2853     * @param ipc
2854     * @param normalize
2855     * @return
2856     * @deprecated
2857     */
2858    private static String LabelToJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2859        StringBuilder JSONed = new StringBuilder();
2860        /*
2861         NOTE: Google Earth / KML colors are backwards.
2862         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2863         * */
2864        JSONed.append("{\"label\":");
2865
2866        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2867        Point2D geoCoord = ipc.PixelsToGeo(coord);
2868        if (normalize) {
2869            geoCoord = NormalizeCoordToGECoord(geoCoord);
2870        }
2871        double latitude = geoCoord.getY();
2872        double longitude = geoCoord.getX();
2873        double angle = shapeInfo.getModifierAngle();
2874        coord.setLocation(longitude, latitude);
2875
2876        shapeInfo.setGlyphPosition(coord);
2877
2878        String text = shapeInfo.getModifierString();
2879
2880        if (text != null && text.equals("") == false) {
2881            JSONed.append("[");
2882            JSONed.append(longitude);
2883            JSONed.append(",");
2884            JSONed.append(latitude);
2885            JSONed.append("]");
2886
2887            JSONed.append(",\"text\":\"");
2888            JSONed.append(text);
2889            JSONed.append("\"");
2890
2891            JSONed.append(",\"angle\":\"");
2892            JSONed.append(angle);
2893            JSONed.append("\"}");
2894        } else {
2895            return "";
2896        }
2897
2898        return JSONed.toString();
2899    }
2900
2901    public static String canRenderMultiPoint(String symbolID, Map<String,String> modifiers, int numPoints) {
2902        try {
2903            String basicID = SymbolUtilities.getBasicSymbolID(symbolID);
2904            MSInfo info = MSLookup.getInstance().getMSLInfo(symbolID);
2905
2906            if (info == null) {
2907                if (SymbolID.getVersion(symbolID) >= SymbolID.Version_2525E) {
2908                    return"Basic ID: " + basicID + " not recognized in version E (15)";
2909                } else {
2910                    return "Basic ID: " + basicID + " not recognized in version D (11)";
2911                }
2912            }
2913
2914            int drawRule = info.getDrawRule();
2915
2916            if (drawRule == DrawRules.DONOTDRAW) {
2917                return "Basic ID: " + basicID + " has no draw rule";
2918            } else if (!SymbolUtilities.isMultiPoint(symbolID)) {
2919                return "Basic ID: " + basicID + " is not a multipoint symbol";
2920            } else if (numPoints < info.getMinPointCount()) {
2921                return "Basic ID: " + basicID + " requires a minimum of " + String.valueOf(info.getMinPointCount()) + " points. " + String.valueOf(numPoints) + " are present.";
2922            }
2923
2924            //now check for required modifiers
2925            ArrayList<Double> AM = new ArrayList();
2926            ArrayList<Double> AN = new ArrayList();
2927            if (modifiers.containsKey(Modifiers.AM_DISTANCE)) {
2928                String[] amArray = modifiers.get(Modifiers.AM_DISTANCE).split(",");
2929                for (String str : amArray) {
2930                    if (!str.equals("")) {
2931                        AM.add(Double.parseDouble(str));
2932                    }
2933                }
2934            }
2935            if (modifiers.containsKey(Modifiers.AN_AZIMUTH)) {
2936                String[] anArray = modifiers.get(Modifiers.AN_AZIMUTH).split(",");
2937                for (String str : anArray) {
2938                    if (!str.equals("")) {
2939                        AN.add(Double.parseDouble(str));
2940                    }
2941                }
2942            }
2943
2944            return hasRequiredModifiers(symbolID, drawRule, AM, AN);
2945        } catch (Exception exc) {
2946            ErrorLogger.LogException("MultiPointHandler", "canRenderMultiPoint", exc);
2947            return "false: " + exc.getMessage();
2948        }
2949    }
2950
2951    static private String hasRequiredModifiers(String symbolID, int drawRule, ArrayList<Double> AM, ArrayList<Double> AN) {
2952
2953        String message = symbolID;
2954        try {
2955            if (drawRule > 700) {
2956                if (drawRule == DrawRules.CIRCULAR1)
2957                {
2958                    if (AM != null && AM.size() > 0) {
2959                        return "true";
2960                    } else {
2961                        message += " requires a modifiers object that has 1 distance/AM value.";
2962                        return message;
2963                    }
2964                } else if (drawRule == DrawRules.RECTANGULAR2)
2965                {
2966                    if (AM != null && AM.size() >= 2
2967                            && AN != null && AN.size() >= 1) {
2968                        return "true";
2969                    } else {
2970                        message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
2971                        return message;
2972                    }
2973                } else if (drawRule == DrawRules.ARC1)
2974                {
2975                    if (AM != null && AM.size() >= 1
2976                            && AN != null && AN.size() >= 2) {
2977                        return "true";
2978                    } else {
2979                        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.");
2980                        return message;
2981                    }
2982                } else if (drawRule == DrawRules.CIRCULAR2)
2983                {
2984                    if (AM != null && AM.size() > 0) {
2985                        return "true";
2986                    } else {
2987                        message += (" requires a modifiers object that has at least 1 distance/AM value");
2988                        return message;
2989                    }
2990                } else if (drawRule == DrawRules.RECTANGULAR1)
2991                {
2992                    if (AM != null && AM.size() > 0) {
2993                        return "true";
2994                    } else {
2995                        message += (" requires a modifiers object that has 1 distance/AM value.");
2996                        return message;
2997                    }
2998                } else if (drawRule == DrawRules.ELLIPSE1)
2999                {
3000                    if (AM != null && AM.size() >= 2
3001                            && AN != null && AN.size() >= 1) {
3002                        return "true";
3003                    } else {
3004                        message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
3005                        return message;
3006                    }
3007                }
3008                else if (drawRule == DrawRules.RECTANGULAR3)
3009                {
3010                    if (AM != null && AM.size() >= 1) {
3011                        return "true";
3012                    } else {
3013                        message += (" requires a modifiers object that has 1 distance/AM value.");
3014                        return message;
3015                    }
3016                } else {
3017                    //should never get here
3018                    return "true";
3019                }
3020            } else if (drawRule == DrawRules.POINT17) {
3021                if (AM != null && AM.size() >= 2
3022                        && AN != null && AN.size() >= 1) {
3023                    return "true";
3024                } else {
3025                    message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
3026                    return message;
3027                }
3028            } else if (drawRule == DrawRules.POINT18) {
3029                if (AM != null && AM.size() >= 2
3030                        && AN != null && AN.size() >= 2) {
3031                    return "true";
3032                } else {
3033                    message += (" requires a modifiers object that has 2 distance/AM values and 2 azimuth/AN values.");
3034                    return message;
3035                }
3036            } else if (drawRule == DrawRules.CORRIDOR1) {
3037                if (AM != null && AM.size() > 0) {
3038                    return "true";
3039                } else {
3040                    message += (" requires a modifiers object that has 1 distance/AM value.");
3041                    return message;
3042                }
3043            } else {
3044                //no required parameters
3045                return "true";
3046            }
3047        } catch (Exception exc) {
3048            ErrorLogger.LogException("MultiPointHandler", "hasRequiredModifiers", exc);
3049            return "true";
3050        }
3051    }
3052
3053    /**
3054     *
3055     * @param id
3056     * @param name
3057     * @param description
3058     * @param basicShapeType
3059     * @param controlPoints
3060     * @param scale
3061     * @param bbox
3062     * @param symbolModifiers
3063     * @param symbolAttributes
3064     * @return
3065     */
3066    public static MilStdSymbol RenderBasicShapeAsMilStdSymbol(String id,
3067                                                          String name,
3068                                                          String description,
3069                                                          int basicShapeType,
3070                                                          String controlPoints,
3071                                                          Double scale,
3072                                                          String bbox,
3073                                                          Map<String,String> symbolModifiers,
3074                                                          Map<String,String> symbolAttributes)
3075    {
3076        MilStdSymbol mSymbol = null;
3077        boolean normalize = true;
3078        Double controlLat = 0.0;
3079        Double controlLong = 0.0;
3080        //String jsonContent = "";
3081
3082        Rectangle rect = null;
3083
3084        //for symbol & line fill
3085        ArrayList<POINT2> tgPoints = null;
3086
3087        String[] coordinates = controlPoints.split(" ");
3088        ArrayList<ShapeInfo> shapes = null;//new ArrayList<ShapeInfo>();
3089        ArrayList<ShapeInfo> modifiers = null;//new ArrayList<ShapeInfo>();
3090        //ArrayList<Point2D> pixels = new ArrayList<Point2D>();
3091        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
3092        int len = coordinates.length;
3093
3094        IPointConversion ipc = null;
3095
3096        //Deutch moved section 6-29-11
3097        Double left = 0.0;
3098        Double right = 0.0;
3099        Double top = 0.0;
3100        Double bottom = 0.0;
3101        Point2D temp = null;
3102        Point2D ptGeoUL = null;
3103        int width = 0;
3104        int height = 0;
3105        int leftX = 0;
3106        int topY = 0;
3107        int bottomY = 0;
3108        int rightX = 0;
3109        int j = 0;
3110        ArrayList<Point2D> bboxCoords = null;
3111        if (bbox != null && bbox.equals("") == false) {
3112            String[] bounds = null;
3113            if (bbox.contains(" "))//trapezoid
3114            {
3115                bboxCoords = new ArrayList<Point2D>();
3116                double x = 0;
3117                double y = 0;
3118                String[] coords = bbox.split(" ");
3119                String[] arrCoord;
3120                for (String coord : coords) {
3121                    arrCoord = coord.split(",");
3122                    x = Double.valueOf(arrCoord[0]);
3123                    y = Double.valueOf(arrCoord[1]);
3124                    bboxCoords.add(new Point2D.Double(x, y));
3125                }
3126                //use the upper left corner of the MBR containing geoCoords
3127                //to set the converter
3128                ptGeoUL = getGeoUL(bboxCoords);
3129                left = ptGeoUL.getX();
3130                top = ptGeoUL.getY();
3131                ipc = new PointConverter(left, top, scale);
3132                Point2D ptPixels = null;
3133                Point2D ptGeo = null;
3134                int n = bboxCoords.size();
3135                //for (j = 0; j < bboxCoords.size(); j++)
3136                for (j = 0; j < n; j++) {
3137                    ptGeo = bboxCoords.get(j);
3138                    ptPixels = ipc.GeoToPixels(ptGeo);
3139                    x = ptPixels.getX();
3140                    y = ptPixels.getY();
3141                    if (x < 20) {
3142                        x = 20;
3143                    }
3144                    if (y < 20) {
3145                        y = 20;
3146                    }
3147                    ptPixels.setLocation(x, y);
3148                    //end section
3149                    bboxCoords.set(j, (Point2D) ptPixels);
3150                }
3151            } else//rectangle
3152            {
3153                bounds = bbox.split(",");
3154                left = Double.valueOf(bounds[0]);
3155                right = Double.valueOf(bounds[2]);
3156                top = Double.valueOf(bounds[3]);
3157                bottom = Double.valueOf(bounds[1]);
3158                scale = getReasonableScale(bbox, scale);
3159                ipc = new PointConverter(left, top, scale);
3160            }
3161
3162            Point2D pt2d = null;
3163            if (bboxCoords == null) {
3164                pt2d = new Point2D.Double(left, top);
3165                temp = ipc.GeoToPixels(pt2d);
3166
3167                leftX = (int) temp.getX();
3168                topY = (int) temp.getY();
3169
3170                pt2d = new Point2D.Double(right, bottom);
3171                temp = ipc.GeoToPixels(pt2d);
3172
3173                bottomY = (int) temp.getY();
3174                rightX = (int) temp.getX();
3175                //diagnostic clipping does not work for large scales
3176//                if (scale > 10e6) {
3177//                    //get widest point in the AOI
3178//                    double midLat = 0;
3179//                    if (bottom < 0 && top > 0) {
3180//                        midLat = 0;
3181//                    } else if (bottom < 0 && top < 0) {
3182//                        midLat = top;
3183//                    } else if (bottom > 0 && top > 0) {
3184//                        midLat = bottom;
3185//                    }
3186//
3187//                    temp = ipc.GeoToPixels(new Point2D.Double(right, midLat));
3188//                    rightX = (int) temp.getX();
3189//                }
3190                //end section
3191
3192                width = (int) Math.abs(rightX - leftX);
3193                height = (int) Math.abs(bottomY - topY);
3194
3195                if(width==0 || height==0)
3196                    rect=null;
3197                else
3198                    rect = new Rectangle(leftX, topY, width, height);
3199            }
3200        } else {
3201            rect = null;
3202        }
3203        //end section
3204
3205        for (int i = 0; i < len; i++) {
3206            String[] coordPair = coordinates[i].split(",");
3207            Double latitude = Double.valueOf(coordPair[1].trim());
3208            Double longitude = Double.valueOf(coordPair[0].trim());
3209            geoCoords.add(new Point2D.Double(longitude, latitude));
3210        }
3211        if (ipc == null) {
3212            Point2D ptCoordsUL = getGeoUL(geoCoords);
3213            ipc = new PointConverter(ptCoordsUL.getX(), ptCoordsUL.getY(), scale);
3214        }
3215        //if (crossesIDL(geoCoords) == true)
3216//        if(Math.abs(right-left)>180)
3217//        {
3218//            normalize = true;
3219//            ((PointConverter)ipc).set_normalize(true);
3220//        }
3221//        else {
3222//            normalize = false;
3223//            ((PointConverter)ipc).set_normalize(false);
3224//        }
3225
3226        //seems to work ok at world view
3227//        if (normalize) {
3228//            NormalizeGECoordsToGEExtents(0, 360, geoCoords);
3229//        }
3230
3231        //M. Deutch 10-3-11
3232        //must shift the rect pixels to synch with the new ipc
3233        //the old ipc was in synch with the bbox, so rect x,y was always 0,0
3234        //the new ipc synchs with the upper left of the geocoords so the boox is shifted
3235        //and therefore the clipping rectangle must shift by the delta x,y between
3236        //the upper left corner of the original bbox and the upper left corner of the geocoords
3237        ArrayList<Point2D> geoCoords2 = new ArrayList<Point2D>();
3238        geoCoords2.add(new Point2D.Double(left, top));
3239        geoCoords2.add(new Point2D.Double(right, bottom));
3240
3241//        if (normalize) {
3242//            NormalizeGECoordsToGEExtents(0, 360, geoCoords2);
3243//        }
3244
3245        //disable clipping
3246        if (crossesIDL(geoCoords) == false) {
3247            rect = null;
3248            bboxCoords = null;
3249        }
3250
3251        String symbolCode = "";
3252        try {
3253            String fillColor = null;
3254            mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
3255
3256//            mSymbol.setUseDashArray(true);
3257
3258            if (symbolModifiers != null || symbolAttributes != null) {
3259                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
3260            } else {
3261                mSymbol.setFillColor(null);
3262            }
3263
3264            if (mSymbol.getFillColor() != null) {
3265                Color fc = mSymbol.getFillColor();
3266                //fillColor = Integer.toHexString(fc.getRGB());
3267                fillColor = Integer.toHexString(fc.toARGB());
3268            }
3269
3270            TGLight tg = clsRenderer.createTGLightFromMilStdSymbolBasicShape(mSymbol, ipc, basicShapeType);
3271            ArrayList<ShapeInfo> shapeInfos = new ArrayList();
3272            ArrayList<ShapeInfo> modifierShapeInfos = new ArrayList();
3273            Object clipArea;
3274            if (bboxCoords == null) {
3275                clipArea = rect;
3276            } else {
3277                clipArea = bboxCoords;
3278            }
3279            if (clsRenderer.intersectsClipArea(tg, ipc, clipArea)) {
3280                clsRenderer.render_GE(tg, shapeInfos, modifierShapeInfos, ipc, clipArea);
3281            }
3282            mSymbol.setSymbolShapes(shapeInfos);
3283            mSymbol.setModifierShapes(modifierShapeInfos);
3284            mSymbol.set_WasClipped(tg.get_WasClipped());
3285            shapes = mSymbol.getSymbolShapes();
3286            modifiers = mSymbol.getModifierShapes();
3287
3288            //convert points////////////////////////////////////////////////////
3289            ArrayList<ArrayList<Point2D>> polylines = null;
3290            ArrayList<ArrayList<Point2D>> newPolylines = null;
3291            ArrayList<Point2D> newLine = null;
3292            for (ShapeInfo shape : shapes) {
3293                polylines = shape.getPolylines();
3294                //System.out.println("pixel polylines: " + String.valueOf(polylines));
3295                newPolylines = ConvertPolylinePixelsToCoords(polylines, ipc, normalize);
3296                shape.setPolylines(newPolylines);
3297            }
3298
3299            for (ShapeInfo label : modifiers) {
3300                Point2D pixelCoord = label.getModifierPosition();
3301                if (pixelCoord == null) {
3302                    pixelCoord = label.getGlyphPosition();
3303                }
3304                Point2D geoCoord = ipc.PixelsToGeo(pixelCoord);
3305
3306                if (normalize) {
3307                    geoCoord = NormalizeCoordToGECoord(geoCoord);
3308                }
3309
3310                double latitude = geoCoord.getY();
3311                double longitude = geoCoord.getX();
3312                label.setModifierPosition(new Point2D.Double(longitude, latitude));
3313
3314            }
3315
3316            ////////////////////////////////////////////////////////////////////
3317            mSymbol.setModifierShapes(modifiers);
3318            mSymbol.setSymbolShapes(shapes);
3319
3320        } catch (Exception exc) {
3321            System.out.println(exc.getMessage());
3322            System.out.println("Symbol Code: " + symbolCode);
3323            exc.printStackTrace();
3324        }
3325
3326        boolean debug = false;
3327        if (debug == true) {
3328            System.out.println("Symbol Code: " + symbolCode);
3329            System.out.println("Scale: " + scale);
3330            System.out.println("BBOX: " + bbox);
3331            if (controlPoints != null) {
3332                System.out.println("Geo Points: " + controlPoints);
3333            }
3334            if (bbox != null) {
3335                System.out.println("geo bounds: " + bbox);
3336            }
3337            if (rect != null) {
3338                System.out.println("pixel bounds: " + rect.toString());
3339            }
3340        }
3341
3342        return mSymbol;
3343
3344    }
3345
3346    /**
3347     *
3348     * @param id - For the client to track the symbol, not related to rendering
3349     * @param name - For the client to track the symbol, not related to rendering
3350     * @param description - For the client to track the symbol, not related to rendering
3351     * @param basicShapeType
3352     * @param controlPoints
3353     * @param scale
3354     * @param bbox
3355     * @param symbolModifiers keyed using constants from
3356     * Modifiers. Pass in comma delimited String for modifiers with multiple
3357     * values like AM, AN &amp; X
3358     * @param symbolAttributes keyed using constants from
3359     * MilStdAttributes. pass in double[] for AM, AN and X; Strings for the
3360     * rest.
3361     * @param format
3362     * @return
3363     */
3364    public static String RenderBasicShape(String id,
3365                                          String name,
3366                                          String description,
3367                                          int basicShapeType,
3368                                          String controlPoints,
3369                                          Double scale,
3370                                          String bbox,
3371                                          Map<String,String> symbolModifiers,
3372                                          Map<String,String> symbolAttributes,
3373                                          int format)//,
3374    {
3375        boolean normalize = true;
3376        //Double controlLat = 0.0;
3377        //Double controlLong = 0.0;
3378        //Double metPerPix = GeoPixelConversion.metersPerPixel(scale);
3379        //String bbox2=getBoundingRectangle(controlPoints,bbox);
3380        StringBuilder jsonOutput = new StringBuilder();
3381        String jsonContent = "";
3382
3383        Rectangle rect = null;
3384        String[] coordinates = controlPoints.split(" ");
3385        ArrayList<ShapeInfo> shapes = new ArrayList<ShapeInfo>();
3386        ArrayList<ShapeInfo> modifiers = new ArrayList<ShapeInfo>();
3387        //ArrayList<Point2D> pixels = new ArrayList<Point2D>();
3388        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
3389        int len = coordinates.length;
3390        //diagnostic create geoCoords here
3391        Point2D coordsUL=null;
3392        final String symbolCode = "";
3393
3394        for (int i = 0; i < len; i++)
3395        {
3396            String[] coordPair = coordinates[i].split(",");
3397            Double latitude = Double.valueOf(coordPair[1].trim()).doubleValue();
3398            Double longitude = Double.valueOf(coordPair[0].trim()).doubleValue();
3399            geoCoords.add(new Point2D.Double(longitude, latitude));
3400        }
3401        ArrayList<POINT2> tgPoints = null;
3402        IPointConversion ipc = null;
3403
3404        //Deutch moved section 6-29-11
3405        Double left = 0.0;
3406        Double right = 0.0;
3407        Double top = 0.0;
3408        Double bottom = 0.0;
3409        Point2D temp = null;
3410        Point2D ptGeoUL = null;
3411        int width = 0;
3412        int height = 0;
3413        int leftX = 0;
3414        int topY = 0;
3415        int bottomY = 0;
3416        int rightX = 0;
3417        int j = 0;
3418        ArrayList<Point2D> bboxCoords = null;
3419        if (bbox != null && bbox.equals("") == false) {
3420            String[] bounds = null;
3421            if (bbox.contains(" "))//trapezoid
3422            {
3423                bboxCoords = new ArrayList<Point2D>();
3424                double x = 0;
3425                double y = 0;
3426                String[] coords = bbox.split(" ");
3427                String[] arrCoord;
3428                for (String coord : coords) {
3429                    arrCoord = coord.split(",");
3430                    x = Double.valueOf(arrCoord[0]);
3431                    y = Double.valueOf(arrCoord[1]);
3432                    bboxCoords.add(new Point2D.Double(x, y));
3433                }
3434                //use the upper left corner of the MBR containing geoCoords
3435                //to set the converter
3436                ptGeoUL = getGeoUL(bboxCoords);
3437                left = ptGeoUL.getX();
3438                top = ptGeoUL.getY();
3439                String bbox2=getBboxFromCoords(bboxCoords);
3440                scale = getReasonableScale(bbox2, scale);
3441                ipc = new PointConverter(left, top, scale);
3442                Point2D ptPixels = null;
3443                Point2D ptGeo = null;
3444                int n = bboxCoords.size();
3445                //for (j = 0; j < bboxCoords.size(); j++)
3446                for (j = 0; j < n; j++) {
3447                    ptGeo = bboxCoords.get(j);
3448                    ptPixels = ipc.GeoToPixels(ptGeo);
3449                    x = ptPixels.getX();
3450                    y = ptPixels.getY();
3451                    if (x < 20) {
3452                        x = 20;
3453                    }
3454                    if (y < 20) {
3455                        y = 20;
3456                    }
3457                    ptPixels.setLocation(x, y);
3458                    //end section
3459                    bboxCoords.set(j, (Point2D) ptPixels);
3460                }
3461            } else//rectangle
3462            {
3463                bounds = bbox.split(",");
3464                left = Double.valueOf(bounds[0]);
3465                right = Double.valueOf(bounds[2]);
3466                top = Double.valueOf(bounds[3]);
3467                bottom = Double.valueOf(bounds[1]);
3468                scale = getReasonableScale(bbox, scale);
3469                ipc = new PointConverter(left, top, scale);
3470            }
3471
3472            Point2D pt2d = null;
3473            if (bboxCoords == null) {
3474                pt2d = new Point2D.Double(left, top);
3475                temp = ipc.GeoToPixels(pt2d);
3476
3477                leftX = (int) temp.getX();
3478                topY = (int) temp.getY();
3479
3480                pt2d = new Point2D.Double(right, bottom);
3481                temp = ipc.GeoToPixels(pt2d);
3482
3483                bottomY = (int) temp.getY();
3484                rightX = (int) temp.getX();
3485
3486                width = (int) Math.abs(rightX - leftX);
3487                height = (int) Math.abs(bottomY - topY);
3488
3489                rect = new Rectangle(leftX, topY, width, height);
3490            }
3491        } else {
3492            rect = null;
3493        }
3494
3495        if (ipc == null) {
3496            Point2D ptCoordsUL = getGeoUL(geoCoords);
3497            ipc = new PointConverter(ptCoordsUL.getX(), ptCoordsUL.getY(), scale);
3498        }
3499
3500        ArrayList<Point2D> geoCoords2 = new ArrayList<Point2D>();
3501        geoCoords2.add(new Point2D.Double(left, top));
3502        geoCoords2.add(new Point2D.Double(right, bottom));
3503
3504//        if (normalize) {
3505//            NormalizeGECoordsToGEExtents(0, 360, geoCoords2);
3506//        }
3507
3508        try {
3509
3510            //String fillColor = null;
3511            MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
3512
3513            if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG){
3514                // Use dash array and hatch pattern fill for SVG output
3515                symbolAttributes.put(MilStdAttributes.UseDashArray, "true");
3516                symbolAttributes.put(MilStdAttributes.UsePatternFill, "true");
3517            }
3518
3519            if (symbolModifiers != null || symbolAttributes != null) {
3520                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
3521            } else {
3522                mSymbol.setFillColor(null);
3523            }
3524
3525            TGLight tg = clsRenderer.createTGLightFromMilStdSymbolBasicShape(mSymbol, ipc, basicShapeType);
3526            ArrayList<ShapeInfo> shapeInfos = new ArrayList();
3527            ArrayList<ShapeInfo> modifierShapeInfos = new ArrayList();
3528            Object clipArea;
3529            if (bboxCoords == null) {
3530                clipArea = rect;
3531            } else {
3532                clipArea = bboxCoords;
3533            }
3534            if (clsRenderer.intersectsClipArea(tg, ipc, clipArea)) {
3535                clsRenderer.render_GE(tg, shapeInfos, modifierShapeInfos, ipc, clipArea);
3536            }
3537            mSymbol.setSymbolShapes(shapeInfos);
3538            mSymbol.setModifierShapes(modifierShapeInfos);
3539            mSymbol.set_WasClipped(tg.get_WasClipped());
3540            shapes = mSymbol.getSymbolShapes();
3541            modifiers = mSymbol.getModifierShapes();
3542
3543            if (format == WebRenderer.OUTPUT_FORMAT_JSON) {
3544                jsonOutput.append("{\"type\":\"symbol\",");
3545                jsonContent = JSONize(shapes, modifiers, ipc, true, normalize);
3546                jsonOutput.append(jsonContent);
3547                jsonOutput.append("}");
3548            } else if (format == WebRenderer.OUTPUT_FORMAT_KML) {
3549                Color textColor = mSymbol.getTextColor();
3550                if(textColor==null)
3551                    textColor=mSymbol.getLineColor();
3552
3553                jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor);
3554                jsonOutput.append(jsonContent);
3555            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOJSON)
3556            {
3557                jsonOutput.append("{\"type\":\"FeatureCollection\",\"features\":");
3558                jsonContent = GeoJSONize(shapes, modifiers, ipc, normalize, mSymbol.getTextColor(), mSymbol.getTextBackgroundColor());
3559                jsonOutput.append(jsonContent);
3560
3561                //moving meta data properties to the last feature with no coords as feature collection doesn't allow properties
3562                jsonOutput.replace(jsonOutput.toString().length()-1,jsonOutput.toString().length(),"" );
3563                if (jsonContent.length() > 2)
3564                    jsonOutput.append(",");
3565                jsonOutput.append("{\"type\": \"Feature\",\"geometry\": { \"type\": \"Polygon\",\"coordinates\": [ ]}");
3566
3567                jsonOutput.append(",\"properties\":{\"id\":\"");
3568                jsonOutput.append(id);
3569                jsonOutput.append("\",\"name\":\"");
3570                jsonOutput.append(name);
3571                jsonOutput.append("\",\"description\":\"");
3572                jsonOutput.append(description);
3573                jsonOutput.append("\",\"symbolID\":\"");
3574                jsonOutput.append(symbolCode);
3575                jsonOutput.append("\",\"wasClipped\":\"");
3576                jsonOutput.append(String.valueOf(mSymbol.get_WasClipped()));
3577                //jsonOutput.append("\"}}");
3578
3579                jsonOutput.append("\"}}]}");
3580            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG) {
3581                String textColor = mSymbol.getTextColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextColor(), false) : "";
3582                String backgroundColor = mSymbol.getTextBackgroundColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextBackgroundColor(), false) : "";
3583                //returns an svg with a geoTL and geoBR value to use to place the canvas on the map
3584                jsonContent = MultiPointHandlerSVG.GeoSVGize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, backgroundColor, mSymbol.get_WasClipped());
3585                jsonOutput.append(jsonContent);
3586            }
3587        } catch (Exception exc) {
3588            String st = JavaRendererUtilities.getStackTrace(exc);
3589            jsonOutput = new StringBuilder();
3590            jsonOutput.append("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + ": " + "- ");
3591            jsonOutput.append(exc.getMessage() + " - ");
3592            jsonOutput.append(st);
3593            jsonOutput.append("\"}");
3594
3595            ErrorLogger.LogException("MultiPointHandler", "RenderBasicShape", exc);
3596        }
3597
3598        boolean debug = false;
3599        if (debug == true) {
3600            System.out.println("Symbol Code: " + symbolCode);
3601            System.out.println("Scale: " + scale);
3602            System.out.println("BBOX: " + bbox);
3603            if (controlPoints != null) {
3604                System.out.println("Geo Points: " + controlPoints);
3605            }
3606            if (bbox != null) {
3607                System.out.println("geo bounds: " + bbox);
3608            }
3609            if (rect != null) {
3610                System.out.println("pixel bounds: " + rect.toString());
3611            }
3612            if (jsonOutput != null) {
3613                System.out.println(jsonOutput.toString());
3614            }
3615        }
3616
3617        ErrorLogger.LogMessage("MultiPointHandler", "RenderBasicShape()", "exit RenderBasicShape", Level.FINER);
3618        return jsonOutput.toString();
3619
3620    }
3621}