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
2104            //Process Anchor point if available
2105            if(shapeInfo.getModifierAnchor() != null)
2106            {
2107                Point2D anchorPoint = ipc.PixelsToGeo(shapeInfo.getModifierAnchor());
2108                if(normalize)
2109                    anchorPoint = NormalizeCoordToGECoord(anchorPoint);
2110                anchorPoint = new Point2D.Double(Math.round(anchorPoint.getX() * 100000000.0) / 100000000.0,Math.round(anchorPoint.getY() * 100000000.0) / 100000000.0);
2111
2112                JSONed.append(",\"anchorPoint\":{\"type\":\"Point\",\"coordinates\":[");
2113                JSONed.append(anchorPoint.getX());
2114                JSONed.append(",");
2115                JSONed.append(anchorPoint.getY());
2116                JSONed.append("]");
2117                JSONed.append("}");
2118
2119                JSONed.append(",\"anchorOffsetX\":").append(Math.round(shapeInfo.getModifierAnchorOffset().getX()));
2120                JSONed.append(",\"anchorOffsetY\":").append(Math.round(shapeInfo.getModifierAnchorOffset().getY()));
2121            }
2122
2123            JSONed.append(",\"labelOutlineColor\":\"");
2124            JSONed.append(RendererUtilities.colorToHexString(outlineColor, false));
2125            JSONed.append("\",\"labelOutlineWidth\":");
2126            JSONed.append("4");
2127            JSONed.append(",\"rotation\":");
2128            JSONed.append(angle);
2129            JSONed.append(",\"angle\":");
2130            JSONed.append(angle);
2131            JSONed.append("},");
2132
2133            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
2134            JSONed.append(longitude);
2135            JSONed.append(",");
2136            JSONed.append(latitude);
2137            JSONed.append("]");
2138            JSONed.append("}}");
2139
2140        } else {
2141            return "";
2142        }
2143
2144        return JSONed.toString();
2145    }
2146
2147    private static String ShapeToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2148        StringBuilder JSONed = new StringBuilder();
2149        StringBuilder properties = new StringBuilder();
2150        StringBuilder geometry = new StringBuilder();
2151        String geometryType = null;
2152        String sda = null;
2153        /*
2154         NOTE: Google Earth / KML colors are backwards.
2155         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2156         * */
2157        Color lineColor = shapeInfo.getLineColor();
2158        Color fillColor = shapeInfo.getFillColor();
2159
2160        if (shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_FILL || fillColor != null || shapeInfo.getPatternFillImage() != null) {
2161            geometryType = "\"Polygon\"";
2162        } else //if(shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_POLYLINE)
2163        {
2164            geometryType = "\"MultiLineString\"";
2165        }
2166
2167        BasicStroke stroke = null;
2168        stroke = shapeInfo.getStroke();
2169        int lineWidth = 4;
2170
2171        if (stroke != null) {
2172            lineWidth = (int) stroke.getLineWidth();
2173            //lineWidth++;
2174            //System.out.println("lineWidth: " + String.valueOf(lineWidth));
2175        }
2176
2177        //generate JSON properties for feature
2178        properties.append("\"properties\":{");
2179        properties.append("\"label\":\"\",");
2180        if (lineColor != null) {
2181            properties.append("\"strokeColor\":\"" + RendererUtilities.colorToHexString(lineColor, false) + "\",");
2182            properties.append("\"lineOpacity\":" + String.valueOf(lineColor.getAlpha() / 255f) + ",");
2183        }
2184        if (fillColor != null) {
2185            properties.append("\"fillColor\":\"" + RendererUtilities.colorToHexString(fillColor, false) + "\",");
2186            properties.append("\"fillOpacity\":" + String.valueOf(fillColor.getAlpha() / 255f) + ",");
2187        }
2188        if (shapeInfo.getPatternFillImage() != null) {
2189            properties.append("\"fillPattern\":\"" + bitmapToString(shapeInfo.getPatternFillImage()) + "\",");
2190        }
2191        if(stroke.getDashArray() != null)
2192        {
2193            float[] arrSDA = stroke.getDashArray();
2194            sda = "[";
2195            sda += String.valueOf(arrSDA[0]);
2196            if(arrSDA.length > 1)
2197            {
2198                for(int i = 1; i < arrSDA.length; i++)
2199                {
2200                    sda = sda + ", " + String.valueOf(arrSDA[i]);
2201                }
2202            }
2203            sda += "]";
2204            sda = "\"strokeDasharray\":" + sda + ",";
2205            properties.append(sda);
2206        }
2207
2208        int lineCap = stroke.getEndCap();
2209        properties.append("\"lineCap\":" + lineCap + ",");
2210
2211        String strokeWidth = String.valueOf(lineWidth);
2212        properties.append("\"strokeWidth\":" + strokeWidth + ",");
2213        properties.append("\"strokeWeight\":" + strokeWidth + "");
2214        properties.append("},");
2215
2216
2217        properties.append("\"style\":{");
2218        if (lineColor != null) {
2219            properties.append("\"stroke\":\"" + RendererUtilities.colorToHexString(lineColor, false) + "\",");
2220            properties.append("\"line-opacity\":" + String.valueOf(lineColor.getAlpha() / 255f) + ",");
2221        }
2222        if (fillColor != null) {
2223            properties.append("\"fill\":\"" + RendererUtilities.colorToHexString(fillColor, false) + "\",");
2224            properties.append("\"fill-opacity\":" + String.valueOf(fillColor.getAlpha() / 255f) + ",");
2225        }
2226        if(stroke.getDashArray() != null)
2227        {
2228            float[] da = stroke.getDashArray();
2229            sda = String.valueOf(da[0]);
2230            if(da.length > 1)
2231            {
2232                for(int i = 1; i < da.length; i++)
2233                {
2234                    sda = sda + " " + String.valueOf(da[i]);
2235                }
2236            }
2237            sda = "\"stroke-dasharray\":\"" + sda + "\",";
2238            properties.append(sda);
2239            sda = null;
2240        }
2241
2242        if(lineCap == BasicStroke.CAP_SQUARE)
2243            properties.append("\"stroke-linecap\":\"square\",");
2244        else if(lineCap == BasicStroke.CAP_ROUND)
2245            properties.append("\"stroke-linecap\":\"round\",");
2246        else if(lineCap == BasicStroke.CAP_BUTT)
2247            properties.append("\"stroke-linecap\":\"butt\",");
2248
2249        strokeWidth = String.valueOf(lineWidth);
2250        properties.append("\"stroke-width\":" + strokeWidth);
2251        properties.append("}");
2252
2253
2254        //generate JSON geometry for feature
2255        geometry.append("\"geometry\":{\"type\":");
2256        geometry.append(geometryType);
2257        geometry.append(",\"coordinates\":[");
2258
2259        ArrayList shapesArray = shapeInfo.getPolylines();
2260
2261        for (int i = 0; i < shapesArray.size(); i++) {
2262            ArrayList pointList = (ArrayList) shapesArray.get(i);
2263
2264            normalize = normalizePoints(pointList, ipc);
2265
2266            geometry.append("[");
2267
2268            //System.out.println("Pixel Coords:");
2269            for (int j = 0; j < pointList.size(); j++) {
2270                Point2D coord = (Point2D) pointList.get(j);
2271                Point2D geoCoord = ipc.PixelsToGeo(coord);
2272                //M. Deutch 9-27-11
2273                if (normalize) {
2274                    geoCoord = NormalizeCoordToGECoord(geoCoord);
2275                }
2276                double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2277                double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2278
2279                //fix for fill crossing DTL
2280                if (normalize && fillColor != null) {
2281                    if (longitude > 0) {
2282                        longitude -= 360;
2283                    }
2284                }
2285
2286                //diagnostic M. Deutch 10-18-11
2287                //set the point as geo so that the 
2288                //coord.setLocation(longitude, latitude);
2289                coord = new Point2D.Double(longitude, latitude);
2290                pointList.set(j, coord);
2291                //end section
2292
2293                geometry.append("[");
2294                geometry.append(longitude);
2295                geometry.append(",");
2296                geometry.append(latitude);
2297                geometry.append("]");
2298
2299                if (j < (pointList.size() - 1)) {
2300                    geometry.append(",");
2301                }
2302            }
2303
2304            geometry.append("]");
2305
2306            if (i < (shapesArray.size() - 1)) {
2307                geometry.append(",");
2308            }
2309        }
2310        geometry.append("]}");
2311
2312        JSONed.append("{\"type\":\"Feature\",");
2313        JSONed.append(properties.toString());
2314        JSONed.append(",");
2315        JSONed.append(geometry.toString());
2316        JSONed.append("}");
2317
2318        return JSONed.toString();
2319    }
2320
2321    private static String ImageToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2322
2323        StringBuilder JSONed = new StringBuilder();
2324        StringBuilder properties = new StringBuilder();
2325        StringBuilder geometry = new StringBuilder();
2326
2327        //AffineTransform at = shapeInfo.getAffineTransform();
2328        //Point2D coord = (Point2D)new Point2D.Double(at.getTranslateX(), at.getTranslateY());
2329        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2330        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
2331        Point2D geoCoord = ipc.PixelsToGeo(coord);
2332        //M. Deutch 9-27-11
2333        if (normalize) {
2334            geoCoord = NormalizeCoordToGECoord(geoCoord);
2335        }
2336        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2337        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2338        double angle = shapeInfo.getModifierAngle();
2339        coord.setLocation(longitude, latitude);
2340
2341        //diagnostic M. Deutch 10-18-11
2342        shapeInfo.setGlyphPosition(coord);
2343
2344        Bitmap image = shapeInfo.getModifierImage();
2345
2346        RendererSettings RS = RendererSettings.getInstance();
2347
2348        if (image != null) {
2349
2350            JSONed.append("{\"type\":\"Feature\",\"properties\":{\"image\":\"");
2351            JSONed.append(bitmapToString(image));
2352            JSONed.append("\",\"rotation\":");
2353            JSONed.append(angle);
2354            JSONed.append(",\"angle\":");
2355            JSONed.append(angle);
2356            //Process Anchor point if available
2357            if(shapeInfo.getModifierAnchor() != null)
2358            {
2359                Point2D anchorPoint = ipc.PixelsToGeo(shapeInfo.getModifierAnchor());
2360                if(normalize)
2361                    anchorPoint = NormalizeCoordToGECoord(anchorPoint);
2362                anchorPoint = new Point2D.Double(Math.round(anchorPoint.getX() * 100000000.0) / 100000000.0,Math.round(anchorPoint.getY() * 100000000.0) / 100000000.0);
2363
2364                JSONed.append(",\"anchorPoint\":{\"type\":\"Point\",\"coordinates\":[");
2365                JSONed.append(anchorPoint.getX());
2366                JSONed.append(",");
2367                JSONed.append(anchorPoint.getY());
2368                JSONed.append("]");
2369                JSONed.append("}");
2370
2371                JSONed.append(",\"anchorOffsetX\":").append(Math.round(shapeInfo.getModifierAnchorOffset().getX()));
2372                JSONed.append(",\"anchorOffsetY\":").append(Math.round(shapeInfo.getModifierAnchorOffset().getY()));
2373            }
2374            JSONed.append("},");
2375            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
2376            JSONed.append(longitude);
2377            JSONed.append(",");
2378            JSONed.append(latitude);
2379            JSONed.append("]");
2380            JSONed.append("}}");
2381
2382        } else {
2383            return "";
2384        }
2385
2386        return JSONed.toString();
2387    }
2388
2389    protected static String bitmapToString(Bitmap bitmap) {
2390        final int COMPRESSION_QUALITY = 100;
2391        String encodedImage;
2392        ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream();
2393        bitmap.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY,
2394                byteArrayBitmapStream);
2395        byte[] b = byteArrayBitmapStream.toByteArray();
2396        encodedImage = Base64.encodeToString(b, Base64.DEFAULT);
2397        return "data:image/png;base64," + encodedImage;
2398    }
2399
2400    private static String GeoJSONize(ArrayList<ShapeInfo> shapes, ArrayList<ShapeInfo> modifiers, IPointConversion ipc, boolean normalize, Color textColor, Color textBackgroundColor) {
2401
2402        String jstr = "";
2403        ShapeInfo tempModifier = null;
2404        StringBuilder fc = new StringBuilder();//JSON feature collection
2405
2406        fc.append("[");
2407
2408        int len = shapes.size();
2409        for (int i = 0; i < len; i++) {
2410
2411            String shapesToAdd = ShapeToGeoJSONString(shapes.get(i), ipc, normalize);
2412            if (shapesToAdd.length() > 0) {
2413                fc.append(shapesToAdd);
2414                if (i < len - 1) {
2415                    fc.append(",");
2416                }
2417            }
2418        }
2419
2420        int len2 = modifiers.size();
2421
2422        for (int j = 0; j < len2; j++) {
2423            tempModifier = modifiers.get(j);
2424
2425            String modifiersToAdd = null;
2426            if(modifiers.get(j).getModifierImage() != null) {
2427                modifiersToAdd = ImageToGeoJSONString(tempModifier, ipc, normalize);
2428            } else {
2429                modifiersToAdd = LabelToGeoJSONString(tempModifier, ipc, normalize, textColor, textBackgroundColor);
2430            }
2431            if (modifiersToAdd.length() > 0) {
2432                if (fc.length() > 1)
2433                    fc.append(",");
2434                fc.append(modifiersToAdd);
2435            }
2436        }
2437        fc.append("]");
2438        String GeoJSON = fc.toString();
2439        return GeoJSON;
2440    }
2441
2442    /**
2443     * 
2444     * @param shapes
2445     * @param modifiers
2446     * @param ipc
2447     * @param normalize
2448     * @deprecated
2449     */
2450    private static void MakeWWReady(
2451            ArrayList<ShapeInfo> shapes,
2452            ArrayList<ShapeInfo> modifiers,
2453            IPointConversion ipc,
2454            boolean normalize) {
2455        ShapeInfo temp = null;
2456        int len = shapes.size();
2457        for (int i = 0; i < len; i++) {
2458
2459            temp = ShapeToWWReady(shapes.get(i), ipc, normalize);
2460            shapes.set(i, temp);
2461
2462        }
2463
2464        int len2 = modifiers.size();
2465        ShapeInfo tempModifier = null;
2466        for (int j = 0; j < len2; j++) {
2467
2468            tempModifier = modifiers.get(j);
2469
2470            //Do we need this for World Wind?
2471            tempModifier = LabelToWWReady(tempModifier, ipc, normalize);
2472            modifiers.set(j, tempModifier);
2473
2474        }
2475
2476    }
2477
2478    static Boolean normalizePoints(ArrayList<Point2D.Double> shape, IPointConversion ipc) {
2479        ArrayList geoCoords = new ArrayList();
2480        int n = shape.size();
2481        //for (int j = 0; j < shape.size(); j++) 
2482        for (int j = 0; j < n; j++) {
2483            Point2D coord = shape.get(j);
2484            Point2D geoCoord = ipc.PixelsToGeo(coord);
2485            geoCoord = NormalizeCoordToGECoord(geoCoord);
2486            double latitude = geoCoord.getY();
2487            double longitude = geoCoord.getX();
2488            Point2D pt2d = new Point2D.Double(longitude, latitude);
2489            geoCoords.add(pt2d);
2490        }
2491        Boolean normalize = crossesIDL(geoCoords);
2492        return normalize;
2493    }
2494
2495    /**
2496     * @deprecated
2497     */
2498    private static Boolean IsOnePointSymbolCode(String symbolCode) {
2499        String basicCode = SymbolUtilities.getBasicSymbolID(symbolCode);
2500        //TODO: Revisit for basic shapes
2501        //some airspaces affected
2502        if (symbolCode.equals("CAKE-----------")) {
2503            return true;
2504        } else if (symbolCode.equals("CYLINDER-------")) {
2505            return true;
2506        } else if (symbolCode.equals("RADARC---------")) {
2507            return true;
2508        }
2509
2510        return false;
2511    }
2512
2513    private static String ShapeToKMLString(ShapeInfo shapeInfo,
2514                                           IPointConversion ipc,
2515                                           boolean normalize) {
2516        java.lang.StringBuilder kml = new java.lang.StringBuilder();
2517        Color lineColor = null;
2518        Color fillColor = null;
2519        String googleLineColor = null;
2520        String googleFillColor = null;
2521        BasicStroke stroke = null;
2522        int lineWidth = 4;
2523
2524        kml.append("<Placemark>");
2525        kml.append("<Style>");
2526
2527        lineColor = shapeInfo.getLineColor();
2528        if (lineColor != null) {
2529            googleLineColor = Integer.toHexString(shapeInfo.getLineColor().toARGB());
2530
2531            stroke = shapeInfo.getStroke();
2532
2533            if (stroke != null) {
2534                lineWidth = (int) stroke.getLineWidth();
2535            }
2536
2537            googleLineColor = JavaRendererUtilities.ARGBtoABGR(googleLineColor);
2538
2539            kml.append("<LineStyle>");
2540            kml.append("<color>" + googleLineColor + "</color>");
2541            kml.append("<colorMode>normal</colorMode>");
2542            kml.append("<width>" + String.valueOf(lineWidth) + "</width>");
2543            kml.append("</LineStyle>");
2544        }
2545
2546        fillColor = shapeInfo.getFillColor();
2547        Bitmap fillPattern = shapeInfo.getPatternFillImage();
2548        if (fillColor != null || fillPattern != null) {
2549            kml.append("<PolyStyle>");
2550
2551            if (fillColor != null) {
2552                googleFillColor = Integer.toHexString(shapeInfo.getFillColor().toARGB());
2553                googleFillColor = JavaRendererUtilities.ARGBtoABGR(googleFillColor);
2554                kml.append("<color>" + googleFillColor + "</color>");
2555                kml.append("<colorMode>normal</colorMode>");
2556            }
2557            if (fillPattern != null){
2558                kml.append("<shader>" + bitmapToString(fillPattern) + "</shader>");
2559            }
2560
2561            kml.append("<fill>1</fill>");
2562            if (lineColor != null) {
2563                kml.append("<outline>1</outline>");
2564            } else {
2565                kml.append("<outline>0</outline>");
2566            }
2567            kml.append("</PolyStyle>");
2568        }
2569
2570        kml.append("</Style>");
2571
2572        ArrayList shapesArray = shapeInfo.getPolylines();
2573        int len = shapesArray.size();
2574        kml.append("<MultiGeometry>");
2575
2576        for (int i = 0; i < len; i++) {
2577            ArrayList shape = (ArrayList) shapesArray.get(i);
2578            normalize = normalizePoints(shape, ipc);
2579            if (lineColor != null && fillColor == null) {
2580                kml.append("<LineString>");
2581                kml.append("<tessellate>1</tessellate>");
2582                kml.append("<altitudeMode>clampToGround</altitudeMode>");
2583                kml.append("<coordinates>");
2584                int n = shape.size();
2585                //for (int j = 0; j < shape.size(); j++) 
2586                for (int j = 0; j < n; j++) {
2587                    Point2D coord = (Point2D) shape.get(j);
2588                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2589                    if (normalize) {
2590                        geoCoord = NormalizeCoordToGECoord(geoCoord);
2591                    }
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                    kml.append(longitude);
2597                    kml.append(",");
2598                    kml.append(latitude);
2599                    if(j<shape.size()-1)
2600                        kml.append(" ");
2601                }
2602
2603                kml.append("</coordinates>");
2604                kml.append("</LineString>");
2605            }
2606
2607            if (fillColor != null) {
2608
2609                if (i == 0) {
2610                    kml.append("<Polygon>");
2611                }
2612                //kml.append("<outerBoundaryIs>");
2613                if (i == 1 && len > 1) {
2614                    kml.append("<innerBoundaryIs>");
2615                } else {
2616                    kml.append("<outerBoundaryIs>");
2617                }
2618                kml.append("<LinearRing>");
2619                kml.append("<altitudeMode>clampToGround</altitudeMode>");
2620                kml.append("<tessellate>1</tessellate>");
2621                kml.append("<coordinates>");
2622
2623                int n = shape.size();
2624                //for (int j = 0; j < shape.size(); j++) 
2625                for (int j = 0; j < n; j++) {
2626                    Point2D coord = (Point2D) shape.get(j);
2627                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2628
2629                    double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2630                    double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2631
2632                    //fix for fill crossing DTL
2633                    if (normalize) {
2634                        if (longitude > 0) {
2635                            longitude -= 360;
2636                        }
2637                    }
2638
2639                    kml.append(longitude);
2640                    kml.append(",");
2641                    kml.append(latitude);
2642                    if(j<shape.size()-1)
2643                        kml.append(" ");
2644                }
2645
2646                kml.append("</coordinates>");
2647                kml.append("</LinearRing>");
2648                if (i == 1 && len > 1) {
2649                    kml.append("</innerBoundaryIs>");
2650                } else {
2651                    kml.append("</outerBoundaryIs>");
2652                }
2653                if (i == len - 1) {
2654                    kml.append("</Polygon>");
2655                }
2656            }
2657        }
2658
2659        kml.append("</MultiGeometry>");
2660        kml.append("</Placemark>");
2661
2662        return kml.toString();
2663    }
2664
2665    /**
2666     * 
2667     * @param shapeInfo
2668     * @param ipc
2669     * @param normalize
2670     * @return
2671     * @deprecated
2672     */
2673    private static ShapeInfo ShapeToWWReady(
2674            ShapeInfo shapeInfo,
2675            IPointConversion ipc,
2676            boolean normalize) {
2677
2678        ArrayList shapesArray = shapeInfo.getPolylines();
2679        int len = shapesArray.size();
2680
2681        for (int i = 0; i < len; i++) {
2682            ArrayList shape = (ArrayList) shapesArray.get(i);
2683
2684            if (shapeInfo.getLineColor() != null) {
2685                int n = shape.size();
2686                //for (int j = 0; j < shape.size(); j++) 
2687                for (int j = 0; j < n; j++) {
2688                    Point2D coord = (Point2D) shape.get(j);
2689                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2690                    //M. Deutch 9-26-11
2691                    if (normalize) {
2692                        geoCoord = NormalizeCoordToGECoord(geoCoord);
2693                    }
2694
2695                    shape.set(j, geoCoord);
2696
2697                }
2698
2699            }
2700
2701            if (shapeInfo.getFillColor() != null) {
2702                int n = shape.size();
2703                //for (int j = 0; j < shape.size(); j++) 
2704                for (int j = 0; j < n; j++) {
2705                    Point2D coord = (Point2D) shape.get(j);
2706                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2707                    //M. Deutch 9-26-11
2708                    //commenting these two lines seems to help with fill not go around the pole
2709                    //if(normalize)
2710                    //geoCoord=NormalizeCoordToGECoord(geoCoord);
2711
2712                    shape.set(j, geoCoord);
2713                }
2714            }
2715        }
2716
2717        return shapeInfo;
2718    }
2719
2720    private static ShapeInfo LabelToWWReady(ShapeInfo shapeInfo,
2721            IPointConversion ipc,
2722            boolean normalize) {
2723
2724        try {
2725            Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2726            Point2D geoCoord = ipc.PixelsToGeo(coord);
2727            //M. Deutch 9-26-11
2728            if (normalize) {
2729                geoCoord = NormalizeCoordToGECoord(geoCoord);
2730            }
2731            double latitude = geoCoord.getY();
2732            double longitude = geoCoord.getX();
2733            long angle = Math.round(shapeInfo.getModifierAngle());
2734
2735            String text = shapeInfo.getModifierString();
2736
2737            if (text != null && text.equals("") == false) {
2738                shapeInfo.setModifierPosition(geoCoord);
2739            } else {
2740                return null;
2741            }
2742        } catch (Exception exc) {
2743            System.err.println(exc.getMessage());
2744            exc.printStackTrace();
2745        }
2746
2747        return shapeInfo;
2748    }
2749
2750    /**
2751     * Google earth centers text on point rather than drawing from that point.
2752     * So we need to adjust the point to where the center of the text would be.
2753     *
2754     * @param modifier
2755     */
2756    private static void AdjustModifierPointToCenter(ShapeInfo modifier) {
2757        AffineTransform at = null;
2758        try {
2759            Rectangle bounds2 = modifier.getTextLayout().getBounds();
2760            Rectangle2D bounds = new Rectangle2D.Double(bounds2.x, bounds2.y, bounds2.width, bounds2.height);
2761        } catch (Exception exc) {
2762            System.err.println(exc.getMessage());
2763            exc.printStackTrace();
2764        }
2765    }
2766
2767    /**
2768     * 
2769     * @param shapeInfo
2770     * @param ipc
2771     * @param geMap
2772     * @param normalize
2773     * @return
2774     * @deprecated
2775     */
2776    private static String ShapeToJSONString(ShapeInfo shapeInfo, IPointConversion ipc, Boolean geMap, boolean normalize) {
2777        StringBuilder JSONed = new StringBuilder();
2778        /*
2779         NOTE: Google Earth / KML colors are backwards.
2780         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2781         * */
2782        String fillColor = null;
2783        String lineColor = null;
2784
2785        if (shapeInfo.getLineColor() != null) {
2786            lineColor = Integer.toHexString(shapeInfo.getLineColor().toARGB());
2787            if (geMap) {
2788                lineColor = JavaRendererUtilities.ARGBtoABGR(lineColor);
2789            }
2790
2791        }
2792        if (shapeInfo.getFillColor() != null) {
2793            fillColor = Integer.toHexString(shapeInfo.getFillColor().toARGB());
2794            if (geMap) {
2795                fillColor = JavaRendererUtilities.ARGBtoABGR(fillColor);
2796            }
2797        }
2798
2799        BasicStroke stroke = null;
2800        stroke = shapeInfo.getStroke();
2801        int lineWidth = 4;
2802
2803        if (stroke != null) {
2804            lineWidth = (int) stroke.getLineWidth();
2805        }
2806
2807        ArrayList shapesArray = shapeInfo.getPolylines();
2808        int n = shapesArray.size();
2809        //for (int i = 0; i < shapesArray.size(); i++) 
2810        for (int i = 0; i < n; i++) {
2811            ArrayList shape = (ArrayList) shapesArray.get(i);
2812
2813            if (fillColor != null) {
2814                JSONed.append("{\"polygon\":[");
2815            } else {
2816                JSONed.append("{\"line\":[");
2817            }
2818
2819            int t = shape.size();
2820            //for (int j = 0; j < shape.size(); j++) 
2821            for (int j = 0; j < t; j++) {
2822                Point2D coord = (Point2D) shape.get(j);
2823                Point2D geoCoord = ipc.PixelsToGeo(coord);
2824                //M. Deutch 9-27-11
2825                if (normalize) {
2826                    geoCoord = NormalizeCoordToGECoord(geoCoord);
2827                }
2828                double latitude = geoCoord.getY();
2829                double longitude = geoCoord.getX();
2830
2831                //diagnostic M. Deutch 10-18-11
2832                //set the point as geo so that the 
2833                coord = new Point2D.Double(longitude, latitude);
2834                shape.set(j, coord);
2835
2836                JSONed.append("[");
2837                JSONed.append(longitude);
2838                JSONed.append(",");
2839                JSONed.append(latitude);
2840                JSONed.append("]");
2841
2842                if (j < (shape.size() - 1)) {
2843                    JSONed.append(",");
2844                }
2845            }
2846
2847            JSONed.append("]");
2848            if (lineColor != null) {
2849                JSONed.append(",\"lineColor\":\"");
2850                JSONed.append(lineColor);
2851
2852                JSONed.append("\"");
2853            }
2854            if (fillColor != null) {
2855                JSONed.append(",\"fillColor\":\"");
2856                JSONed.append(fillColor);
2857                JSONed.append("\"");
2858            }
2859
2860            JSONed.append(",\"lineWidth\":\"");
2861            JSONed.append(String.valueOf(lineWidth));
2862            JSONed.append("\"");
2863
2864            JSONed.append("}");
2865
2866            if (i < (shapesArray.size() - 1)) {
2867                JSONed.append(",");
2868            }
2869        }
2870
2871        return JSONed.toString();
2872    }
2873
2874    private static String LabelToKMLString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize, Color textColor) {
2875        java.lang.StringBuilder kml = new java.lang.StringBuilder();
2876
2877        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2878        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
2879        Point2D geoCoord = ipc.PixelsToGeo(coord);
2880        //M. Deutch 9-26-11
2881        if (normalize) {
2882            geoCoord = NormalizeCoordToGECoord(geoCoord);
2883        }
2884        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2885        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2886        long angle = Math.round(shapeInfo.getModifierAngle());
2887
2888        String text = shapeInfo.getModifierString();
2889
2890        String cdataStart = "<![CDATA[";
2891        String cdataEnd = "]]>";
2892
2893        String color = Integer.toHexString(textColor.toARGB());
2894        color = JavaRendererUtilities.ARGBtoABGR(color);
2895        float kmlScale = RendererSettings.getInstance().getKMLLabelScale();
2896
2897        if (kmlScale > 0 && text != null && text.equals("") == false) {
2898            kml.append("<Placemark>");//("<Placemark id=\"" + id + "_lp" + i + "\">");
2899            kml.append("<name>" + cdataStart + text + cdataEnd + "</name>");
2900            kml.append("<Style>");
2901            kml.append("<IconStyle>");
2902            kml.append("<scale>" + kmlScale + "</scale>");
2903            kml.append("<heading>" + angle + "</heading>");
2904            kml.append("<Icon>");
2905            kml.append("<href></href>");
2906            kml.append("</Icon>");
2907            kml.append("</IconStyle>");
2908            kml.append("<LabelStyle>");
2909            kml.append("<color>" + color + "</color>");
2910            kml.append("<scale>" + String.valueOf(kmlScale) +"</scale>");
2911            kml.append("</LabelStyle>");
2912            kml.append("</Style>");
2913            kml.append("<Point>");
2914            kml.append("<extrude>1</extrude>");
2915            kml.append("<altitudeMode>relativeToGround</altitudeMode>");
2916            kml.append("<coordinates>");
2917            kml.append(longitude);
2918            kml.append(",");
2919            kml.append(latitude);
2920            kml.append("</coordinates>");
2921            kml.append("</Point>");
2922            kml.append("</Placemark>");
2923        } else {
2924            return "";
2925        }
2926
2927        return kml.toString();
2928    }
2929
2930    /**
2931     * 
2932     * @param shapeInfo
2933     * @param ipc
2934     * @param normalize
2935     * @return
2936     * @deprecated
2937     */
2938    private static String LabelToJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2939        StringBuilder JSONed = new StringBuilder();
2940        /*
2941         NOTE: Google Earth / KML colors are backwards.
2942         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2943         * */
2944        JSONed.append("{\"label\":");
2945
2946        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2947        Point2D geoCoord = ipc.PixelsToGeo(coord);
2948        if (normalize) {
2949            geoCoord = NormalizeCoordToGECoord(geoCoord);
2950        }
2951        double latitude = geoCoord.getY();
2952        double longitude = geoCoord.getX();
2953        double angle = shapeInfo.getModifierAngle();
2954        coord.setLocation(longitude, latitude);
2955
2956        shapeInfo.setGlyphPosition(coord);
2957
2958        String text = shapeInfo.getModifierString();
2959
2960        if (text != null && text.equals("") == false) {
2961            JSONed.append("[");
2962            JSONed.append(longitude);
2963            JSONed.append(",");
2964            JSONed.append(latitude);
2965            JSONed.append("]");
2966
2967            JSONed.append(",\"text\":\"");
2968            JSONed.append(text);
2969            JSONed.append("\"");
2970
2971            JSONed.append(",\"angle\":\"");
2972            JSONed.append(angle);
2973            JSONed.append("\"}");
2974        } else {
2975            return "";
2976        }
2977
2978        return JSONed.toString();
2979    }
2980
2981    public static String canRenderMultiPoint(String symbolID, Map<String,String> modifiers, int numPoints) {
2982        try {
2983            String basicID = SymbolUtilities.getBasicSymbolID(symbolID);
2984            MSInfo info = MSLookup.getInstance().getMSLInfo(symbolID);
2985
2986            if (info == null) {
2987                if (SymbolID.getVersion(symbolID) >= SymbolID.Version_2525E) {
2988                    return"Basic ID: " + basicID + " not recognized in version E (15)";
2989                } else {
2990                    return "Basic ID: " + basicID + " not recognized in version D (11)";
2991                }
2992            }
2993
2994            int drawRule = info.getDrawRule();
2995
2996            if (drawRule == DrawRules.DONOTDRAW) {
2997                return "Basic ID: " + basicID + " has no draw rule";
2998            } else if (!SymbolUtilities.isMultiPoint(symbolID)) {
2999                return "Basic ID: " + basicID + " is not a multipoint symbol";
3000            } else if (numPoints < info.getMinPointCount()) {
3001                return "Basic ID: " + basicID + " requires a minimum of " + String.valueOf(info.getMinPointCount()) + " points. " + String.valueOf(numPoints) + " are present.";
3002            }
3003
3004            //now check for required modifiers
3005            ArrayList<Double> AM = new ArrayList();
3006            ArrayList<Double> AN = new ArrayList();
3007            if (modifiers.containsKey(Modifiers.AM_DISTANCE)) {
3008                String[] amArray = modifiers.get(Modifiers.AM_DISTANCE).split(",");
3009                for (String str : amArray) {
3010                    if (!str.equals("")) {
3011                        AM.add(Double.parseDouble(str));
3012                    }
3013                }
3014            }
3015            if (modifiers.containsKey(Modifiers.AN_AZIMUTH)) {
3016                String[] anArray = modifiers.get(Modifiers.AN_AZIMUTH).split(",");
3017                for (String str : anArray) {
3018                    if (!str.equals("")) {
3019                        AN.add(Double.parseDouble(str));
3020                    }
3021                }
3022            }
3023
3024            return hasRequiredModifiers(symbolID, drawRule, AM, AN);
3025        } catch (Exception exc) {
3026            ErrorLogger.LogException("MultiPointHandler", "canRenderMultiPoint", exc);
3027            return "false: " + exc.getMessage();
3028        }
3029    }
3030
3031    static private String hasRequiredModifiers(String symbolID, int drawRule, ArrayList<Double> AM, ArrayList<Double> AN) {
3032
3033        String message = symbolID;
3034        try {
3035            if (drawRule > 700) {
3036                if (drawRule == DrawRules.CIRCULAR1)
3037                {
3038                    if (AM != null && AM.size() > 0) {
3039                        return "true";
3040                    } else {
3041                        message += " requires a modifiers object that has 1 distance/AM value.";
3042                        return message;
3043                    }
3044                } else if (drawRule == DrawRules.RECTANGULAR2)
3045                {
3046                    if (AM != null && AM.size() >= 2
3047                            && AN != null && AN.size() >= 1) {
3048                        return "true";
3049                    } else {
3050                        message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
3051                        return message;
3052                    }
3053                } else if (drawRule == DrawRules.ARC1)
3054                {
3055                    if (AM != null && AM.size() >= 1
3056                            && AN != null && AN.size() >= 2) {
3057                        return "true";
3058                    } else {
3059                        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.");
3060                        return message;
3061                    }
3062                } else if (drawRule == DrawRules.CIRCULAR2)
3063                {
3064                    if (AM != null && AM.size() > 0) {
3065                        return "true";
3066                    } else {
3067                        message += (" requires a modifiers object that has at least 1 distance/AM value");
3068                        return message;
3069                    }
3070                } else if (drawRule == DrawRules.RECTANGULAR1)
3071                {
3072                    if (AM != null && AM.size() > 0) {
3073                        return "true";
3074                    } else {
3075                        message += (" requires a modifiers object that has 1 distance/AM value.");
3076                        return message;
3077                    }
3078                } else if (drawRule == DrawRules.ELLIPSE1)
3079                {
3080                    if (AM != null && AM.size() >= 2
3081                            && AN != null && AN.size() >= 1) {
3082                        return "true";
3083                    } else {
3084                        message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
3085                        return message;
3086                    }
3087                }
3088                else if (drawRule == DrawRules.RECTANGULAR3)
3089                {
3090                    if (AM != null && AM.size() >= 1) {
3091                        return "true";
3092                    } else {
3093                        message += (" requires a modifiers object that has 1 distance/AM value.");
3094                        return message;
3095                    }
3096                } else {
3097                    //should never get here
3098                    return "true";
3099                }
3100            } else if (drawRule == DrawRules.POINT17) {
3101                if (AM != null && AM.size() >= 2
3102                        && AN != null && AN.size() >= 1) {
3103                    return "true";
3104                } else {
3105                    message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
3106                    return message;
3107                }
3108            } else if (drawRule == DrawRules.POINT18) {
3109                if (AM != null && AM.size() >= 2
3110                        && AN != null && AN.size() >= 2) {
3111                    return "true";
3112                } else {
3113                    message += (" requires a modifiers object that has 2 distance/AM values and 2 azimuth/AN values.");
3114                    return message;
3115                }
3116            } else if (drawRule == DrawRules.CORRIDOR1) {
3117                if (AM != null && AM.size() > 0) {
3118                    return "true";
3119                } else {
3120                    message += (" requires a modifiers object that has 1 distance/AM value.");
3121                    return message;
3122                }
3123            } else {
3124                //no required parameters
3125                return "true";
3126            }
3127        } catch (Exception exc) {
3128            ErrorLogger.LogException("MultiPointHandler", "hasRequiredModifiers", exc);
3129            return "true";
3130        }
3131    }
3132
3133    /**
3134     *
3135     * @param id
3136     * @param name
3137     * @param description
3138     * @param basicShapeType
3139     * @param controlPoints
3140     * @param scale
3141     * @param bbox
3142     * @param symbolModifiers
3143     * @param symbolAttributes
3144     * @return
3145     */
3146    public static MilStdSymbol RenderBasicShapeAsMilStdSymbol(String id,
3147                                                          String name,
3148                                                          String description,
3149                                                          int basicShapeType,
3150                                                          String controlPoints,
3151                                                          Double scale,
3152                                                          String bbox,
3153                                                          Map<String,String> symbolModifiers,
3154                                                          Map<String,String> symbolAttributes)
3155    {
3156        MilStdSymbol mSymbol = null;
3157        boolean normalize = true;
3158        Double controlLat = 0.0;
3159        Double controlLong = 0.0;
3160        //String jsonContent = "";
3161
3162        Rectangle rect = null;
3163
3164        //for symbol & line fill
3165        ArrayList<POINT2> tgPoints = null;
3166
3167        String[] coordinates = controlPoints.split(" ");
3168        ArrayList<ShapeInfo> shapes = null;//new ArrayList<ShapeInfo>();
3169        ArrayList<ShapeInfo> modifiers = null;//new ArrayList<ShapeInfo>();
3170        //ArrayList<Point2D> pixels = new ArrayList<Point2D>();
3171        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
3172        int len = coordinates.length;
3173
3174        IPointConversion ipc = null;
3175
3176        //Deutch moved section 6-29-11
3177        Double left = 0.0;
3178        Double right = 0.0;
3179        Double top = 0.0;
3180        Double bottom = 0.0;
3181        Point2D temp = null;
3182        Point2D ptGeoUL = null;
3183        int width = 0;
3184        int height = 0;
3185        int leftX = 0;
3186        int topY = 0;
3187        int bottomY = 0;
3188        int rightX = 0;
3189        int j = 0;
3190        ArrayList<Point2D> bboxCoords = null;
3191        if (bbox != null && bbox.equals("") == false) {
3192            String[] bounds = null;
3193            if (bbox.contains(" "))//trapezoid
3194            {
3195                bboxCoords = new ArrayList<Point2D>();
3196                double x = 0;
3197                double y = 0;
3198                String[] coords = bbox.split(" ");
3199                String[] arrCoord;
3200                for (String coord : coords) {
3201                    arrCoord = coord.split(",");
3202                    x = Double.valueOf(arrCoord[0]);
3203                    y = Double.valueOf(arrCoord[1]);
3204                    bboxCoords.add(new Point2D.Double(x, y));
3205                }
3206                //use the upper left corner of the MBR containing geoCoords
3207                //to set the converter
3208                ptGeoUL = getGeoUL(bboxCoords);
3209                left = ptGeoUL.getX();
3210                top = ptGeoUL.getY();
3211                ipc = new PointConverter(left, top, scale);
3212                Point2D ptPixels = null;
3213                Point2D ptGeo = null;
3214                int n = bboxCoords.size();
3215                //for (j = 0; j < bboxCoords.size(); j++)
3216                for (j = 0; j < n; j++) {
3217                    ptGeo = bboxCoords.get(j);
3218                    ptPixels = ipc.GeoToPixels(ptGeo);
3219                    x = ptPixels.getX();
3220                    y = ptPixels.getY();
3221                    if (x < 20) {
3222                        x = 20;
3223                    }
3224                    if (y < 20) {
3225                        y = 20;
3226                    }
3227                    ptPixels.setLocation(x, y);
3228                    //end section
3229                    bboxCoords.set(j, (Point2D) ptPixels);
3230                }
3231            } else//rectangle
3232            {
3233                bounds = bbox.split(",");
3234                left = Double.valueOf(bounds[0]);
3235                right = Double.valueOf(bounds[2]);
3236                top = Double.valueOf(bounds[3]);
3237                bottom = Double.valueOf(bounds[1]);
3238                scale = getReasonableScale(bbox, scale);
3239                ipc = new PointConverter(left, top, scale);
3240            }
3241
3242            Point2D pt2d = null;
3243            if (bboxCoords == null) {
3244                pt2d = new Point2D.Double(left, top);
3245                temp = ipc.GeoToPixels(pt2d);
3246
3247                leftX = (int) temp.getX();
3248                topY = (int) temp.getY();
3249
3250                pt2d = new Point2D.Double(right, bottom);
3251                temp = ipc.GeoToPixels(pt2d);
3252
3253                bottomY = (int) temp.getY();
3254                rightX = (int) temp.getX();
3255                //diagnostic clipping does not work for large scales
3256//                if (scale > 10e6) {
3257//                    //get widest point in the AOI
3258//                    double midLat = 0;
3259//                    if (bottom < 0 && top > 0) {
3260//                        midLat = 0;
3261//                    } else if (bottom < 0 && top < 0) {
3262//                        midLat = top;
3263//                    } else if (bottom > 0 && top > 0) {
3264//                        midLat = bottom;
3265//                    }
3266//
3267//                    temp = ipc.GeoToPixels(new Point2D.Double(right, midLat));
3268//                    rightX = (int) temp.getX();
3269//                }
3270                //end section
3271
3272                width = (int) Math.abs(rightX - leftX);
3273                height = (int) Math.abs(bottomY - topY);
3274
3275                if(width==0 || height==0)
3276                    rect=null;
3277                else
3278                    rect = new Rectangle(leftX, topY, width, height);
3279            }
3280        } else {
3281            rect = null;
3282        }
3283        //end section
3284
3285        for (int i = 0; i < len; i++) {
3286            String[] coordPair = coordinates[i].split(",");
3287            Double latitude = Double.valueOf(coordPair[1].trim());
3288            Double longitude = Double.valueOf(coordPair[0].trim());
3289            geoCoords.add(new Point2D.Double(longitude, latitude));
3290        }
3291        if (ipc == null) {
3292            Point2D ptCoordsUL = getGeoUL(geoCoords);
3293            ipc = new PointConverter(ptCoordsUL.getX(), ptCoordsUL.getY(), scale);
3294        }
3295        //if (crossesIDL(geoCoords) == true)
3296//        if(Math.abs(right-left)>180)
3297//        {
3298//            normalize = true;
3299//            ((PointConverter)ipc).set_normalize(true);
3300//        }
3301//        else {
3302//            normalize = false;
3303//            ((PointConverter)ipc).set_normalize(false);
3304//        }
3305
3306        //seems to work ok at world view
3307//        if (normalize) {
3308//            NormalizeGECoordsToGEExtents(0, 360, geoCoords);
3309//        }
3310
3311        //M. Deutch 10-3-11
3312        //must shift the rect pixels to synch with the new ipc
3313        //the old ipc was in synch with the bbox, so rect x,y was always 0,0
3314        //the new ipc synchs with the upper left of the geocoords so the boox is shifted
3315        //and therefore the clipping rectangle must shift by the delta x,y between
3316        //the upper left corner of the original bbox and the upper left corner of the geocoords
3317        ArrayList<Point2D> geoCoords2 = new ArrayList<Point2D>();
3318        geoCoords2.add(new Point2D.Double(left, top));
3319        geoCoords2.add(new Point2D.Double(right, bottom));
3320
3321//        if (normalize) {
3322//            NormalizeGECoordsToGEExtents(0, 360, geoCoords2);
3323//        }
3324
3325        //disable clipping
3326        if (crossesIDL(geoCoords) == false) {
3327            rect = null;
3328            bboxCoords = null;
3329        }
3330
3331        String symbolCode = "";
3332        try {
3333            String fillColor = null;
3334            mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
3335
3336//            mSymbol.setUseDashArray(true);
3337
3338            if (symbolModifiers != null || symbolAttributes != null) {
3339                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
3340            } else {
3341                mSymbol.setFillColor(null);
3342            }
3343
3344            if (mSymbol.getFillColor() != null) {
3345                Color fc = mSymbol.getFillColor();
3346                //fillColor = Integer.toHexString(fc.getRGB());
3347                fillColor = Integer.toHexString(fc.toARGB());
3348            }
3349
3350            TGLight tg = clsRenderer.createTGLightFromMilStdSymbolBasicShape(mSymbol, ipc, basicShapeType);
3351            ArrayList<ShapeInfo> shapeInfos = new ArrayList();
3352            ArrayList<ShapeInfo> modifierShapeInfos = new ArrayList();
3353            Object clipArea;
3354            if (bboxCoords == null) {
3355                clipArea = rect;
3356            } else {
3357                clipArea = bboxCoords;
3358            }
3359            if (clsRenderer.intersectsClipArea(tg, ipc, clipArea)) {
3360                clsRenderer.render_GE(tg, shapeInfos, modifierShapeInfos, ipc, clipArea);
3361            }
3362            mSymbol.setSymbolShapes(shapeInfos);
3363            mSymbol.setModifierShapes(modifierShapeInfos);
3364            mSymbol.set_WasClipped(tg.get_WasClipped());
3365            shapes = mSymbol.getSymbolShapes();
3366            modifiers = mSymbol.getModifierShapes();
3367
3368            //convert points////////////////////////////////////////////////////
3369            ArrayList<ArrayList<Point2D>> polylines = null;
3370            ArrayList<ArrayList<Point2D>> newPolylines = null;
3371            ArrayList<Point2D> newLine = null;
3372            for (ShapeInfo shape : shapes) {
3373                polylines = shape.getPolylines();
3374                //System.out.println("pixel polylines: " + String.valueOf(polylines));
3375                newPolylines = ConvertPolylinePixelsToCoords(polylines, ipc, normalize);
3376                shape.setPolylines(newPolylines);
3377            }
3378
3379            for (ShapeInfo label : modifiers) {
3380                Point2D pixelCoord = label.getModifierPosition();
3381                if (pixelCoord == null) {
3382                    pixelCoord = label.getGlyphPosition();
3383                }
3384                Point2D geoCoord = ipc.PixelsToGeo(pixelCoord);
3385
3386                if (normalize) {
3387                    geoCoord = NormalizeCoordToGECoord(geoCoord);
3388                }
3389
3390                double latitude = geoCoord.getY();
3391                double longitude = geoCoord.getX();
3392                label.setModifierPosition(new Point2D.Double(longitude, latitude));
3393
3394            }
3395
3396            ////////////////////////////////////////////////////////////////////
3397            mSymbol.setModifierShapes(modifiers);
3398            mSymbol.setSymbolShapes(shapes);
3399
3400        } catch (Exception exc) {
3401            System.out.println(exc.getMessage());
3402            System.out.println("Symbol Code: " + symbolCode);
3403            exc.printStackTrace();
3404        }
3405
3406        boolean debug = false;
3407        if (debug == true) {
3408            System.out.println("Symbol Code: " + symbolCode);
3409            System.out.println("Scale: " + scale);
3410            System.out.println("BBOX: " + bbox);
3411            if (controlPoints != null) {
3412                System.out.println("Geo Points: " + controlPoints);
3413            }
3414            if (bbox != null) {
3415                System.out.println("geo bounds: " + bbox);
3416            }
3417            if (rect != null) {
3418                System.out.println("pixel bounds: " + rect.toString());
3419            }
3420        }
3421
3422        return mSymbol;
3423
3424    }
3425
3426    /**
3427     *
3428     * @param id - For the client to track the symbol, not related to rendering
3429     * @param name - For the client to track the symbol, not related to rendering
3430     * @param description - For the client to track the symbol, not related to rendering
3431     * @param basicShapeType
3432     * @param controlPoints
3433     * @param scale
3434     * @param bbox
3435     * @param symbolModifiers keyed using constants from
3436     * Modifiers. Pass in comma delimited String for modifiers with multiple
3437     * values like AM, AN &amp; X
3438     * @param symbolAttributes keyed using constants from
3439     * MilStdAttributes. pass in double[] for AM, AN and X; Strings for the
3440     * rest.
3441     * @param format
3442     * @return
3443     */
3444    public static String RenderBasicShape(String id,
3445                                          String name,
3446                                          String description,
3447                                          int basicShapeType,
3448                                          String controlPoints,
3449                                          Double scale,
3450                                          String bbox,
3451                                          Map<String,String> symbolModifiers,
3452                                          Map<String,String> symbolAttributes,
3453                                          int format)//,
3454    {
3455        boolean normalize = true;
3456        //Double controlLat = 0.0;
3457        //Double controlLong = 0.0;
3458        //Double metPerPix = GeoPixelConversion.metersPerPixel(scale);
3459        //String bbox2=getBoundingRectangle(controlPoints,bbox);
3460        StringBuilder jsonOutput = new StringBuilder();
3461        String jsonContent = "";
3462
3463        Rectangle rect = null;
3464        String[] coordinates = controlPoints.split(" ");
3465        ArrayList<ShapeInfo> shapes = new ArrayList<ShapeInfo>();
3466        ArrayList<ShapeInfo> modifiers = new ArrayList<ShapeInfo>();
3467        //ArrayList<Point2D> pixels = new ArrayList<Point2D>();
3468        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
3469        int len = coordinates.length;
3470        //diagnostic create geoCoords here
3471        Point2D coordsUL=null;
3472        final String symbolCode = "";
3473
3474        for (int i = 0; i < len; i++)
3475        {
3476            String[] coordPair = coordinates[i].split(",");
3477            Double latitude = Double.valueOf(coordPair[1].trim()).doubleValue();
3478            Double longitude = Double.valueOf(coordPair[0].trim()).doubleValue();
3479            geoCoords.add(new Point2D.Double(longitude, latitude));
3480        }
3481        ArrayList<POINT2> tgPoints = null;
3482        IPointConversion ipc = null;
3483
3484        //Deutch moved section 6-29-11
3485        Double left = 0.0;
3486        Double right = 0.0;
3487        Double top = 0.0;
3488        Double bottom = 0.0;
3489        Point2D temp = null;
3490        Point2D ptGeoUL = null;
3491        int width = 0;
3492        int height = 0;
3493        int leftX = 0;
3494        int topY = 0;
3495        int bottomY = 0;
3496        int rightX = 0;
3497        int j = 0;
3498        ArrayList<Point2D> bboxCoords = null;
3499        if (bbox != null && bbox.equals("") == false) {
3500            String[] bounds = null;
3501            if (bbox.contains(" "))//trapezoid
3502            {
3503                bboxCoords = new ArrayList<Point2D>();
3504                double x = 0;
3505                double y = 0;
3506                String[] coords = bbox.split(" ");
3507                String[] arrCoord;
3508                for (String coord : coords) {
3509                    arrCoord = coord.split(",");
3510                    x = Double.valueOf(arrCoord[0]);
3511                    y = Double.valueOf(arrCoord[1]);
3512                    bboxCoords.add(new Point2D.Double(x, y));
3513                }
3514                //use the upper left corner of the MBR containing geoCoords
3515                //to set the converter
3516                ptGeoUL = getGeoUL(bboxCoords);
3517                left = ptGeoUL.getX();
3518                top = ptGeoUL.getY();
3519                String bbox2=getBboxFromCoords(bboxCoords);
3520                scale = getReasonableScale(bbox2, scale);
3521                ipc = new PointConverter(left, top, scale);
3522                Point2D ptPixels = null;
3523                Point2D ptGeo = null;
3524                int n = bboxCoords.size();
3525                //for (j = 0; j < bboxCoords.size(); j++)
3526                for (j = 0; j < n; j++) {
3527                    ptGeo = bboxCoords.get(j);
3528                    ptPixels = ipc.GeoToPixels(ptGeo);
3529                    x = ptPixels.getX();
3530                    y = ptPixels.getY();
3531                    if (x < 20) {
3532                        x = 20;
3533                    }
3534                    if (y < 20) {
3535                        y = 20;
3536                    }
3537                    ptPixels.setLocation(x, y);
3538                    //end section
3539                    bboxCoords.set(j, (Point2D) ptPixels);
3540                }
3541            } else//rectangle
3542            {
3543                bounds = bbox.split(",");
3544                left = Double.valueOf(bounds[0]);
3545                right = Double.valueOf(bounds[2]);
3546                top = Double.valueOf(bounds[3]);
3547                bottom = Double.valueOf(bounds[1]);
3548                scale = getReasonableScale(bbox, scale);
3549                ipc = new PointConverter(left, top, scale);
3550            }
3551
3552            Point2D pt2d = null;
3553            if (bboxCoords == null) {
3554                pt2d = new Point2D.Double(left, top);
3555                temp = ipc.GeoToPixels(pt2d);
3556
3557                leftX = (int) temp.getX();
3558                topY = (int) temp.getY();
3559
3560                pt2d = new Point2D.Double(right, bottom);
3561                temp = ipc.GeoToPixels(pt2d);
3562
3563                bottomY = (int) temp.getY();
3564                rightX = (int) temp.getX();
3565
3566                width = (int) Math.abs(rightX - leftX);
3567                height = (int) Math.abs(bottomY - topY);
3568
3569                rect = new Rectangle(leftX, topY, width, height);
3570            }
3571        } else {
3572            rect = null;
3573        }
3574
3575        if (ipc == null) {
3576            Point2D ptCoordsUL = getGeoUL(geoCoords);
3577            ipc = new PointConverter(ptCoordsUL.getX(), ptCoordsUL.getY(), scale);
3578        }
3579
3580        ArrayList<Point2D> geoCoords2 = new ArrayList<Point2D>();
3581        geoCoords2.add(new Point2D.Double(left, top));
3582        geoCoords2.add(new Point2D.Double(right, bottom));
3583
3584//        if (normalize) {
3585//            NormalizeGECoordsToGEExtents(0, 360, geoCoords2);
3586//        }
3587
3588        try {
3589
3590            //String fillColor = null;
3591            MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
3592
3593            if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG){
3594                // Use dash array and hatch pattern fill for SVG output
3595                symbolAttributes.put(MilStdAttributes.UseDashArray, "true");
3596                symbolAttributes.put(MilStdAttributes.UsePatternFill, "true");
3597            }
3598
3599            if (symbolModifiers != null || symbolAttributes != null) {
3600                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
3601            } else {
3602                mSymbol.setFillColor(null);
3603            }
3604
3605            TGLight tg = clsRenderer.createTGLightFromMilStdSymbolBasicShape(mSymbol, ipc, basicShapeType);
3606            ArrayList<ShapeInfo> shapeInfos = new ArrayList();
3607            ArrayList<ShapeInfo> modifierShapeInfos = new ArrayList();
3608            Object clipArea;
3609            if (bboxCoords == null) {
3610                clipArea = rect;
3611            } else {
3612                clipArea = bboxCoords;
3613            }
3614            if (clsRenderer.intersectsClipArea(tg, ipc, clipArea)) {
3615                clsRenderer.render_GE(tg, shapeInfos, modifierShapeInfos, ipc, clipArea);
3616            }
3617            mSymbol.setSymbolShapes(shapeInfos);
3618            mSymbol.setModifierShapes(modifierShapeInfos);
3619            mSymbol.set_WasClipped(tg.get_WasClipped());
3620            shapes = mSymbol.getSymbolShapes();
3621            modifiers = mSymbol.getModifierShapes();
3622
3623            if (format == WebRenderer.OUTPUT_FORMAT_JSON) {
3624                jsonOutput.append("{\"type\":\"symbol\",");
3625                jsonContent = JSONize(shapes, modifiers, ipc, true, normalize);
3626                jsonOutput.append(jsonContent);
3627                jsonOutput.append("}");
3628            } else if (format == WebRenderer.OUTPUT_FORMAT_KML) {
3629                Color textColor = mSymbol.getTextColor();
3630                if(textColor==null)
3631                    textColor=mSymbol.getLineColor();
3632
3633                jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, mSymbol.getWasClipped(), mSymbol.isTextScaleSensitive(), mSymbol.isSymbolScaleSensitive());
3634                jsonOutput.append(jsonContent);
3635            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOJSON)
3636            {
3637                jsonOutput.append("{\"type\":\"FeatureCollection\",\"features\":");
3638                jsonContent = GeoJSONize(shapes, modifiers, ipc, normalize, mSymbol.getTextColor(), mSymbol.getTextBackgroundColor());
3639                jsonOutput.append(jsonContent);
3640
3641                //moving meta data properties to the last feature with no coords as feature collection doesn't allow properties
3642                jsonOutput.replace(jsonOutput.toString().length()-1,jsonOutput.toString().length(),"" );
3643                if (jsonContent.length() > 2)
3644                    jsonOutput.append(",");
3645                jsonOutput.append("{\"type\": \"Feature\",\"geometry\": { \"type\": \"Polygon\",\"coordinates\": [ ]}");
3646
3647                jsonOutput.append(",\"properties\":{\"id\":\"");
3648                jsonOutput.append(id);
3649                jsonOutput.append("\",\"name\":\"");
3650                jsonOutput.append(name);
3651                jsonOutput.append("\",\"description\":\"");
3652                jsonOutput.append(description);
3653                jsonOutput.append("\",\"symbolID\":\"");
3654                jsonOutput.append(symbolCode);
3655                jsonOutput.append("\",\"wasClipped\":\"");
3656                jsonOutput.append(String.valueOf(mSymbol.getWasClipped()));
3657                jsonOutput.append("\",\"textScaleSensitive\":\"");
3658                jsonOutput.append(String.valueOf(mSymbol.isTextScaleSensitive()));
3659                jsonOutput.append("\",\"symbolScaleSensitive\":\"");
3660                jsonOutput.append(String.valueOf(mSymbol.isSymbolScaleSensitive()));
3661                //jsonOutput.append("\"}}");
3662
3663                jsonOutput.append("\"}}]}");
3664            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG) {
3665                String textColor = mSymbol.getTextColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextColor(), false) : "";
3666                String backgroundColor = mSymbol.getTextBackgroundColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextBackgroundColor(), false) : "";
3667                //returns an svg with a geoTL and geoBR value to use to place the canvas on the map
3668                jsonContent = MultiPointHandlerSVG.GeoSVGize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, backgroundColor, mSymbol.get_WasClipped());
3669                jsonOutput.append(jsonContent);
3670            }
3671        } catch (Exception exc) {
3672            String st = JavaRendererUtilities.getStackTrace(exc);
3673            jsonOutput = new StringBuilder();
3674            jsonOutput.append("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + ": " + "- ");
3675            jsonOutput.append(exc.getMessage() + " - ");
3676            jsonOutput.append(st);
3677            jsonOutput.append("\"}");
3678
3679            ErrorLogger.LogException("MultiPointHandler", "RenderBasicShape", exc);
3680        }
3681
3682        boolean debug = false;
3683        if (debug == true) {
3684            System.out.println("Symbol Code: " + symbolCode);
3685            System.out.println("Scale: " + scale);
3686            System.out.println("BBOX: " + bbox);
3687            if (controlPoints != null) {
3688                System.out.println("Geo Points: " + controlPoints);
3689            }
3690            if (bbox != null) {
3691                System.out.println("geo bounds: " + bbox);
3692            }
3693            if (rect != null) {
3694                System.out.println("pixel bounds: " + rect.toString());
3695            }
3696            if (jsonOutput != null) {
3697                System.out.println(jsonOutput.toString());
3698            }
3699        }
3700
3701        ErrorLogger.LogMessage("MultiPointHandler", "RenderBasicShape()", "exit RenderBasicShape", Level.FINER);
3702        return jsonOutput.toString();
3703
3704    }
3705}