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