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