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