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