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 useLinePattern = symbol.getUseLinePattern();
1648        boolean usePatternFill = symbol.getUseFillPattern();
1649        int patternFillType = 0;
1650        boolean hideOptionalLabels = false;
1651        DistanceUnit distanceUnit = null;
1652        DistanceUnit altitudeUnit = null;
1653        int pixelSize = 100;
1654        boolean keepUnitRatio = true;
1655        double patternScale = RendererSettings.getInstance().getPatternScale();
1656
1657        try {
1658
1659            // The following attirubtes are labels.  All of them
1660            // are strings and can be added on the creation of the
1661            // MilStdSymbol by adding to a Map and passing in the
1662            // modifiers parameter.
1663            if (saModifiers != null) {
1664                if (saModifiers.containsKey(Modifiers.C_QUANTITY)) {
1665                    modifiers.put(Modifiers.C_QUANTITY, String.valueOf(saModifiers.get(Modifiers.C_QUANTITY)));
1666                }
1667
1668                if (saModifiers.containsKey(Modifiers.H_ADDITIONAL_INFO_1)) {
1669                    modifiers.put(Modifiers.H_ADDITIONAL_INFO_1, String.valueOf(saModifiers.get(Modifiers.H_ADDITIONAL_INFO_1)));
1670                }
1671
1672                if (saModifiers.containsKey(Modifiers.H1_ADDITIONAL_INFO_2)) {
1673                    modifiers.put(Modifiers.H1_ADDITIONAL_INFO_2, String.valueOf(saModifiers.get(Modifiers.H1_ADDITIONAL_INFO_2)));
1674                }
1675
1676                if (saModifiers.containsKey(Modifiers.H2_ADDITIONAL_INFO_3)) {
1677                    modifiers.put(Modifiers.H2_ADDITIONAL_INFO_3, String.valueOf(saModifiers.get(Modifiers.H2_ADDITIONAL_INFO_3)));
1678                }
1679
1680                if (saModifiers.containsKey(Modifiers.N_HOSTILE)) {
1681                    if (saModifiers.get(Modifiers.N_HOSTILE) == null) {
1682                        modifiers.put(Modifiers.N_HOSTILE, "");
1683                    } else {
1684                        modifiers.put(Modifiers.N_HOSTILE, String.valueOf(saModifiers.get(Modifiers.N_HOSTILE)));
1685                    }
1686                }
1687
1688                if (saModifiers.containsKey(Modifiers.Q_DIRECTION_OF_MOVEMENT)) {
1689                    modifiers.put(Modifiers.Q_DIRECTION_OF_MOVEMENT, String.valueOf(saModifiers.get(Modifiers.Q_DIRECTION_OF_MOVEMENT)));
1690                }
1691
1692                if (saModifiers.containsKey(Modifiers.T_UNIQUE_DESIGNATION_1)) {
1693                    modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, String.valueOf(saModifiers.get(Modifiers.T_UNIQUE_DESIGNATION_1)));
1694                }
1695
1696                if (saModifiers.containsKey(Modifiers.T1_UNIQUE_DESIGNATION_2)) {
1697                    modifiers.put(Modifiers.T1_UNIQUE_DESIGNATION_2, String.valueOf(saModifiers.get(Modifiers.T1_UNIQUE_DESIGNATION_2)));
1698                }
1699
1700                if (saModifiers.containsKey(Modifiers.V_EQUIP_TYPE)) {
1701                    modifiers.put(Modifiers.V_EQUIP_TYPE, String.valueOf(saModifiers.get(Modifiers.V_EQUIP_TYPE)));
1702                }
1703
1704                if (saModifiers.containsKey(Modifiers.AS_COUNTRY)) {
1705                    modifiers.put(Modifiers.AS_COUNTRY, String.valueOf(saModifiers.get(Modifiers.AS_COUNTRY)));
1706                } else if (SymbolID.getCountryCode(symbol.getSymbolID()) > 0 && !GENCLookup.getInstance().get3CharCode(SymbolID.getCountryCode(symbol.getSymbolID())).equals("")) {
1707                    modifiers.put(Modifiers.AS_COUNTRY, GENCLookup.getInstance().get3CharCode(SymbolID.getCountryCode(symbol.getSymbolID())));
1708                }
1709
1710                if (saModifiers.containsKey(Modifiers.AP_TARGET_NUMBER)) {
1711                    modifiers.put(Modifiers.AP_TARGET_NUMBER, String.valueOf(saModifiers.get(Modifiers.AP_TARGET_NUMBER)));
1712                }
1713
1714                if (saModifiers.containsKey(Modifiers.W_DTG_1)) {
1715                    modifiers.put(Modifiers.W_DTG_1, String.valueOf(saModifiers.get(Modifiers.W_DTG_1)));
1716                }
1717
1718                if (saModifiers.containsKey(Modifiers.W1_DTG_2)) {
1719                    modifiers.put(Modifiers.W1_DTG_2, String.valueOf(saModifiers.get(Modifiers.W1_DTG_2)));
1720                }
1721
1722                if (saModifiers.containsKey(Modifiers.Y_LOCATION)) {
1723                    modifiers.put(Modifiers.Y_LOCATION, String.valueOf(saModifiers.get(Modifiers.Y_LOCATION)));
1724                }
1725
1726                //Required multipoint modifier arrays
1727                if (saModifiers.containsKey(Modifiers.X_ALTITUDE_DEPTH)) {
1728                    altitudes = new ArrayList<Double>();
1729                    String[] arrAltitudes = String.valueOf(saModifiers.get(Modifiers.X_ALTITUDE_DEPTH)).split(",");
1730                    for (String x : arrAltitudes) {
1731                        if (x.equals("") != true) {
1732                            altitudes.add(Double.parseDouble(x));
1733                        }
1734                    }
1735                }
1736
1737                if (saModifiers.containsKey(Modifiers.AM_DISTANCE)) {
1738                    distances = new ArrayList<Double>();
1739                    String[] arrDistances = String.valueOf(saModifiers.get(Modifiers.AM_DISTANCE)).split(",");
1740                    for (String am : arrDistances) {
1741                        if (am.equals("") != true) {
1742                            distances.add(Double.parseDouble(am));
1743                        }
1744                    }
1745                }
1746
1747                if (saModifiers.containsKey(Modifiers.AN_AZIMUTH)) {
1748                    azimuths = new ArrayList<Double>();
1749                    String[] arrAzimuths = String.valueOf(saModifiers.get(Modifiers.AN_AZIMUTH)).split(",");;
1750                    for (String an : arrAzimuths) {
1751                        if (an.equals("") != true) {
1752                            azimuths.add(Double.parseDouble(an));
1753                        }
1754                    }
1755                }
1756            }
1757            if (saAttributes != null) {
1758                // These properties are ints, not labels, they are colors.//////////////////
1759                if (saAttributes.containsKey(MilStdAttributes.FillColor)) {
1760                    fillColor = (String) saAttributes.get(MilStdAttributes.FillColor);
1761                }
1762
1763                if (saAttributes.containsKey(MilStdAttributes.LineColor)) {
1764                    lineColor = (String) saAttributes.get(MilStdAttributes.LineColor);
1765                }
1766
1767                if (saAttributes.containsKey(MilStdAttributes.LineWidth)) {
1768                    lineWidth = Integer.parseInt(saAttributes.get(MilStdAttributes.LineWidth));
1769                }
1770                
1771                if (saAttributes.containsKey(MilStdAttributes.TextColor)) {
1772                    textColor = (String) saAttributes.get(MilStdAttributes.TextColor);
1773                }
1774                
1775                if (saAttributes.containsKey(MilStdAttributes.TextBackgroundColor)) {
1776                    textBackgroundColor = (String) saAttributes.get(MilStdAttributes.TextBackgroundColor);
1777                }
1778
1779                if (saAttributes.containsKey(MilStdAttributes.AltitudeMode)) {
1780                    altMode = saAttributes.get(MilStdAttributes.AltitudeMode);
1781                }
1782
1783                if (saAttributes.containsKey(MilStdAttributes.UseDashArray)) {
1784                    useDashArray = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.UseDashArray));
1785                }
1786
1787                if (saAttributes.containsKey(MilStdAttributes.UseLinePattern)) {
1788                    useLinePattern = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.UseLinePattern));
1789                }
1790
1791                if (saAttributes.containsKey(MilStdAttributes.UsePatternFill)) {
1792                    usePatternFill = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.UsePatternFill));
1793                }
1794
1795                if (saAttributes.containsKey(MilStdAttributes.PatternFillType)) {
1796                    patternFillType = Integer.parseInt((saAttributes.get(MilStdAttributes.PatternFillType)));
1797                }
1798
1799                if (saAttributes.containsKey(MilStdAttributes.HideOptionalLabels)) {
1800                    hideOptionalLabels = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.HideOptionalLabels));
1801                }
1802
1803                if(saAttributes.containsKey(MilStdAttributes.AltitudeUnits)) {
1804                    altitudeUnit = DistanceUnit.parse(saAttributes.get(MilStdAttributes.AltitudeUnits));
1805                }
1806
1807                if(saAttributes.containsKey(MilStdAttributes.DistanceUnits)) {
1808                    distanceUnit = DistanceUnit.parse(saAttributes.get(MilStdAttributes.DistanceUnits));
1809                }
1810
1811                if(saAttributes.containsKey(MilStdAttributes.PixelSize)) {
1812                    pixelSize = Integer.parseInt(saAttributes.get(MilStdAttributes.PixelSize));
1813                    symbol.setUnitSize(pixelSize);
1814                }
1815
1816                if (saAttributes.containsKey(MilStdAttributes.KeepUnitRatio)) {
1817                    keepUnitRatio = Boolean.parseBoolean(saAttributes.get(MilStdAttributes.KeepUnitRatio));
1818                    symbol.setKeepUnitRatio(keepUnitRatio);
1819                }
1820
1821                if(saAttributes.containsKey(MilStdAttributes.PatternScale)) {
1822                    patternScale = Double.parseDouble(saAttributes.get(MilStdAttributes.PatternScale));
1823                }
1824            }
1825
1826            symbol.setModifierMap(modifiers);
1827
1828            if (fillColor != null && fillColor.equals("") == false) {
1829                symbol.setFillColor(RendererUtilities.getColorFromHexString(fillColor));
1830            } 
1831
1832            if (lineColor != null && lineColor.equals("") == false) {
1833                symbol.setLineColor(RendererUtilities.getColorFromHexString(lineColor));
1834                symbol.setTextColor(RendererUtilities.getColorFromHexString(lineColor));
1835            }
1836            else if(symbol.getLineColor()==null)
1837                symbol.setLineColor(Color.black);
1838
1839            if (lineWidth > 0) {
1840                symbol.setLineWidth(lineWidth);
1841            }
1842            
1843            if (textColor != null && textColor.equals("") == false) {
1844                symbol.setTextColor(RendererUtilities.getColorFromHexString(textColor));
1845            } else if(symbol.getTextColor()==null)
1846                symbol.setTextColor(Color.black);
1847                
1848            if (textBackgroundColor != null && textBackgroundColor.equals("") == false) {
1849                symbol.setTextBackgroundColor(RendererUtilities.getColorFromHexString(textBackgroundColor));
1850            }
1851
1852            if (altMode != null) {
1853                symbol.setAltitudeMode(altMode);
1854            }
1855
1856            symbol.setUseDashArray(useDashArray);
1857            symbol.setUseLinePattern(useLinePattern);
1858            symbol.setUseFillPattern(usePatternFill);
1859            symbol.setHideOptionalLabels(hideOptionalLabels);
1860            symbol.setAltitudeUnit(altitudeUnit);
1861            symbol.setDistanceUnit(distanceUnit);
1862            symbol.setPatternScale(patternScale);
1863
1864            // Check grpahic modifiers variables.  If we set earlier, populate
1865            // the fields, otherwise, ignore.
1866            if (altitudes != null) {
1867                symbol.setModifiers_AM_AN_X(Modifiers.X_ALTITUDE_DEPTH, altitudes);
1868            }
1869            if (distances != null) {
1870                symbol.setModifiers_AM_AN_X(Modifiers.AM_DISTANCE, distances);
1871            }
1872
1873            if (azimuths != null) {
1874                symbol.setModifiers_AM_AN_X(Modifiers.AN_AZIMUTH, azimuths);
1875            }
1876
1877            //Check if sector range fan has required min range
1878            if (SymbolUtilities.getBasicSymbolID(symbol.getSymbolID()).equals("25242200")) {
1879                if (symbol.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH) != null
1880                        && symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE) != null) {
1881                    int anCount = symbol.getModifiers_AM_AN_X(Modifiers.AN_AZIMUTH).size();
1882                    int amCount = symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE).size();
1883                    ArrayList<Double> am = null;
1884                    if (amCount < ((anCount / 2) + 1)) {
1885                        am = symbol.getModifiers_AM_AN_X(Modifiers.AM_DISTANCE);
1886                        if (am.get(0) != 0.0) {
1887                            am.add(0, 0.0);
1888                        }
1889                    }
1890                }
1891            }
1892        } catch (Exception exc2) {
1893            Log.e("MPH.populateModifiers", exc2.getMessage(), exc2);
1894        }
1895        return true;
1896
1897    }
1898
1899    private static String KMLize(String id,
1900                                 String name,
1901                                 String description,
1902                                 String symbolCode,
1903                                 ArrayList<ShapeInfo> shapes,
1904                                 ArrayList<ShapeInfo> modifiers,
1905                                 IPointConversion ipc,
1906                                 boolean normalize,
1907                                 Color textColor,
1908                                 boolean wasClipped,
1909                                 int textScaleSensitive,
1910                                 int symbolScaleSensitive) {
1911        java.lang.StringBuilder kml = new java.lang.StringBuilder();
1912        ShapeInfo tempModifier = null;
1913        String cdataStart = "<![CDATA[";
1914        String cdataEnd = "]]>";
1915        int len = shapes.size();
1916        kml.append("<Folder id=\"").append(id).append("\">");
1917        kml.append("<name>").append(cdataStart).append(name).append(cdataEnd).append("</name>");
1918        kml.append("<visibility>1</visibility>");
1919        kml.append("<description>").append(cdataStart).append(description).append(cdataEnd).append("</description>");
1920        kml.append("<ExtendedData>");
1921        kml.append("<Data name=\"symbolID\"><value>").append(symbolCode).append("</value></Data>");
1922        kml.append("<Data name=\"wasClipped\"><value>").append(wasClipped).append("</value></Data>");
1923        kml.append("<Data name=\"textScaleSensitive\"><value>").append(textScaleSensitive).append("</value></Data>");
1924        kml.append("<Data name=\"symbolScaleSensitive\"><value>").append(symbolScaleSensitive).append("</value></Data>");
1925        kml.append("</ExtendedData>");
1926        for (int i = 0; i < len; i++) {
1927            String shapesToAdd = ShapeToKMLString(shapes.get(i), ipc, normalize);
1928            kml.append(shapesToAdd);
1929        }
1930
1931        int len2 = modifiers.size();
1932
1933        for (int j = 0; j < len2; j++) {
1934
1935            tempModifier = modifiers.get(j);
1936
1937            //if(geMap)//if using google earth
1938            //assume kml text is going to be centered
1939            //AdjustModifierPointToCenter(tempModifier);
1940
1941            String labelsToAdd = LabelToKMLString(tempModifier, ipc, normalize, textColor);
1942            kml.append(labelsToAdd);
1943        }
1944
1945        kml.append("</Folder>");
1946        return kml.toString();
1947    }
1948
1949    /**
1950     * 
1951     * @param shapes
1952     * @param modifiers
1953     * @param ipc
1954     * @param geMap
1955     * @param normalize
1956     * @return 
1957     * @deprecated Use GeoJSONize()
1958     */
1959    private static String JSONize(ArrayList<ShapeInfo> shapes, ArrayList<ShapeInfo> modifiers, IPointConversion ipc, Boolean geMap, boolean normalize) {
1960        String polygons = "";
1961        String lines = "";
1962        String labels = "";
1963        String jstr = "";
1964        ShapeInfo tempModifier = null;
1965
1966        int len = shapes.size();
1967        for (int i = 0; i < len; i++) {
1968            if (jstr.length() > 0) {
1969                jstr += ",";
1970            }
1971            String shapesToAdd = ShapeToJSONString(shapes.get(i), ipc, geMap, normalize);
1972            if (shapesToAdd.length() > 0) {
1973                if (shapesToAdd.startsWith("line", 2)) {
1974                    if (lines.length() > 0) {
1975                        lines += ",";
1976                    }
1977
1978                    lines += shapesToAdd;
1979                } else if (shapesToAdd.startsWith("polygon", 2)) {
1980                    if (polygons.length() > 0) {
1981                        polygons += ",";
1982                    }
1983
1984                    polygons += shapesToAdd;
1985                }
1986            }
1987        }
1988
1989        jstr += "\"polygons\": [" + polygons + "],"
1990                + "\"lines\": [" + lines + "],";
1991        int len2 = modifiers.size();
1992        labels = "";
1993        for (int j = 0; j < len2; j++) {
1994            tempModifier = modifiers.get(j);
1995            if (geMap) {
1996                AdjustModifierPointToCenter(tempModifier);
1997            }
1998            String labelsToAdd = LabelToJSONString(tempModifier, ipc, normalize);
1999            if (labelsToAdd.length() > 0) {
2000                if (labels.length() > 0) {
2001                    labels += ",";
2002                }
2003
2004                labels += labelsToAdd;
2005
2006            }
2007        }
2008        jstr += "\"labels\": [" + labels + "]";
2009        return jstr;
2010    }
2011
2012    static Color getIdealTextBackgroundColor(Color fgColor) {
2013        //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "in function", Level.SEVERE);
2014        try {
2015            //an array of three elements containing the
2016            //hue, saturation, and brightness (in that order),
2017            //of the color with the indicated red, green, and blue components/
2018            float hsbvals[] = new float[3];
2019
2020            if (fgColor != null) {/*
2021                 Color.RGBtoHSB(fgColor.getRed(), fgColor.getGreen(), fgColor.getBlue(), hsbvals);
2022
2023                 if(hsbvals != null)
2024                 {
2025                 //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "length: " + String.valueOf(hsbvals.length));
2026                 //ErrorLogger.LogMessage("SymbolDraw","getIdealtextBGColor", "H: " + String.valueOf(hsbvals[0]) + " S: " + String.valueOf(hsbvals[1]) + " B: " + String.valueOf(hsbvals[2]),Level.SEVERE);
2027                 if(hsbvals[2] > 0.6)
2028                 return Color.BLACK;
2029                 else
2030                 return Color.WHITE;
2031                 }*/
2032
2033                int nThreshold = RendererSettings.getInstance().getTextBackgroundAutoColorThreshold();//160;
2034                int bgDelta = (int) ((fgColor.getRed() * 0.299) + (fgColor.getGreen() * 0.587) + (fgColor.getBlue() * 0.114));
2035                //ErrorLogger.LogMessage("bgDelta: " + String.valueOf(255-bgDelta));
2036                //if less than threshold, black, otherwise white.
2037                //return (255 - bgDelta < nThreshold) ? Color.BLACK : Color.WHITE;//new Color(0, 0, 0, fgColor.getAlpha())
2038                return (255 - bgDelta < nThreshold) ? new Color(0, 0, 0, fgColor.getAlpha()) : new Color(255, 255, 255, fgColor.getAlpha());
2039            }
2040        } catch (Exception exc) {
2041            ErrorLogger.LogException("MultiPointHandler", "getIdealTextBackgroundColor", exc);
2042        }
2043        return Color.WHITE;
2044    }
2045
2046    private static String LabelToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize, Color textColor, Color textBackgroundColor) {
2047
2048        StringBuilder JSONed = new StringBuilder();
2049        StringBuilder properties = new StringBuilder();
2050        StringBuilder geometry = new StringBuilder();
2051
2052        Color outlineColor = getIdealTextBackgroundColor(textColor);
2053        if(textBackgroundColor != null)
2054                outlineColor = textBackgroundColor;
2055
2056        //AffineTransform at = shapeInfo.getAffineTransform();
2057        //Point2D coord = (Point2D)new Point2D.Double(at.getTranslateX(), at.getTranslateY());
2058        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2059        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
2060        Point2D geoCoord = ipc.PixelsToGeo(coord);
2061        //M. Deutch 9-27-11
2062        if (normalize) {
2063            geoCoord = NormalizeCoordToGECoord(geoCoord);
2064        }
2065        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2066        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2067        double angle = shapeInfo.getModifierAngle();
2068        coord.setLocation(longitude, latitude);
2069
2070        //diagnostic M. Deutch 10-18-11
2071        shapeInfo.setGlyphPosition(coord);
2072
2073        String text = shapeInfo.getModifierString();
2074        
2075        int justify=shapeInfo.getTextJustify();
2076        String strJustify="left";
2077        if(justify==0)
2078            strJustify="left";
2079        else if(justify==1)
2080            strJustify="center";
2081        else if(justify==2)
2082            strJustify="right";
2083
2084        
2085        RendererSettings RS = RendererSettings.getInstance();
2086
2087        if (text != null && text.equals("") == false) {
2088
2089            JSONed.append("{\"type\":\"Feature\",\"properties\":{\"label\":\"");
2090            JSONed.append(text);
2091            JSONed.append("\",\"pointRadius\":0,\"fontColor\":\"");
2092            JSONed.append(RendererUtilities.colorToHexString(textColor, false));
2093            JSONed.append("\",\"fontSize\":\"");
2094            JSONed.append(String.valueOf(RS.getMPLabelFontSize()) + "pt\"");
2095            JSONed.append(",\"fontFamily\":\"");
2096            JSONed.append(RS.getMPLabelFontName());
2097            JSONed.append(", sans-serif");
2098
2099            if (RS.getMPLabelFontType() == Typeface.BOLD) {
2100                JSONed.append("\",\"fontWeight\":\"bold\"");
2101            } else {
2102                JSONed.append("\",\"fontWeight\":\"normal\"");
2103            }
2104
2105            //JSONed.append(",\"labelAlign\":\"lm\"");
2106            JSONed.append(",\"labelAlign\":\"");
2107            JSONed.append(strJustify);
2108            JSONed.append("\",\"labelBaseline\":\"alphabetic\"");
2109
2110            //Process Anchor point if available
2111            if(shapeInfo.getModifierAnchor() != null)
2112            {
2113                Point2D anchorPoint = ipc.PixelsToGeo(shapeInfo.getModifierAnchor());
2114                if(normalize)
2115                    anchorPoint = NormalizeCoordToGECoord(anchorPoint);
2116                anchorPoint = new Point2D.Double(Math.round(anchorPoint.getX() * 100000000.0) / 100000000.0,Math.round(anchorPoint.getY() * 100000000.0) / 100000000.0);
2117
2118                JSONed.append(",\"anchorPoint\":{\"type\":\"Point\",\"coordinates\":[");
2119                JSONed.append(anchorPoint.getX());
2120                JSONed.append(",");
2121                JSONed.append(anchorPoint.getY());
2122                JSONed.append("]");
2123                JSONed.append("}");
2124
2125                JSONed.append(",\"anchorOffsetX\":").append(Math.round(shapeInfo.getModifierAnchorOffset().getX()));
2126                JSONed.append(",\"anchorOffsetY\":").append(Math.round(shapeInfo.getModifierAnchorOffset().getY()));
2127            }
2128
2129            JSONed.append(",\"labelOutlineColor\":\"");
2130            JSONed.append(RendererUtilities.colorToHexString(outlineColor, false));
2131            JSONed.append("\",\"labelOutlineWidth\":");
2132            JSONed.append("4");
2133            JSONed.append(",\"rotation\":");
2134            JSONed.append(angle);
2135            JSONed.append(",\"angle\":");
2136            JSONed.append(angle);
2137            JSONed.append("},");
2138
2139            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
2140            JSONed.append(longitude);
2141            JSONed.append(",");
2142            JSONed.append(latitude);
2143            JSONed.append("]");
2144            JSONed.append("}}");
2145
2146        } else {
2147            return "";
2148        }
2149
2150        return JSONed.toString();
2151    }
2152
2153    private static String ShapeToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2154        StringBuilder JSONed = new StringBuilder();
2155        StringBuilder properties = new StringBuilder();
2156        StringBuilder geometry = new StringBuilder();
2157        String geometryType = null;
2158        String sda = null;
2159        /*
2160         NOTE: Google Earth / KML colors are backwards.
2161         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2162         * */
2163        Color lineColor = shapeInfo.getLineColor();
2164        Color fillColor = shapeInfo.getFillColor();
2165
2166        if (shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_FILL || fillColor != null || shapeInfo.getPatternFillImage() != null) {
2167            geometryType = "\"Polygon\"";
2168        } else //if(shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_POLYLINE)
2169        {
2170            geometryType = "\"MultiLineString\"";
2171        }
2172
2173        BasicStroke stroke = null;
2174        stroke = shapeInfo.getStroke();
2175        int lineWidth = 4;
2176
2177        if (stroke != null) {
2178            lineWidth = (int) stroke.getLineWidth();
2179            //lineWidth++;
2180            //System.out.println("lineWidth: " + String.valueOf(lineWidth));
2181        }
2182
2183        //generate JSON properties for feature
2184        properties.append("\"properties\":{");
2185        properties.append("\"label\":\"\",");
2186        if (lineColor != null) {
2187            properties.append("\"strokeColor\":\"" + RendererUtilities.colorToHexString(lineColor, false) + "\",");
2188            properties.append("\"lineOpacity\":" + String.valueOf(lineColor.getAlpha() / 255f) + ",");
2189        }
2190        if (fillColor != null) {
2191            properties.append("\"fillColor\":\"" + RendererUtilities.colorToHexString(fillColor, false) + "\",");
2192            properties.append("\"fillOpacity\":" + String.valueOf(fillColor.getAlpha() / 255f) + ",");
2193        }
2194        if (shapeInfo.getPatternFillImage() != null) {
2195            properties.append("\"fillPattern\":\"" + bitmapToString(shapeInfo.getPatternFillImage()) + "\",");
2196        }
2197        if(stroke.getDashArray() != null)
2198        {
2199            float[] arrSDA = stroke.getDashArray();
2200            sda = "[";
2201            sda += String.valueOf(arrSDA[0]);
2202            if(arrSDA.length > 1)
2203            {
2204                for(int i = 1; i < arrSDA.length; i++)
2205                {
2206                    sda = sda + ", " + String.valueOf(arrSDA[i]);
2207                }
2208            }
2209            sda += "]";
2210            sda = "\"strokeDasharray\":" + sda + ",";
2211            properties.append(sda);
2212        }
2213
2214        int lineCap = stroke.getEndCap();
2215        properties.append("\"lineCap\":" + lineCap + ",");
2216
2217        String strokeWidth = String.valueOf(lineWidth);
2218        properties.append("\"strokeWidth\":" + strokeWidth + ",");
2219        properties.append("\"strokeWeight\":" + strokeWidth + "");
2220        properties.append("},");
2221
2222
2223        properties.append("\"style\":{");
2224        if (lineColor != null) {
2225            properties.append("\"stroke\":\"" + RendererUtilities.colorToHexString(lineColor, false) + "\",");
2226            properties.append("\"line-opacity\":" + String.valueOf(lineColor.getAlpha() / 255f) + ",");
2227        }
2228        if (fillColor != null) {
2229            properties.append("\"fill\":\"" + RendererUtilities.colorToHexString(fillColor, false) + "\",");
2230            properties.append("\"fill-opacity\":" + String.valueOf(fillColor.getAlpha() / 255f) + ",");
2231        }
2232        if(stroke.getDashArray() != null)
2233        {
2234            float[] da = stroke.getDashArray();
2235            sda = String.valueOf(da[0]);
2236            if(da.length > 1)
2237            {
2238                for(int i = 1; i < da.length; i++)
2239                {
2240                    sda = sda + " " + String.valueOf(da[i]);
2241                }
2242            }
2243            sda = "\"stroke-dasharray\":\"" + sda + "\",";
2244            properties.append(sda);
2245            sda = null;
2246        }
2247
2248        if(lineCap == BasicStroke.CAP_SQUARE)
2249            properties.append("\"stroke-linecap\":\"square\",");
2250        else if(lineCap == BasicStroke.CAP_ROUND)
2251            properties.append("\"stroke-linecap\":\"round\",");
2252        else if(lineCap == BasicStroke.CAP_BUTT)
2253            properties.append("\"stroke-linecap\":\"butt\",");
2254
2255        strokeWidth = String.valueOf(lineWidth);
2256        properties.append("\"stroke-width\":" + strokeWidth);
2257        properties.append("}");
2258
2259
2260        //generate JSON geometry for feature
2261        geometry.append("\"geometry\":{\"type\":");
2262        geometry.append(geometryType);
2263        geometry.append(",\"coordinates\":[");
2264
2265        ArrayList shapesArray = shapeInfo.getPolylines();
2266
2267        for (int i = 0; i < shapesArray.size(); i++) {
2268            ArrayList pointList = (ArrayList) shapesArray.get(i);
2269
2270            normalize = normalizePoints(pointList, ipc);
2271
2272            geometry.append("[");
2273
2274            //System.out.println("Pixel Coords:");
2275            for (int j = 0; j < pointList.size(); j++) {
2276                Point2D coord = (Point2D) pointList.get(j);
2277                Point2D geoCoord = ipc.PixelsToGeo(coord);
2278                //M. Deutch 9-27-11
2279                if (normalize) {
2280                    geoCoord = NormalizeCoordToGECoord(geoCoord);
2281                }
2282                double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2283                double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2284
2285                //fix for fill crossing DTL
2286                if (normalize && fillColor != null) {
2287                    if (longitude > 0) {
2288                        longitude -= 360;
2289                    }
2290                }
2291
2292                //diagnostic M. Deutch 10-18-11
2293                //set the point as geo so that the 
2294                //coord.setLocation(longitude, latitude);
2295                coord = new Point2D.Double(longitude, latitude);
2296                pointList.set(j, coord);
2297                //end section
2298
2299                geometry.append("[");
2300                geometry.append(longitude);
2301                geometry.append(",");
2302                geometry.append(latitude);
2303                geometry.append("]");
2304
2305                if (j < (pointList.size() - 1)) {
2306                    geometry.append(",");
2307                }
2308            }
2309
2310            geometry.append("]");
2311
2312            if (i < (shapesArray.size() - 1)) {
2313                geometry.append(",");
2314            }
2315        }
2316        geometry.append("]}");
2317
2318        JSONed.append("{\"type\":\"Feature\",");
2319        JSONed.append(properties.toString());
2320        JSONed.append(",");
2321        JSONed.append(geometry.toString());
2322        JSONed.append("}");
2323
2324        return JSONed.toString();
2325    }
2326
2327    private static String ImageToGeoJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2328
2329        StringBuilder JSONed = new StringBuilder();
2330        StringBuilder properties = new StringBuilder();
2331        StringBuilder geometry = new StringBuilder();
2332
2333        //AffineTransform at = shapeInfo.getAffineTransform();
2334        //Point2D coord = (Point2D)new Point2D.Double(at.getTranslateX(), at.getTranslateY());
2335        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2336        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
2337        Point2D geoCoord = ipc.PixelsToGeo(coord);
2338        //M. Deutch 9-27-11
2339        if (normalize) {
2340            geoCoord = NormalizeCoordToGECoord(geoCoord);
2341        }
2342        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2343        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2344        double angle = shapeInfo.getModifierAngle();
2345        coord.setLocation(longitude, latitude);
2346
2347        //diagnostic M. Deutch 10-18-11
2348        shapeInfo.setGlyphPosition(coord);
2349
2350        Bitmap image = shapeInfo.getModifierImage();
2351
2352        RendererSettings RS = RendererSettings.getInstance();
2353
2354        if (image != null) {
2355
2356            JSONed.append("{\"type\":\"Feature\",\"properties\":{\"image\":\"");
2357            JSONed.append(bitmapToString(image));
2358            JSONed.append("\",\"rotation\":");
2359            JSONed.append(angle);
2360            JSONed.append(",\"angle\":");
2361            JSONed.append(angle);
2362            //Process Anchor point if available
2363            if(shapeInfo.getModifierAnchor() != null)
2364            {
2365                Point2D anchorPoint = ipc.PixelsToGeo(shapeInfo.getModifierAnchor());
2366                if(normalize)
2367                    anchorPoint = NormalizeCoordToGECoord(anchorPoint);
2368                anchorPoint = new Point2D.Double(Math.round(anchorPoint.getX() * 100000000.0) / 100000000.0,Math.round(anchorPoint.getY() * 100000000.0) / 100000000.0);
2369
2370                JSONed.append(",\"anchorPoint\":{\"type\":\"Point\",\"coordinates\":[");
2371                JSONed.append(anchorPoint.getX());
2372                JSONed.append(",");
2373                JSONed.append(anchorPoint.getY());
2374                JSONed.append("]");
2375                JSONed.append("}");
2376
2377                JSONed.append(",\"anchorOffsetX\":").append(Math.round(shapeInfo.getModifierAnchorOffset().getX()));
2378                JSONed.append(",\"anchorOffsetY\":").append(Math.round(shapeInfo.getModifierAnchorOffset().getY()));
2379            }
2380            JSONed.append("},");
2381            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
2382            JSONed.append(longitude);
2383            JSONed.append(",");
2384            JSONed.append(latitude);
2385            JSONed.append("]");
2386            JSONed.append("}}");
2387
2388        } else {
2389            return "";
2390        }
2391
2392        return JSONed.toString();
2393    }
2394
2395    protected static String bitmapToString(Bitmap bitmap) {
2396        final int COMPRESSION_QUALITY = 100;
2397        String encodedImage;
2398        ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream();
2399        bitmap.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY,
2400                byteArrayBitmapStream);
2401        byte[] b = byteArrayBitmapStream.toByteArray();
2402        encodedImage = Base64.encodeToString(b, Base64.DEFAULT);
2403        return "data:image/png;base64," + encodedImage;
2404    }
2405
2406    private static String GeoJSONize(ArrayList<ShapeInfo> shapes, ArrayList<ShapeInfo> modifiers, IPointConversion ipc, boolean normalize, Color textColor, Color textBackgroundColor) {
2407
2408        String jstr = "";
2409        ShapeInfo tempModifier = null;
2410        StringBuilder fc = new StringBuilder();//JSON feature collection
2411
2412        fc.append("[");
2413
2414        int len = shapes.size();
2415        for (int i = 0; i < len; i++) {
2416
2417            String shapesToAdd = ShapeToGeoJSONString(shapes.get(i), ipc, normalize);
2418            if (shapesToAdd.length() > 0) {
2419                fc.append(shapesToAdd);
2420                if (i < len - 1) {
2421                    fc.append(",");
2422                }
2423            }
2424        }
2425
2426        int len2 = modifiers.size();
2427
2428        for (int j = 0; j < len2; j++) {
2429            tempModifier = modifiers.get(j);
2430
2431            String modifiersToAdd = null;
2432            if(modifiers.get(j).getModifierImage() != null) {
2433                modifiersToAdd = ImageToGeoJSONString(tempModifier, ipc, normalize);
2434            } else {
2435                modifiersToAdd = LabelToGeoJSONString(tempModifier, ipc, normalize, textColor, textBackgroundColor);
2436            }
2437            if (modifiersToAdd.length() > 0) {
2438                if (fc.length() > 1)
2439                    fc.append(",");
2440                fc.append(modifiersToAdd);
2441            }
2442        }
2443        fc.append("]");
2444        String GeoJSON = fc.toString();
2445        return GeoJSON;
2446    }
2447
2448    /**
2449     * 
2450     * @param shapes
2451     * @param modifiers
2452     * @param ipc
2453     * @param normalize
2454     * @deprecated
2455     */
2456    private static void MakeWWReady(
2457            ArrayList<ShapeInfo> shapes,
2458            ArrayList<ShapeInfo> modifiers,
2459            IPointConversion ipc,
2460            boolean normalize) {
2461        ShapeInfo temp = null;
2462        int len = shapes.size();
2463        for (int i = 0; i < len; i++) {
2464
2465            temp = ShapeToWWReady(shapes.get(i), ipc, normalize);
2466            shapes.set(i, temp);
2467
2468        }
2469
2470        int len2 = modifiers.size();
2471        ShapeInfo tempModifier = null;
2472        for (int j = 0; j < len2; j++) {
2473
2474            tempModifier = modifiers.get(j);
2475
2476            //Do we need this for World Wind?
2477            tempModifier = LabelToWWReady(tempModifier, ipc, normalize);
2478            modifiers.set(j, tempModifier);
2479
2480        }
2481
2482    }
2483
2484    static Boolean normalizePoints(ArrayList<Point2D.Double> shape, IPointConversion ipc) {
2485        ArrayList geoCoords = new ArrayList();
2486        int n = shape.size();
2487        //for (int j = 0; j < shape.size(); j++) 
2488        for (int j = 0; j < n; j++) {
2489            Point2D coord = shape.get(j);
2490            Point2D geoCoord = ipc.PixelsToGeo(coord);
2491            geoCoord = NormalizeCoordToGECoord(geoCoord);
2492            double latitude = geoCoord.getY();
2493            double longitude = geoCoord.getX();
2494            Point2D pt2d = new Point2D.Double(longitude, latitude);
2495            geoCoords.add(pt2d);
2496        }
2497        Boolean normalize = crossesIDL(geoCoords);
2498        return normalize;
2499    }
2500
2501    /**
2502     * @deprecated
2503     */
2504    private static Boolean IsOnePointSymbolCode(String symbolCode) {
2505        String basicCode = SymbolUtilities.getBasicSymbolID(symbolCode);
2506        //TODO: Revisit for basic shapes
2507        //some airspaces affected
2508        if (symbolCode.equals("CAKE-----------")) {
2509            return true;
2510        } else if (symbolCode.equals("CYLINDER-------")) {
2511            return true;
2512        } else if (symbolCode.equals("RADARC---------")) {
2513            return true;
2514        }
2515
2516        return false;
2517    }
2518
2519    private static String ShapeToKMLString(ShapeInfo shapeInfo,
2520                                           IPointConversion ipc,
2521                                           boolean normalize) {
2522        java.lang.StringBuilder kml = new java.lang.StringBuilder();
2523        Color lineColor = null;
2524        Color fillColor = null;
2525        String googleLineColor = null;
2526        String googleFillColor = null;
2527        BasicStroke stroke = null;
2528        int lineWidth = 4;
2529
2530        kml.append("<Placemark>");
2531        kml.append("<Style>");
2532
2533        lineColor = shapeInfo.getLineColor();
2534        if (lineColor != null) {
2535            googleLineColor = Integer.toHexString(shapeInfo.getLineColor().toARGB());
2536
2537            stroke = shapeInfo.getStroke();
2538
2539            if (stroke != null) {
2540                lineWidth = (int) stroke.getLineWidth();
2541            }
2542
2543            googleLineColor = JavaRendererUtilities.ARGBtoABGR(googleLineColor);
2544
2545            kml.append("<LineStyle>");
2546            kml.append("<color>" + googleLineColor + "</color>");
2547            kml.append("<colorMode>normal</colorMode>");
2548            kml.append("<width>" + String.valueOf(lineWidth) + "</width>");
2549            kml.append("</LineStyle>");
2550        }
2551
2552        fillColor = shapeInfo.getFillColor();
2553        Bitmap fillPattern = shapeInfo.getPatternFillImage();
2554        if (fillColor != null || fillPattern != null) {
2555            kml.append("<PolyStyle>");
2556
2557            if (fillColor != null) {
2558                googleFillColor = Integer.toHexString(shapeInfo.getFillColor().toARGB());
2559                googleFillColor = JavaRendererUtilities.ARGBtoABGR(googleFillColor);
2560                kml.append("<color>" + googleFillColor + "</color>");
2561                kml.append("<colorMode>normal</colorMode>");
2562            }
2563            if (fillPattern != null){
2564                kml.append("<shader>" + bitmapToString(fillPattern) + "</shader>");
2565            }
2566
2567            kml.append("<fill>1</fill>");
2568            if (lineColor != null) {
2569                kml.append("<outline>1</outline>");
2570            } else {
2571                kml.append("<outline>0</outline>");
2572            }
2573            kml.append("</PolyStyle>");
2574        }
2575
2576        kml.append("</Style>");
2577
2578        ArrayList shapesArray = shapeInfo.getPolylines();
2579        int len = shapesArray.size();
2580        kml.append("<MultiGeometry>");
2581
2582        for (int i = 0; i < len; i++) {
2583            ArrayList shape = (ArrayList) shapesArray.get(i);
2584            normalize = normalizePoints(shape, ipc);
2585            if (lineColor != null && fillColor == null) {
2586                kml.append("<LineString>");
2587                kml.append("<tessellate>1</tessellate>");
2588                kml.append("<altitudeMode>clampToGround</altitudeMode>");
2589                kml.append("<coordinates>");
2590                int n = shape.size();
2591                //for (int j = 0; j < shape.size(); j++) 
2592                for (int j = 0; j < n; j++) {
2593                    Point2D coord = (Point2D) shape.get(j);
2594                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2595                    if (normalize) {
2596                        geoCoord = NormalizeCoordToGECoord(geoCoord);
2597                    }
2598
2599                    double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2600                    double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2601
2602                    kml.append(longitude);
2603                    kml.append(",");
2604                    kml.append(latitude);
2605                    if(j<shape.size()-1)
2606                        kml.append(" ");
2607                }
2608
2609                kml.append("</coordinates>");
2610                kml.append("</LineString>");
2611            }
2612
2613            if (fillColor != null) {
2614
2615                if (i == 0) {
2616                    kml.append("<Polygon>");
2617                }
2618                //kml.append("<outerBoundaryIs>");
2619                if (i == 1 && len > 1) {
2620                    kml.append("<innerBoundaryIs>");
2621                } else {
2622                    kml.append("<outerBoundaryIs>");
2623                }
2624                kml.append("<LinearRing>");
2625                kml.append("<altitudeMode>clampToGround</altitudeMode>");
2626                kml.append("<tessellate>1</tessellate>");
2627                kml.append("<coordinates>");
2628
2629                int n = shape.size();
2630                //for (int j = 0; j < shape.size(); j++) 
2631                for (int j = 0; j < n; j++) {
2632                    Point2D coord = (Point2D) shape.get(j);
2633                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2634
2635                    double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2636                    double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2637
2638                    //fix for fill crossing DTL
2639                    if (normalize) {
2640                        if (longitude > 0) {
2641                            longitude -= 360;
2642                        }
2643                    }
2644
2645                    kml.append(longitude);
2646                    kml.append(",");
2647                    kml.append(latitude);
2648                    if(j<shape.size()-1)
2649                        kml.append(" ");
2650                }
2651
2652                kml.append("</coordinates>");
2653                kml.append("</LinearRing>");
2654                if (i == 1 && len > 1) {
2655                    kml.append("</innerBoundaryIs>");
2656                } else {
2657                    kml.append("</outerBoundaryIs>");
2658                }
2659                if (i == len - 1) {
2660                    kml.append("</Polygon>");
2661                }
2662            }
2663        }
2664
2665        kml.append("</MultiGeometry>");
2666        kml.append("</Placemark>");
2667
2668        return kml.toString();
2669    }
2670
2671    /**
2672     * 
2673     * @param shapeInfo
2674     * @param ipc
2675     * @param normalize
2676     * @return
2677     * @deprecated
2678     */
2679    private static ShapeInfo ShapeToWWReady(
2680            ShapeInfo shapeInfo,
2681            IPointConversion ipc,
2682            boolean normalize) {
2683
2684        ArrayList shapesArray = shapeInfo.getPolylines();
2685        int len = shapesArray.size();
2686
2687        for (int i = 0; i < len; i++) {
2688            ArrayList shape = (ArrayList) shapesArray.get(i);
2689
2690            if (shapeInfo.getLineColor() != null) {
2691                int n = shape.size();
2692                //for (int j = 0; j < shape.size(); j++) 
2693                for (int j = 0; j < n; j++) {
2694                    Point2D coord = (Point2D) shape.get(j);
2695                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2696                    //M. Deutch 9-26-11
2697                    if (normalize) {
2698                        geoCoord = NormalizeCoordToGECoord(geoCoord);
2699                    }
2700
2701                    shape.set(j, geoCoord);
2702
2703                }
2704
2705            }
2706
2707            if (shapeInfo.getFillColor() != null) {
2708                int n = shape.size();
2709                //for (int j = 0; j < shape.size(); j++) 
2710                for (int j = 0; j < n; j++) {
2711                    Point2D coord = (Point2D) shape.get(j);
2712                    Point2D geoCoord = ipc.PixelsToGeo(coord);
2713                    //M. Deutch 9-26-11
2714                    //commenting these two lines seems to help with fill not go around the pole
2715                    //if(normalize)
2716                    //geoCoord=NormalizeCoordToGECoord(geoCoord);
2717
2718                    shape.set(j, geoCoord);
2719                }
2720            }
2721        }
2722
2723        return shapeInfo;
2724    }
2725
2726    private static ShapeInfo LabelToWWReady(ShapeInfo shapeInfo,
2727            IPointConversion ipc,
2728            boolean normalize) {
2729
2730        try {
2731            Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2732            Point2D geoCoord = ipc.PixelsToGeo(coord);
2733            //M. Deutch 9-26-11
2734            if (normalize) {
2735                geoCoord = NormalizeCoordToGECoord(geoCoord);
2736            }
2737            double latitude = geoCoord.getY();
2738            double longitude = geoCoord.getX();
2739            long angle = Math.round(shapeInfo.getModifierAngle());
2740
2741            String text = shapeInfo.getModifierString();
2742
2743            if (text != null && text.equals("") == false) {
2744                shapeInfo.setModifierPosition(geoCoord);
2745            } else {
2746                return null;
2747            }
2748        } catch (Exception exc) {
2749            System.err.println(exc.getMessage());
2750            exc.printStackTrace();
2751        }
2752
2753        return shapeInfo;
2754    }
2755
2756    /**
2757     * Google earth centers text on point rather than drawing from that point.
2758     * So we need to adjust the point to where the center of the text would be.
2759     *
2760     * @param modifier
2761     */
2762    private static void AdjustModifierPointToCenter(ShapeInfo modifier) {
2763        AffineTransform at = null;
2764        try {
2765            Rectangle bounds2 = modifier.getTextLayout().getBounds();
2766            Rectangle2D bounds = new Rectangle2D.Double(bounds2.x, bounds2.y, bounds2.width, bounds2.height);
2767        } catch (Exception exc) {
2768            System.err.println(exc.getMessage());
2769            exc.printStackTrace();
2770        }
2771    }
2772
2773    /**
2774     * 
2775     * @param shapeInfo
2776     * @param ipc
2777     * @param geMap
2778     * @param normalize
2779     * @return
2780     * @deprecated
2781     */
2782    private static String ShapeToJSONString(ShapeInfo shapeInfo, IPointConversion ipc, Boolean geMap, boolean normalize) {
2783        StringBuilder JSONed = new StringBuilder();
2784        /*
2785         NOTE: Google Earth / KML colors are backwards.
2786         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2787         * */
2788        String fillColor = null;
2789        String lineColor = null;
2790
2791        if (shapeInfo.getLineColor() != null) {
2792            lineColor = Integer.toHexString(shapeInfo.getLineColor().toARGB());
2793            if (geMap) {
2794                lineColor = JavaRendererUtilities.ARGBtoABGR(lineColor);
2795            }
2796
2797        }
2798        if (shapeInfo.getFillColor() != null) {
2799            fillColor = Integer.toHexString(shapeInfo.getFillColor().toARGB());
2800            if (geMap) {
2801                fillColor = JavaRendererUtilities.ARGBtoABGR(fillColor);
2802            }
2803        }
2804
2805        BasicStroke stroke = null;
2806        stroke = shapeInfo.getStroke();
2807        int lineWidth = 4;
2808
2809        if (stroke != null) {
2810            lineWidth = (int) stroke.getLineWidth();
2811        }
2812
2813        ArrayList shapesArray = shapeInfo.getPolylines();
2814        int n = shapesArray.size();
2815        //for (int i = 0; i < shapesArray.size(); i++) 
2816        for (int i = 0; i < n; i++) {
2817            ArrayList shape = (ArrayList) shapesArray.get(i);
2818
2819            if (fillColor != null) {
2820                JSONed.append("{\"polygon\":[");
2821            } else {
2822                JSONed.append("{\"line\":[");
2823            }
2824
2825            int t = shape.size();
2826            //for (int j = 0; j < shape.size(); j++) 
2827            for (int j = 0; j < t; j++) {
2828                Point2D coord = (Point2D) shape.get(j);
2829                Point2D geoCoord = ipc.PixelsToGeo(coord);
2830                //M. Deutch 9-27-11
2831                if (normalize) {
2832                    geoCoord = NormalizeCoordToGECoord(geoCoord);
2833                }
2834                double latitude = geoCoord.getY();
2835                double longitude = geoCoord.getX();
2836
2837                //diagnostic M. Deutch 10-18-11
2838                //set the point as geo so that the 
2839                coord = new Point2D.Double(longitude, latitude);
2840                shape.set(j, coord);
2841
2842                JSONed.append("[");
2843                JSONed.append(longitude);
2844                JSONed.append(",");
2845                JSONed.append(latitude);
2846                JSONed.append("]");
2847
2848                if (j < (shape.size() - 1)) {
2849                    JSONed.append(",");
2850                }
2851            }
2852
2853            JSONed.append("]");
2854            if (lineColor != null) {
2855                JSONed.append(",\"lineColor\":\"");
2856                JSONed.append(lineColor);
2857
2858                JSONed.append("\"");
2859            }
2860            if (fillColor != null) {
2861                JSONed.append(",\"fillColor\":\"");
2862                JSONed.append(fillColor);
2863                JSONed.append("\"");
2864            }
2865
2866            JSONed.append(",\"lineWidth\":\"");
2867            JSONed.append(String.valueOf(lineWidth));
2868            JSONed.append("\"");
2869
2870            JSONed.append("}");
2871
2872            if (i < (shapesArray.size() - 1)) {
2873                JSONed.append(",");
2874            }
2875        }
2876
2877        return JSONed.toString();
2878    }
2879
2880    private static String LabelToKMLString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize, Color textColor) {
2881        java.lang.StringBuilder kml = new java.lang.StringBuilder();
2882
2883        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2884        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY());
2885        Point2D geoCoord = ipc.PixelsToGeo(coord);
2886        //M. Deutch 9-26-11
2887        if (normalize) {
2888            geoCoord = NormalizeCoordToGECoord(geoCoord);
2889        }
2890        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
2891        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
2892        long angle = Math.round(shapeInfo.getModifierAngle());
2893
2894        String text = shapeInfo.getModifierString();
2895
2896        String cdataStart = "<![CDATA[";
2897        String cdataEnd = "]]>";
2898
2899        String color = Integer.toHexString(textColor.toARGB());
2900        color = JavaRendererUtilities.ARGBtoABGR(color);
2901        float kmlScale = RendererSettings.getInstance().getKMLLabelScale();
2902
2903        if (kmlScale > 0 && text != null && text.equals("") == false) {
2904            kml.append("<Placemark>");//("<Placemark id=\"" + id + "_lp" + i + "\">");
2905            kml.append("<name>" + cdataStart + text + cdataEnd + "</name>");
2906            kml.append("<Style>");
2907            kml.append("<IconStyle>");
2908            kml.append("<scale>" + kmlScale + "</scale>");
2909            kml.append("<heading>" + angle + "</heading>");
2910            kml.append("<Icon>");
2911            kml.append("<href></href>");
2912            kml.append("</Icon>");
2913            kml.append("</IconStyle>");
2914            kml.append("<LabelStyle>");
2915            kml.append("<color>" + color + "</color>");
2916            kml.append("<scale>" + String.valueOf(kmlScale) +"</scale>");
2917            kml.append("</LabelStyle>");
2918            kml.append("</Style>");
2919            kml.append("<Point>");
2920            kml.append("<extrude>1</extrude>");
2921            kml.append("<altitudeMode>relativeToGround</altitudeMode>");
2922            kml.append("<coordinates>");
2923            kml.append(longitude);
2924            kml.append(",");
2925            kml.append(latitude);
2926            kml.append("</coordinates>");
2927            kml.append("</Point>");
2928            kml.append("</Placemark>");
2929        } else {
2930            return "";
2931        }
2932
2933        return kml.toString();
2934    }
2935
2936    /**
2937     * 
2938     * @param shapeInfo
2939     * @param ipc
2940     * @param normalize
2941     * @return
2942     * @deprecated
2943     */
2944    private static String LabelToJSONString(ShapeInfo shapeInfo, IPointConversion ipc, boolean normalize) {
2945        StringBuilder JSONed = new StringBuilder();
2946        /*
2947         NOTE: Google Earth / KML colors are backwards.
2948         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
2949         * */
2950        JSONed.append("{\"label\":");
2951
2952        Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
2953        Point2D geoCoord = ipc.PixelsToGeo(coord);
2954        if (normalize) {
2955            geoCoord = NormalizeCoordToGECoord(geoCoord);
2956        }
2957        double latitude = geoCoord.getY();
2958        double longitude = geoCoord.getX();
2959        double angle = shapeInfo.getModifierAngle();
2960        coord.setLocation(longitude, latitude);
2961
2962        shapeInfo.setGlyphPosition(coord);
2963
2964        String text = shapeInfo.getModifierString();
2965
2966        if (text != null && text.equals("") == false) {
2967            JSONed.append("[");
2968            JSONed.append(longitude);
2969            JSONed.append(",");
2970            JSONed.append(latitude);
2971            JSONed.append("]");
2972
2973            JSONed.append(",\"text\":\"");
2974            JSONed.append(text);
2975            JSONed.append("\"");
2976
2977            JSONed.append(",\"angle\":\"");
2978            JSONed.append(angle);
2979            JSONed.append("\"}");
2980        } else {
2981            return "";
2982        }
2983
2984        return JSONed.toString();
2985    }
2986
2987    public static String canRenderMultiPoint(String symbolID, Map<String,String> modifiers, int numPoints) {
2988        try {
2989            String basicID = SymbolUtilities.getBasicSymbolID(symbolID);
2990            MSInfo info = MSLookup.getInstance().getMSLInfo(symbolID);
2991
2992            if (info == null) {
2993                if (SymbolID.getVersion(symbolID) >= SymbolID.Version_2525E) {
2994                    return"Basic ID: " + basicID + " not recognized in version E (15)";
2995                } else {
2996                    return "Basic ID: " + basicID + " not recognized in version D (11)";
2997                }
2998            }
2999
3000            int drawRule = info.getDrawRule();
3001
3002            if (drawRule == DrawRules.DONOTDRAW) {
3003                return "Basic ID: " + basicID + " has no draw rule";
3004            } else if (!SymbolUtilities.isMultiPoint(symbolID)) {
3005                return "Basic ID: " + basicID + " is not a multipoint symbol";
3006            } else if (numPoints < info.getMinPointCount()) {
3007                return "Basic ID: " + basicID + " requires a minimum of " + String.valueOf(info.getMinPointCount()) + " points. " + String.valueOf(numPoints) + " are present.";
3008            }
3009
3010            //now check for required modifiers
3011            ArrayList<Double> AM = new ArrayList();
3012            ArrayList<Double> AN = new ArrayList();
3013            if (modifiers.containsKey(Modifiers.AM_DISTANCE)) {
3014                String[] amArray = modifiers.get(Modifiers.AM_DISTANCE).split(",");
3015                for (String str : amArray) {
3016                    if (!str.equals("")) {
3017                        AM.add(Double.parseDouble(str));
3018                    }
3019                }
3020            }
3021            if (modifiers.containsKey(Modifiers.AN_AZIMUTH)) {
3022                String[] anArray = modifiers.get(Modifiers.AN_AZIMUTH).split(",");
3023                for (String str : anArray) {
3024                    if (!str.equals("")) {
3025                        AN.add(Double.parseDouble(str));
3026                    }
3027                }
3028            }
3029
3030            return hasRequiredModifiers(symbolID, drawRule, AM, AN);
3031        } catch (Exception exc) {
3032            ErrorLogger.LogException("MultiPointHandler", "canRenderMultiPoint", exc);
3033            return "false: " + exc.getMessage();
3034        }
3035    }
3036
3037    static private String hasRequiredModifiers(String symbolID, int drawRule, ArrayList<Double> AM, ArrayList<Double> AN) {
3038
3039        String message = symbolID;
3040        try {
3041            if (drawRule > 700) {
3042                if (drawRule == DrawRules.CIRCULAR1)
3043                {
3044                    if (AM != null && AM.size() > 0) {
3045                        return "true";
3046                    } else {
3047                        message += " requires a modifiers object that has 1 distance/AM value.";
3048                        return message;
3049                    }
3050                } else if (drawRule == DrawRules.RECTANGULAR2)
3051                {
3052                    if (AM != null && AM.size() >= 2
3053                            && AN != null && AN.size() >= 1) {
3054                        return "true";
3055                    } else {
3056                        message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
3057                        return message;
3058                    }
3059                } else if (drawRule == DrawRules.ARC1)
3060                {
3061                    if (AM != null && AM.size() >= 1
3062                            && AN != null && AN.size() >= 2) {
3063                        return "true";
3064                    } else {
3065                        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.");
3066                        return message;
3067                    }
3068                } else if (drawRule == DrawRules.CIRCULAR2)
3069                {
3070                    if (AM != null && AM.size() > 0) {
3071                        return "true";
3072                    } else {
3073                        message += (" requires a modifiers object that has at least 1 distance/AM value");
3074                        return message;
3075                    }
3076                } else if (drawRule == DrawRules.RECTANGULAR1)
3077                {
3078                    if (AM != null && AM.size() > 0) {
3079                        return "true";
3080                    } else {
3081                        message += (" requires a modifiers object that has 1 distance/AM value.");
3082                        return message;
3083                    }
3084                } else if (drawRule == DrawRules.ELLIPSE1)
3085                {
3086                    if (AM != null && AM.size() >= 2
3087                            && AN != null && AN.size() >= 1) {
3088                        return "true";
3089                    } else {
3090                        message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
3091                        return message;
3092                    }
3093                }
3094                else if (drawRule == DrawRules.RECTANGULAR3)
3095                {
3096                    if (AM != null && AM.size() >= 1) {
3097                        return "true";
3098                    } else {
3099                        message += (" requires a modifiers object that has 1 distance/AM value.");
3100                        return message;
3101                    }
3102                } else {
3103                    //should never get here
3104                    return "true";
3105                }
3106            } else if (drawRule == DrawRules.POINT17) {
3107                if (AM != null && AM.size() >= 2
3108                        && AN != null && AN.size() >= 1) {
3109                    return "true";
3110                } else {
3111                    message += (" requires a modifiers object that has 2 distance/AM values and 1 azimuth/AN value.");
3112                    return message;
3113                }
3114            } else if (drawRule == DrawRules.POINT18) {
3115                if (AM != null && AM.size() >= 2
3116                        && AN != null && AN.size() >= 2) {
3117                    return "true";
3118                } else {
3119                    message += (" requires a modifiers object that has 2 distance/AM values and 2 azimuth/AN values.");
3120                    return message;
3121                }
3122            } else if (drawRule == DrawRules.CORRIDOR1) {
3123                if (AM != null && AM.size() > 0) {
3124                    return "true";
3125                } else {
3126                    message += (" requires a modifiers object that has 1 distance/AM value.");
3127                    return message;
3128                }
3129            } else {
3130                //no required parameters
3131                return "true";
3132            }
3133        } catch (Exception exc) {
3134            ErrorLogger.LogException("MultiPointHandler", "hasRequiredModifiers", exc);
3135            return "true";
3136        }
3137    }
3138
3139    /**
3140     *
3141     * @param id
3142     * @param name
3143     * @param description
3144     * @param basicShapeType
3145     * @param controlPoints
3146     * @param scale
3147     * @param bbox
3148     * @param symbolModifiers
3149     * @param symbolAttributes
3150     * @return
3151     */
3152    public static MilStdSymbol RenderBasicShapeAsMilStdSymbol(String id,
3153                                                          String name,
3154                                                          String description,
3155                                                          int basicShapeType,
3156                                                          String controlPoints,
3157                                                          Double scale,
3158                                                          String bbox,
3159                                                          Map<String,String> symbolModifiers,
3160                                                          Map<String,String> symbolAttributes)
3161    {
3162        MilStdSymbol mSymbol = null;
3163        boolean normalize = true;
3164        Double controlLat = 0.0;
3165        Double controlLong = 0.0;
3166        //String jsonContent = "";
3167
3168        Rectangle rect = null;
3169
3170        //for symbol & line fill
3171        ArrayList<POINT2> tgPoints = null;
3172
3173        String[] coordinates = controlPoints.split(" ");
3174        ArrayList<ShapeInfo> shapes = null;//new ArrayList<ShapeInfo>();
3175        ArrayList<ShapeInfo> modifiers = null;//new ArrayList<ShapeInfo>();
3176        //ArrayList<Point2D> pixels = new ArrayList<Point2D>();
3177        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
3178        int len = coordinates.length;
3179
3180        IPointConversion ipc = null;
3181
3182        //Deutch moved section 6-29-11
3183        Double left = 0.0;
3184        Double right = 0.0;
3185        Double top = 0.0;
3186        Double bottom = 0.0;
3187        Point2D temp = null;
3188        Point2D ptGeoUL = null;
3189        int width = 0;
3190        int height = 0;
3191        int leftX = 0;
3192        int topY = 0;
3193        int bottomY = 0;
3194        int rightX = 0;
3195        int j = 0;
3196        ArrayList<Point2D> bboxCoords = null;
3197        if (bbox != null && bbox.equals("") == false) {
3198            String[] bounds = null;
3199            if (bbox.contains(" "))//trapezoid
3200            {
3201                bboxCoords = new ArrayList<Point2D>();
3202                double x = 0;
3203                double y = 0;
3204                String[] coords = bbox.split(" ");
3205                String[] arrCoord;
3206                for (String coord : coords) {
3207                    arrCoord = coord.split(",");
3208                    x = Double.valueOf(arrCoord[0]);
3209                    y = Double.valueOf(arrCoord[1]);
3210                    bboxCoords.add(new Point2D.Double(x, y));
3211                }
3212                //use the upper left corner of the MBR containing geoCoords
3213                //to set the converter
3214                ptGeoUL = getGeoUL(bboxCoords);
3215                left = ptGeoUL.getX();
3216                top = ptGeoUL.getY();
3217                ipc = new PointConverter(left, top, scale);
3218                Point2D ptPixels = null;
3219                Point2D ptGeo = null;
3220                int n = bboxCoords.size();
3221                //for (j = 0; j < bboxCoords.size(); j++)
3222                for (j = 0; j < n; j++) {
3223                    ptGeo = bboxCoords.get(j);
3224                    ptPixels = ipc.GeoToPixels(ptGeo);
3225                    x = ptPixels.getX();
3226                    y = ptPixels.getY();
3227                    if (x < 20) {
3228                        x = 20;
3229                    }
3230                    if (y < 20) {
3231                        y = 20;
3232                    }
3233                    ptPixels.setLocation(x, y);
3234                    //end section
3235                    bboxCoords.set(j, (Point2D) ptPixels);
3236                }
3237            } else//rectangle
3238            {
3239                bounds = bbox.split(",");
3240                left = Double.valueOf(bounds[0]);
3241                right = Double.valueOf(bounds[2]);
3242                top = Double.valueOf(bounds[3]);
3243                bottom = Double.valueOf(bounds[1]);
3244                scale = getReasonableScale(bbox, scale);
3245                ipc = new PointConverter(left, top, scale);
3246            }
3247
3248            Point2D pt2d = null;
3249            if (bboxCoords == null) {
3250                pt2d = new Point2D.Double(left, top);
3251                temp = ipc.GeoToPixels(pt2d);
3252
3253                leftX = (int) temp.getX();
3254                topY = (int) temp.getY();
3255
3256                pt2d = new Point2D.Double(right, bottom);
3257                temp = ipc.GeoToPixels(pt2d);
3258
3259                bottomY = (int) temp.getY();
3260                rightX = (int) temp.getX();
3261                //diagnostic clipping does not work for large scales
3262//                if (scale > 10e6) {
3263//                    //get widest point in the AOI
3264//                    double midLat = 0;
3265//                    if (bottom < 0 && top > 0) {
3266//                        midLat = 0;
3267//                    } else if (bottom < 0 && top < 0) {
3268//                        midLat = top;
3269//                    } else if (bottom > 0 && top > 0) {
3270//                        midLat = bottom;
3271//                    }
3272//
3273//                    temp = ipc.GeoToPixels(new Point2D.Double(right, midLat));
3274//                    rightX = (int) temp.getX();
3275//                }
3276                //end section
3277
3278                width = (int) Math.abs(rightX - leftX);
3279                height = (int) Math.abs(bottomY - topY);
3280
3281                if(width==0 || height==0)
3282                    rect=null;
3283                else
3284                    rect = new Rectangle(leftX, topY, width, height);
3285            }
3286        } else {
3287            rect = null;
3288        }
3289        //end section
3290
3291        for (int i = 0; i < len; i++) {
3292            String[] coordPair = coordinates[i].split(",");
3293            Double latitude = Double.valueOf(coordPair[1].trim());
3294            Double longitude = Double.valueOf(coordPair[0].trim());
3295            geoCoords.add(new Point2D.Double(longitude, latitude));
3296        }
3297        if (ipc == null) {
3298            Point2D ptCoordsUL = getGeoUL(geoCoords);
3299            ipc = new PointConverter(ptCoordsUL.getX(), ptCoordsUL.getY(), scale);
3300        }
3301        //if (crossesIDL(geoCoords) == true)
3302//        if(Math.abs(right-left)>180)
3303//        {
3304//            normalize = true;
3305//            ((PointConverter)ipc).set_normalize(true);
3306//        }
3307//        else {
3308//            normalize = false;
3309//            ((PointConverter)ipc).set_normalize(false);
3310//        }
3311
3312        //seems to work ok at world view
3313//        if (normalize) {
3314//            NormalizeGECoordsToGEExtents(0, 360, geoCoords);
3315//        }
3316
3317        //M. Deutch 10-3-11
3318        //must shift the rect pixels to synch with the new ipc
3319        //the old ipc was in synch with the bbox, so rect x,y was always 0,0
3320        //the new ipc synchs with the upper left of the geocoords so the boox is shifted
3321        //and therefore the clipping rectangle must shift by the delta x,y between
3322        //the upper left corner of the original bbox and the upper left corner of the geocoords
3323        ArrayList<Point2D> geoCoords2 = new ArrayList<Point2D>();
3324        geoCoords2.add(new Point2D.Double(left, top));
3325        geoCoords2.add(new Point2D.Double(right, bottom));
3326
3327//        if (normalize) {
3328//            NormalizeGECoordsToGEExtents(0, 360, geoCoords2);
3329//        }
3330
3331        //disable clipping
3332        if (crossesIDL(geoCoords) == false) {
3333            rect = null;
3334            bboxCoords = null;
3335        }
3336
3337        String symbolCode = "";
3338        try {
3339            String fillColor = null;
3340            mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
3341
3342//            mSymbol.setUseDashArray(true);
3343
3344            if (symbolModifiers != null || symbolAttributes != null) {
3345                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
3346            } else {
3347                mSymbol.setFillColor(null);
3348            }
3349
3350            if (mSymbol.getFillColor() != null) {
3351                Color fc = mSymbol.getFillColor();
3352                //fillColor = Integer.toHexString(fc.getRGB());
3353                fillColor = Integer.toHexString(fc.toARGB());
3354            }
3355
3356            TGLight tg = clsRenderer.createTGLightFromMilStdSymbolBasicShape(mSymbol, ipc, basicShapeType);
3357            ArrayList<ShapeInfo> shapeInfos = new ArrayList();
3358            ArrayList<ShapeInfo> modifierShapeInfos = new ArrayList();
3359            Object clipArea;
3360            if (bboxCoords == null) {
3361                clipArea = rect;
3362            } else {
3363                clipArea = bboxCoords;
3364            }
3365            if (clsRenderer.intersectsClipArea(tg, ipc, clipArea)) {
3366                clsRenderer.render_GE(tg, shapeInfos, modifierShapeInfos, ipc, clipArea);
3367            }
3368            mSymbol.setSymbolShapes(shapeInfos);
3369            mSymbol.setModifierShapes(modifierShapeInfos);
3370            mSymbol.set_WasClipped(tg.get_WasClipped());
3371            shapes = mSymbol.getSymbolShapes();
3372            modifiers = mSymbol.getModifierShapes();
3373
3374            //convert points////////////////////////////////////////////////////
3375            ArrayList<ArrayList<Point2D>> polylines = null;
3376            ArrayList<ArrayList<Point2D>> newPolylines = null;
3377            ArrayList<Point2D> newLine = null;
3378            for (ShapeInfo shape : shapes) {
3379                polylines = shape.getPolylines();
3380                //System.out.println("pixel polylines: " + String.valueOf(polylines));
3381                newPolylines = ConvertPolylinePixelsToCoords(polylines, ipc, normalize);
3382                shape.setPolylines(newPolylines);
3383            }
3384
3385            for (ShapeInfo label : modifiers) {
3386                Point2D pixelCoord = label.getModifierPosition();
3387                if (pixelCoord == null) {
3388                    pixelCoord = label.getGlyphPosition();
3389                }
3390                Point2D geoCoord = ipc.PixelsToGeo(pixelCoord);
3391
3392                if (normalize) {
3393                    geoCoord = NormalizeCoordToGECoord(geoCoord);
3394                }
3395
3396                double latitude = geoCoord.getY();
3397                double longitude = geoCoord.getX();
3398                label.setModifierPosition(new Point2D.Double(longitude, latitude));
3399
3400            }
3401
3402            ////////////////////////////////////////////////////////////////////
3403            mSymbol.setModifierShapes(modifiers);
3404            mSymbol.setSymbolShapes(shapes);
3405
3406        } catch (Exception exc) {
3407            System.out.println(exc.getMessage());
3408            System.out.println("Symbol Code: " + symbolCode);
3409            exc.printStackTrace();
3410        }
3411
3412        boolean debug = false;
3413        if (debug == true) {
3414            System.out.println("Symbol Code: " + symbolCode);
3415            System.out.println("Scale: " + scale);
3416            System.out.println("BBOX: " + bbox);
3417            if (controlPoints != null) {
3418                System.out.println("Geo Points: " + controlPoints);
3419            }
3420            if (bbox != null) {
3421                System.out.println("geo bounds: " + bbox);
3422            }
3423            if (rect != null) {
3424                System.out.println("pixel bounds: " + rect.toString());
3425            }
3426        }
3427
3428        return mSymbol;
3429
3430    }
3431
3432    /**
3433     *
3434     * @param id - For the client to track the symbol, not related to rendering
3435     * @param name - For the client to track the symbol, not related to rendering
3436     * @param description - For the client to track the symbol, not related to rendering
3437     * @param basicShapeType
3438     * @param controlPoints
3439     * @param scale
3440     * @param bbox
3441     * @param symbolModifiers keyed using constants from
3442     * Modifiers. Pass in comma delimited String for modifiers with multiple
3443     * values like AM, AN &amp; X
3444     * @param symbolAttributes keyed using constants from
3445     * MilStdAttributes. pass in double[] for AM, AN and X; Strings for the
3446     * rest.
3447     * @param format
3448     * @return
3449     */
3450    public static String RenderBasicShape(String id,
3451                                          String name,
3452                                          String description,
3453                                          int basicShapeType,
3454                                          String controlPoints,
3455                                          Double scale,
3456                                          String bbox,
3457                                          Map<String,String> symbolModifiers,
3458                                          Map<String,String> symbolAttributes,
3459                                          int format)//,
3460    {
3461        boolean normalize = true;
3462        //Double controlLat = 0.0;
3463        //Double controlLong = 0.0;
3464        //Double metPerPix = GeoPixelConversion.metersPerPixel(scale);
3465        //String bbox2=getBoundingRectangle(controlPoints,bbox);
3466        StringBuilder jsonOutput = new StringBuilder();
3467        String jsonContent = "";
3468
3469        Rectangle rect = null;
3470        String[] coordinates = controlPoints.split(" ");
3471        ArrayList<ShapeInfo> shapes = new ArrayList<ShapeInfo>();
3472        ArrayList<ShapeInfo> modifiers = new ArrayList<ShapeInfo>();
3473        //ArrayList<Point2D> pixels = new ArrayList<Point2D>();
3474        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
3475        int len = coordinates.length;
3476        //diagnostic create geoCoords here
3477        Point2D coordsUL=null;
3478        final String symbolCode = "";
3479
3480        for (int i = 0; i < len; i++)
3481        {
3482            String[] coordPair = coordinates[i].split(",");
3483            Double latitude = Double.valueOf(coordPair[1].trim()).doubleValue();
3484            Double longitude = Double.valueOf(coordPair[0].trim()).doubleValue();
3485            geoCoords.add(new Point2D.Double(longitude, latitude));
3486        }
3487        ArrayList<POINT2> tgPoints = null;
3488        IPointConversion ipc = null;
3489
3490        //Deutch moved section 6-29-11
3491        Double left = 0.0;
3492        Double right = 0.0;
3493        Double top = 0.0;
3494        Double bottom = 0.0;
3495        Point2D temp = null;
3496        Point2D ptGeoUL = null;
3497        int width = 0;
3498        int height = 0;
3499        int leftX = 0;
3500        int topY = 0;
3501        int bottomY = 0;
3502        int rightX = 0;
3503        int j = 0;
3504        ArrayList<Point2D> bboxCoords = null;
3505        if (bbox != null && bbox.equals("") == false) {
3506            String[] bounds = null;
3507            if (bbox.contains(" "))//trapezoid
3508            {
3509                bboxCoords = new ArrayList<Point2D>();
3510                double x = 0;
3511                double y = 0;
3512                String[] coords = bbox.split(" ");
3513                String[] arrCoord;
3514                for (String coord : coords) {
3515                    arrCoord = coord.split(",");
3516                    x = Double.valueOf(arrCoord[0]);
3517                    y = Double.valueOf(arrCoord[1]);
3518                    bboxCoords.add(new Point2D.Double(x, y));
3519                }
3520                //use the upper left corner of the MBR containing geoCoords
3521                //to set the converter
3522                ptGeoUL = getGeoUL(bboxCoords);
3523                left = ptGeoUL.getX();
3524                top = ptGeoUL.getY();
3525                String bbox2=getBboxFromCoords(bboxCoords);
3526                scale = getReasonableScale(bbox2, scale);
3527                ipc = new PointConverter(left, top, scale);
3528                Point2D ptPixels = null;
3529                Point2D ptGeo = null;
3530                int n = bboxCoords.size();
3531                //for (j = 0; j < bboxCoords.size(); j++)
3532                for (j = 0; j < n; j++) {
3533                    ptGeo = bboxCoords.get(j);
3534                    ptPixels = ipc.GeoToPixels(ptGeo);
3535                    x = ptPixels.getX();
3536                    y = ptPixels.getY();
3537                    if (x < 20) {
3538                        x = 20;
3539                    }
3540                    if (y < 20) {
3541                        y = 20;
3542                    }
3543                    ptPixels.setLocation(x, y);
3544                    //end section
3545                    bboxCoords.set(j, (Point2D) ptPixels);
3546                }
3547            } else//rectangle
3548            {
3549                bounds = bbox.split(",");
3550                left = Double.valueOf(bounds[0]);
3551                right = Double.valueOf(bounds[2]);
3552                top = Double.valueOf(bounds[3]);
3553                bottom = Double.valueOf(bounds[1]);
3554                scale = getReasonableScale(bbox, scale);
3555                ipc = new PointConverter(left, top, scale);
3556            }
3557
3558            Point2D pt2d = null;
3559            if (bboxCoords == null) {
3560                pt2d = new Point2D.Double(left, top);
3561                temp = ipc.GeoToPixels(pt2d);
3562
3563                leftX = (int) temp.getX();
3564                topY = (int) temp.getY();
3565
3566                pt2d = new Point2D.Double(right, bottom);
3567                temp = ipc.GeoToPixels(pt2d);
3568
3569                bottomY = (int) temp.getY();
3570                rightX = (int) temp.getX();
3571
3572                width = (int) Math.abs(rightX - leftX);
3573                height = (int) Math.abs(bottomY - topY);
3574
3575                rect = new Rectangle(leftX, topY, width, height);
3576            }
3577        } else {
3578            rect = null;
3579        }
3580
3581        if (ipc == null) {
3582            Point2D ptCoordsUL = getGeoUL(geoCoords);
3583            ipc = new PointConverter(ptCoordsUL.getX(), ptCoordsUL.getY(), scale);
3584        }
3585
3586        ArrayList<Point2D> geoCoords2 = new ArrayList<Point2D>();
3587        geoCoords2.add(new Point2D.Double(left, top));
3588        geoCoords2.add(new Point2D.Double(right, bottom));
3589
3590//        if (normalize) {
3591//            NormalizeGECoordsToGEExtents(0, 360, geoCoords2);
3592//        }
3593
3594        try {
3595
3596            //String fillColor = null;
3597            MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
3598
3599            if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG){
3600                // Use dash array and hatch pattern fill for SVG output
3601                symbolAttributes.put(MilStdAttributes.UseDashArray, "true");
3602                symbolAttributes.put(MilStdAttributes.UsePatternFill, "true");
3603            }
3604
3605            if (symbolModifiers != null || symbolAttributes != null) {
3606                populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
3607            } else {
3608                mSymbol.setFillColor(null);
3609            }
3610
3611            TGLight tg = clsRenderer.createTGLightFromMilStdSymbolBasicShape(mSymbol, ipc, basicShapeType);
3612            ArrayList<ShapeInfo> shapeInfos = new ArrayList();
3613            ArrayList<ShapeInfo> modifierShapeInfos = new ArrayList();
3614            Object clipArea;
3615            if (bboxCoords == null) {
3616                clipArea = rect;
3617            } else {
3618                clipArea = bboxCoords;
3619            }
3620            if (clsRenderer.intersectsClipArea(tg, ipc, clipArea)) {
3621                clsRenderer.render_GE(tg, shapeInfos, modifierShapeInfos, ipc, clipArea);
3622            }
3623            mSymbol.setSymbolShapes(shapeInfos);
3624            mSymbol.setModifierShapes(modifierShapeInfos);
3625            mSymbol.set_WasClipped(tg.get_WasClipped());
3626            shapes = mSymbol.getSymbolShapes();
3627            modifiers = mSymbol.getModifierShapes();
3628
3629            if (format == WebRenderer.OUTPUT_FORMAT_JSON) {
3630                jsonOutput.append("{\"type\":\"symbol\",");
3631                jsonContent = JSONize(shapes, modifiers, ipc, true, normalize);
3632                jsonOutput.append(jsonContent);
3633                jsonOutput.append("}");
3634            } else if (format == WebRenderer.OUTPUT_FORMAT_KML) {
3635                Color textColor = mSymbol.getTextColor();
3636                if(textColor==null)
3637                    textColor=mSymbol.getLineColor();
3638
3639                jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, mSymbol.getWasClipped(), mSymbol.isTextScaleSensitive(), mSymbol.isSymbolScaleSensitive());
3640                jsonOutput.append(jsonContent);
3641            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOJSON)
3642            {
3643                jsonOutput.append("{\"type\":\"FeatureCollection\",\"features\":");
3644                jsonContent = GeoJSONize(shapes, modifiers, ipc, normalize, mSymbol.getTextColor(), mSymbol.getTextBackgroundColor());
3645                jsonOutput.append(jsonContent);
3646
3647                //moving meta data properties to the last feature with no coords as feature collection doesn't allow properties
3648                jsonOutput.replace(jsonOutput.toString().length()-1,jsonOutput.toString().length(),"" );
3649                if (jsonContent.length() > 2)
3650                    jsonOutput.append(",");
3651                jsonOutput.append("{\"type\": \"Feature\",\"geometry\": { \"type\": \"Polygon\",\"coordinates\": [ ]}");
3652
3653                jsonOutput.append(",\"properties\":{\"id\":\"");
3654                jsonOutput.append(id);
3655                jsonOutput.append("\",\"name\":\"");
3656                jsonOutput.append(name);
3657                jsonOutput.append("\",\"description\":\"");
3658                jsonOutput.append(description);
3659                jsonOutput.append("\",\"symbolID\":\"");
3660                jsonOutput.append(symbolCode);
3661                jsonOutput.append("\",\"wasClipped\":\"");
3662                jsonOutput.append(String.valueOf(mSymbol.getWasClipped()));
3663                jsonOutput.append("\",\"textScaleSensitive\":\"");
3664                jsonOutput.append(String.valueOf(mSymbol.isTextScaleSensitive()));
3665                jsonOutput.append("\",\"symbolScaleSensitive\":\"");
3666                jsonOutput.append(String.valueOf(mSymbol.isSymbolScaleSensitive()));
3667                //jsonOutput.append("\"}}");
3668
3669                jsonOutput.append("\"}}]}");
3670            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG) {
3671                String textColor = mSymbol.getTextColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextColor(), false) : "";
3672                String backgroundColor = mSymbol.getTextBackgroundColor() != null ? RendererUtilities.colorToHexString(mSymbol.getTextBackgroundColor(), false) : "";
3673                //returns an svg with a geoTL and geoBR value to use to place the canvas on the map
3674                jsonContent = MultiPointHandlerSVG.GeoSVGize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, backgroundColor, mSymbol.get_WasClipped());
3675                jsonOutput.append(jsonContent);
3676            }
3677        } catch (Exception exc) {
3678            String st = JavaRendererUtilities.getStackTrace(exc);
3679            jsonOutput = new StringBuilder();
3680            jsonOutput.append("{\"type\":\"error\",\"error\":\"There was an error creating the MilStdSymbol " + symbolCode + ": " + "- ");
3681            jsonOutput.append(exc.getMessage() + " - ");
3682            jsonOutput.append(st);
3683            jsonOutput.append("\"}");
3684
3685            ErrorLogger.LogException("MultiPointHandler", "RenderBasicShape", exc);
3686        }
3687
3688        boolean debug = false;
3689        if (debug == true) {
3690            System.out.println("Symbol Code: " + symbolCode);
3691            System.out.println("Scale: " + scale);
3692            System.out.println("BBOX: " + bbox);
3693            if (controlPoints != null) {
3694                System.out.println("Geo Points: " + controlPoints);
3695            }
3696            if (bbox != null) {
3697                System.out.println("geo bounds: " + bbox);
3698            }
3699            if (rect != null) {
3700                System.out.println("pixel bounds: " + rect.toString());
3701            }
3702            if (jsonOutput != null) {
3703                System.out.println(jsonOutput.toString());
3704            }
3705        }
3706
3707        ErrorLogger.LogMessage("MultiPointHandler", "RenderBasicShape()", "exit RenderBasicShape", Level.FINER);
3708        return jsonOutput.toString();
3709
3710    }
3711}