001package armyc2.c5isr.web.render;
002
003import android.graphics.Bitmap;
004import android.graphics.Typeface;
005
006import java.util.ArrayList;
007import java.util.Collections;
008import java.util.Map;
009import java.util.logging.Level;
010
011import armyc2.c5isr.JavaLineArray.POINT2;
012import armyc2.c5isr.JavaTacticalRenderer.TGLight;
013import armyc2.c5isr.RenderMultipoints.clsRenderer;
014import armyc2.c5isr.graphics2d.BasicStroke;
015import armyc2.c5isr.graphics2d.Point2D;
016import armyc2.c5isr.graphics2d.Rectangle;
017import armyc2.c5isr.renderer.utilities.Color;
018import armyc2.c5isr.renderer.utilities.DrawRules;
019import armyc2.c5isr.renderer.utilities.ErrorLogger;
020import armyc2.c5isr.renderer.utilities.IPointConversion;
021import armyc2.c5isr.renderer.utilities.MSLookup;
022import armyc2.c5isr.renderer.utilities.MilStdAttributes;
023import armyc2.c5isr.renderer.utilities.MilStdSymbol;
024import armyc2.c5isr.renderer.utilities.Modifiers;
025import armyc2.c5isr.renderer.utilities.RendererSettings;
026import armyc2.c5isr.renderer.utilities.RendererUtilities;
027import armyc2.c5isr.renderer.utilities.ShapeInfo;
028import armyc2.c5isr.renderer.utilities.SymbolUtilities;
029import armyc2.c5isr.web.render.utilities.Basic3DShapes;
030import armyc2.c5isr.web.render.utilities.JavaRendererUtilities;
031import armyc2.c5isr.web.render.utilities.Point3D;
032import armyc2.c5isr.web.render.utilities.ShapeInfo3D;
033
034public class Shape3DHandler {
035    public static String RenderMilStd3dSymbol(String id,
036                                              String name,
037                                              String description,
038                                              String symbolCode,
039                                              String controlPoints,
040                                              String altitudeMode,
041                                              Double scale,
042                                              String bbox,
043                                              Map<String, String> symbolModifiers,
044                                              Map<String, String> symbolAttributes,
045                                              int format) {
046        //System.out.println("MultiPointHandler.RenderSymbol()");
047        boolean normalize = true;
048        //Double controlLat = 0.0;
049        //Double controlLong = 0.0;
050        //Double metPerPix = GeoPixelConversion.metersPerPixel(scale);
051        //String bbox2=getBoundingRectangle(controlPoints,bbox);
052        StringBuilder jsonOutput = new StringBuilder();
053        String jsonContent = "";
054
055        Rectangle rect = null;
056        String[] coordinates = controlPoints.split(" ");
057        TGLight tgl = new TGLight();
058        ArrayList<ShapeInfo3D> shapes = new ArrayList<ShapeInfo3D>();
059        ArrayList<ShapeInfo3D> modifiers = new ArrayList<ShapeInfo3D>();
060        //ArrayList<Point2D> pixels = new ArrayList<Point2D>();
061        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
062        int len = coordinates.length;
063        //diagnostic create geoCoords here
064        Point2D coordsUL = null;
065
066        // 3D default colors
067        if (symbolAttributes.get(MilStdAttributes.LineColor) == null) {
068            Color defaultColor = SymbolUtilities.getLineColorOfAffiliation(symbolCode);
069            if (defaultColor == null) {
070                defaultColor = new Color(Color.BLACK);
071            }
072            symbolAttributes.put(MilStdAttributes.LineColor, defaultColor.toHexString());
073        }
074        if (symbolAttributes.get(MilStdAttributes.FillColor) == null) {
075            Color defaultColor = SymbolUtilities.getFillColorOfAffiliation(symbolCode);
076            if (defaultColor == null) {
077                defaultColor = new Color(Color.WHITE);
078            }
079            defaultColor.setAlpha(170);
080            symbolAttributes.put(MilStdAttributes.FillColor, defaultColor.toHexString());
081        }
082
083        if (altitudeMode == null || altitudeMode.equals("clampToGround"))
084            altitudeMode = "absolute";
085
086        if (!JavaRendererUtilities.is3dSymbol(symbolCode)) {
087            String basicID = SymbolUtilities.getBasicSymbolID(symbolCode);
088            final String errorMsg = "Basic ID: " + basicID + " is not a 3D Symbol";
089            String ErrorOutput = "";
090            ErrorOutput += ("{\"type\":\"error\",\"error\":\"There was an error creating the 3D MilStdSymbol " + symbolCode + " - ID: " + id + " - ");
091            ErrorOutput += errorMsg; //reason for error
092            ErrorOutput += ("\"}");
093            ErrorLogger.LogMessage("Shape3DHandler", "RenderMilStd3dSymbol", errorMsg, Level.FINE);
094            return ErrorOutput;
095        }
096
097        String symbolIsValid = MultiPointHandler.canRenderMultiPoint(symbolCode, symbolModifiers, len);
098        if (!symbolIsValid.equals("true")) {
099            String ErrorOutput = "";
100            ErrorOutput += ("{\"type\":\"error\",\"error\":\"There was an error creating the 3D MilStdSymbol " + symbolCode + " - ID: " + id + " - ");
101            ErrorOutput += symbolIsValid; //reason for error
102            ErrorOutput += ("\"}");
103            ErrorLogger.LogMessage("Shape3DHandler", "RenderMilStd3dSymbol", symbolIsValid, Level.WARNING);
104            return ErrorOutput;
105        }
106
107        if (MSLookup.getInstance().getMSLInfo(symbolCode).getDrawRule() != DrawRules.AREA10) // AREA10 can support infinite points
108            len = Math.min(len, MSLookup.getInstance().getMSLInfo(symbolCode).getMaxPointCount());
109        for (int i = 0; i < len; i++) {
110            String[] coordPair = coordinates[i].split(",");
111            Double latitude = Double.valueOf(coordPair[1].trim()).doubleValue();
112            Double longitude = Double.valueOf(coordPair[0].trim()).doubleValue();
113            geoCoords.add(new Point2D.Double(longitude, latitude));
114        }
115        ArrayList<POINT2> tgPoints = null;
116        IPointConversion ipc = null;
117
118        //Deutch moved section 6-29-11
119        Double left = 0.0;
120        Double right = 0.0;
121        Double top = 0.0;
122        Double bottom = 0.0;
123        Point2D temp = null;
124        Point2D ptGeoUL = null;
125        int width = 0;
126        int height = 0;
127        int leftX = 0;
128        int topY = 0;
129        int bottomY = 0;
130        int rightX = 0;
131        int j = 0;
132        ArrayList<Point2D> bboxCoords = null;
133        if (bbox != null && bbox.equals("") == false) {
134            String[] bounds = null;
135            if (bbox.contains(" "))//trapezoid
136            {
137                bboxCoords = new ArrayList<Point2D>();
138                double x = 0;
139                double y = 0;
140                String[] coords = bbox.split(" ");
141                String[] arrCoord;
142                for (String coord : coords) {
143                    arrCoord = coord.split(",");
144                    x = Double.valueOf(arrCoord[0]);
145                    y = Double.valueOf(arrCoord[1]);
146                    bboxCoords.add(new Point2D.Double(x, y));
147                }
148                //use the upper left corner of the MBR containing geoCoords
149                //to set the converter
150                ptGeoUL = MultiPointHandler.getGeoUL(bboxCoords);
151                left = ptGeoUL.getX();
152                top = ptGeoUL.getY();
153                String bbox2 = MultiPointHandler.getBboxFromCoords(bboxCoords);
154                scale = MultiPointHandler.getReasonableScale(bbox2, scale);
155                ipc = new PointConverter(left, top, scale);
156                Point2D ptPixels = null;
157                Point2D ptGeo = null;
158                int n = bboxCoords.size();
159                //for (j = 0; j < bboxCoords.size(); j++)
160                for (j = 0; j < n; j++) {
161                    ptGeo = bboxCoords.get(j);
162                    ptPixels = ipc.GeoToPixels(ptGeo);
163                    x = ptPixels.getX();
164                    y = ptPixels.getY();
165                    if (x < 20) {
166                        x = 20;
167                    }
168                    if (y < 20) {
169                        y = 20;
170                    }
171                    ptPixels.setLocation(x, y);
172                    //end section
173                    bboxCoords.set(j, (Point2D) ptPixels);
174                }
175            } else//rectangle
176            {
177                bounds = bbox.split(",");
178                left = Double.valueOf(bounds[0]);
179                right = Double.valueOf(bounds[2]);
180                top = Double.valueOf(bounds[3]);
181                bottom = Double.valueOf(bounds[1]);
182                scale = MultiPointHandler.getReasonableScale(bbox, scale);
183                ipc = new PointConverter(left, top, scale);
184            }
185
186            Point2D pt2d = null;
187            if (bboxCoords == null) {
188                pt2d = new Point2D.Double(left, top);
189                temp = ipc.GeoToPixels(pt2d);
190
191                leftX = (int) temp.getX();
192                topY = (int) temp.getY();
193
194                pt2d = new Point2D.Double(right, bottom);
195                temp = ipc.GeoToPixels(pt2d);
196
197                bottomY = (int) temp.getY();
198                rightX = (int) temp.getX();
199                //diagnostic clipping does not work at large scales
200//                if(scale>10e6)
201//                {
202//                    //diagnostic replace above by using a new ipc based on the coordinates MBR
203//                    coordsUL=getGeoUL(geoCoords);
204//                    temp = ipc.GeoToPixels(coordsUL);
205//                    left=coordsUL.getX();
206//                    top=coordsUL.getY();
207//                    //shift the ipc to coordsUL origin so that conversions will be more accurate for large scales.
208//                    ipc = new PointConverter(left, top, scale);
209//                    //shift the rect to compenstate for the shifted ipc so that we can maintain the original clipping area.
210//                    leftX -= (int)temp.getX();
211//                    rightX -= (int)temp.getX();
212//                    topY -= (int)temp.getY();
213//                    bottomY -= (int)temp.getY();
214//                    //end diagnostic
215//                }
216                //end section
217
218                width = (int) Math.abs(rightX - leftX);
219                height = (int) Math.abs(bottomY - topY);
220
221                rect = new Rectangle(leftX, topY, width, height);
222            }
223        } else {
224            rect = null;
225        }
226        //end section
227
228//        for (int i = 0; i < len; i++) {
229//            String[] coordPair = coordinates[i].split(",");
230//            Double latitude = Double.valueOf(coordPair[1].trim());
231//            Double longitude = Double.valueOf(coordPair[0].trim());
232//            geoCoords.add(new Point2D.Double(longitude, latitude));
233//        }
234        if (ipc == null) {
235            Point2D ptCoordsUL = MultiPointHandler.getGeoUL(geoCoords);
236            ipc = new PointConverter(ptCoordsUL.getX(), ptCoordsUL.getY(), scale);
237        }
238        //if (crossesIDL(geoCoords) == true)
239//        if(Math.abs(right-left)>180)
240//        {
241//            normalize = true;
242//            ((PointConverter)ipc).set_normalize(true);
243//        }
244//        else {
245//            normalize = false;
246//            ((PointConverter)ipc).set_normalize(false);
247//        }
248
249        //seems to work ok at world view
250//        if (normalize) {
251//            NormalizeGECoordsToGEExtents(0, 360, geoCoords);
252//        }
253
254        //M. Deutch 10-3-11
255        //must shift the rect pixels to synch with the new ipc
256        //the old ipc was in synch with the bbox, so rect x,y was always 0,0
257        //the new ipc synchs with the upper left of the geocoords so the boox is shifted
258        //and therefore the clipping rectangle must shift by the delta x,y between
259        //the upper left corner of the original bbox and the upper left corner of the geocoords
260        ArrayList<Point2D> geoCoords2 = new ArrayList<Point2D>();
261        geoCoords2.add(new Point2D.Double(left, top));
262        geoCoords2.add(new Point2D.Double(right, bottom));
263
264//        if (normalize) {
265//            NormalizeGECoordsToGEExtents(0, 360, geoCoords2);
266//        }
267
268        //disable clipping
269        if (MultiPointHandler.ShouldClipSymbol(symbolCode) == false)
270            if (MultiPointHandler.crossesIDL(geoCoords) == false) {
271                rect = null;
272                bboxCoords = null;
273            }
274
275        tgl.set_SymbolId(symbolCode);// "GFGPSLA---****X" AMBUSH symbol code
276        tgl.set_Pixels(null);
277
278        try {
279
280            //String fillColor = null;
281            MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
282
283            if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG) {
284                // Use dash array and hatch pattern fill for SVG output
285                symbolAttributes.put(MilStdAttributes.UseDashArray, "true");
286                symbolAttributes.put(MilStdAttributes.UsePatternFill, "true");
287            }
288
289            if (symbolModifiers != null || symbolAttributes != null) {
290                MultiPointHandler.populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
291            } else {
292                mSymbol.setFillColor(null);
293            }
294
295            if (bboxCoords == null) {
296                Rectangle clipBounds = MultiPointHandler.getOverscanClipBounds(rect, ipc);
297                clsRenderer.renderWithPolylines(mSymbol, ipc, clipBounds);
298            } else {
299                clsRenderer.renderWithPolylines(mSymbol, ipc, bboxCoords);
300            }
301
302            // Convert 2D shape to 3D
303            if (MSLookup.getInstance().getMSLInfo(symbolCode).getDrawRule() == DrawRules.CORRIDOR1) {
304                // Remove circles from air corridor for 3d
305                // Set line color for other shapes
306                for (int i = 0; i < mSymbol.getSymbolShapes().size() - 1; i++) {
307                    mSymbol.getSymbolShapes().get(i).setLineColor(mSymbol.getSymbolShapes().get(mSymbol.getSymbolShapes().size() - 1).getLineColor());
308                }
309                mSymbol.getSymbolShapes().remove(mSymbol.getSymbolShapes().size() - 1);
310                Collections.reverse(mSymbol.getSymbolShapes());
311            }
312            // Confirm there are at least two altitudes per shape
313            ArrayList<Double> altitudes = mSymbol.getModifiers_AM_AN_X(Modifiers.X_ALTITUDE_DEPTH);
314            if (altitudes.size() == 1) {
315                altitudes.add(0, 0.0);
316            }
317            final Double lastAlt = altitudes.get(altitudes.size() - 1);
318            final Double nextToLastAlt = altitudes.get(altitudes.size() - 2);
319            while (altitudes.size() < mSymbol.getSymbolShapes().size() * 2) {
320                altitudes.add(nextToLastAlt);
321                altitudes.add(lastAlt);
322            }
323            for (int shapeIndex = 0; shapeIndex < mSymbol.getSymbolShapes().size(); shapeIndex++) {
324                final Double minAlt = altitudes.get(shapeIndex * 2);
325                final Double maxAlt = altitudes.get((shapeIndex * 2) + 1);
326                final ShapeInfo oldShape = mSymbol.getSymbolShapes().get(shapeIndex);
327
328                ShapeInfo3D bottomShape = new ShapeInfo3D();
329                bottomShape.setShapeType(oldShape.getShapeType());
330                bottomShape.setStroke(oldShape.getStroke());
331                bottomShape.setLineColor(oldShape.getLineColor());
332                bottomShape.setFillColor(oldShape.getFillColor());
333                bottomShape.setPatternFillImage(oldShape.getPatternFillImage());
334                bottomShape.setPolylines3D(new ArrayList<>());
335                ShapeInfo3D topShape = new ShapeInfo3D();
336                topShape.setShapeType(oldShape.getShapeType());
337                topShape.setStroke(oldShape.getStroke());
338                topShape.setLineColor(oldShape.getLineColor());
339                topShape.setFillColor(oldShape.getFillColor());
340                topShape.setPatternFillImage(oldShape.getPatternFillImage());
341                topShape.setPolylines3D(new ArrayList<>());
342
343                for (int polyLineIndex = 0; polyLineIndex < oldShape.getPolylines().size(); polyLineIndex++) {
344                    final ArrayList<Point2D> polyline = oldShape.getPolylines().get(polyLineIndex);
345                    bottomShape.getPolylines3D().add(new ArrayList<Point3D>());
346                    topShape.getPolylines3D().add(new ArrayList<Point3D>());
347                    for (int ptIndex = 0; ptIndex < polyline.size(); ptIndex++) {
348                        final Point2D pt = polyline.get(ptIndex);
349                        final Point2D pt2 = polyline.get((ptIndex + 1) % polyline.size());
350                        bottomShape.getPolylines3D().get(polyLineIndex).add(new Point3D(pt, minAlt));
351                        topShape.getPolylines3D().get(polyLineIndex).add(new Point3D(pt, maxAlt));
352
353                        ShapeInfo3D sideShape = new ShapeInfo3D();
354                        sideShape.setShapeType(oldShape.getShapeType());
355                        sideShape.setStroke(oldShape.getStroke());
356                        sideShape.setLineColor(oldShape.getLineColor());
357                        sideShape.setFillColor(oldShape.getFillColor());
358                        sideShape.setPatternFillImage(oldShape.getPatternFillImage());
359                        sideShape.setPolylines3D(new ArrayList<ArrayList<Point3D>>());
360                        sideShape.getPolylines3D().add(new ArrayList<>());
361                        sideShape.getPolylines3D().get(0).add(new Point3D(pt, minAlt));
362                        sideShape.getPolylines3D().get(0).add(new Point3D(pt2, minAlt));
363                        sideShape.getPolylines3D().get(0).add(new Point3D(pt2, maxAlt));
364                        sideShape.getPolylines3D().get(0).add(new Point3D(pt, maxAlt));
365                        sideShape.getPolylines3D().get(0).add(new Point3D(pt, minAlt));
366                        shapes.add(sideShape);
367                    }
368                }
369                shapes.add(bottomShape);
370                shapes.add(topShape);
371            }
372
373            if (!mSymbol.getSymbolShapes().isEmpty() && !mSymbol.getModifierShapes().isEmpty()) {
374                final double modifierAlt = Collections.max(altitudes.subList(0, mSymbol.getSymbolShapes().size() * 2));
375                for (ShapeInfo oldShape : mSymbol.getModifierShapes()) {
376                    ShapeInfo3D modShape = new ShapeInfo3D();
377                    modShape.setModifierString(oldShape.getModifierString());
378                    modShape.setModifierPosition(new Point3D(oldShape.getModifierPosition(), modifierAlt));
379                    modShape.setModifierAngle(oldShape.getModifierAngle());
380                    modShape.setTextJustify(oldShape.getTextJustify());
381                    modShape.setModifierImage(oldShape.getModifierImage());
382                    modifiers.add(modShape);
383                }
384            }
385
386            if (format == WebRenderer.OUTPUT_FORMAT_KML) {
387                Color textColor = mSymbol.getTextColor();
388                if (textColor == null) textColor = mSymbol.getLineColor();
389
390                jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, altitudeMode, mSymbol.get_WasClipped());
391                jsonOutput.append(jsonContent);
392            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOJSON) {
393                jsonOutput.append("{\"type\":\"FeatureCollection\",\"features\":");
394                jsonContent = GeoJSONize(shapes, modifiers, ipc, normalize, mSymbol.getTextColor(), mSymbol.getTextBackgroundColor());
395                jsonOutput.append(jsonContent);
396
397                //moving meta data properties to the last feature with no coords as feature collection doesn't allow properties
398                jsonOutput.replace(jsonOutput.toString().length() - 1, jsonOutput.toString().length(), "");
399                if (jsonContent.length() > 2) jsonOutput.append(",");
400                jsonOutput.append("{\"type\": \"Feature\",\"geometry\": { \"type\": \"Polygon\",\"coordinates\": [ ]}");
401
402                jsonOutput.append(",\"properties\":{\"id\":\"");
403                jsonOutput.append(id);
404                jsonOutput.append("\",\"name\":\"");
405                jsonOutput.append(name);
406                jsonOutput.append("\",\"description\":\"");
407                jsonOutput.append(description);
408                jsonOutput.append("\",\"symbolID\":\"");
409                jsonOutput.append(symbolCode);
410                jsonOutput.append("\",\"wasClipped\":\"");
411                jsonOutput.append(String.valueOf(mSymbol.get_WasClipped()));
412                jsonOutput.append("\"}}]}");
413            }
414        } catch (Exception exc) {
415            String st = JavaRendererUtilities.getStackTrace(exc);
416            jsonOutput = new StringBuilder();
417            jsonOutput.append("{\"type\":\"error\",\"error\":\"There was an error creating the 3D MilStdSymbol ").append(symbolCode).append(": ").append("- ");
418            jsonOutput.append(exc.getMessage()).append(" - ");
419            jsonOutput.append(st);
420            jsonOutput.append("\"}");
421
422            ErrorLogger.LogException("Shape3DHandler", "RenderMilStd3dSymbol", exc);
423        }
424
425        boolean debug = false;
426        if (debug == true) {
427            System.out.println("Symbol Code: " + symbolCode);
428            System.out.println("Scale: " + scale);
429            System.out.println("BBOX: " + bbox);
430            if (controlPoints != null) {
431                System.out.println("Geo Points: " + controlPoints);
432            }
433            if (tgl != null && tgl.get_Pixels() != null)//pixels != null
434            {
435                System.out.println("Pixel: " + tgl.get_Pixels().toString());
436            }
437            if (bbox != null) {
438                System.out.println("geo bounds: " + bbox);
439            }
440            if (rect != null) {
441                System.out.println("pixel bounds: " + rect.toString());
442            }
443            if (jsonOutput != null) {
444                System.out.println(jsonOutput.toString());
445            }
446        }
447
448        ErrorLogger.LogMessage("Shape3DHandler", "RenderMilStd3dSymbol()", "exit RenderMilStd3dSymbol", Level.FINER);
449        return jsonOutput.toString();
450    }
451
452    public static String RenderBasic3DShape(String id,
453                                            String name,
454                                            String description,
455                                            int basicShapeType,
456                                            String controlPoints,
457                                            String altitudeMode,
458                                            Double scale,
459                                            String bbox,
460                                            Map<String, String> symbolModifiers,
461                                            Map<String, String> symbolAttributes,
462                                            int format) {
463        boolean normalize = true;
464        //Double controlLat = 0.0;
465        //Double controlLong = 0.0;
466        //Double metPerPix = GeoPixelConversion.metersPerPixel(scale);
467        //String bbox2=getBoundingRectangle(controlPoints,bbox);
468        StringBuilder jsonOutput = new StringBuilder();
469        String jsonContent = "";
470
471        Rectangle rect = null;
472        String[] coordinates = controlPoints.split(" ");
473        ArrayList<ShapeInfo3D> shapes = new ArrayList<ShapeInfo3D>();
474        ArrayList<ShapeInfo3D> modifiers = new ArrayList<ShapeInfo3D>();
475        //ArrayList<Point2D> pixels = new ArrayList<Point2D>();
476        ArrayList<Point2D> geoCoords = new ArrayList<Point2D>();
477        int len = coordinates.length;
478        //diagnostic create geoCoords here
479        Point2D coordsUL = null;
480        final String symbolCode = "";
481
482        // 3D default colors
483        if (symbolAttributes.get(MilStdAttributes.LineColor) == null) {
484            symbolAttributes.put(MilStdAttributes.LineColor, Color.BLACK.toHexString());
485        }
486        if (symbolAttributes.get(MilStdAttributes.FillColor) == null) {
487            Color defaultColor = new Color(255, 255, 255, 170);
488            symbolAttributes.put(MilStdAttributes.FillColor, defaultColor.toHexString());
489        }
490
491        if (altitudeMode == null || altitudeMode.equals("clampToGround"))
492            altitudeMode = "absolute";
493
494        for (int i = 0; i < len; i++) {
495            String[] coordPair = coordinates[i].split(",");
496            Double latitude = Double.valueOf(coordPair[1].trim()).doubleValue();
497            Double longitude = Double.valueOf(coordPair[0].trim()).doubleValue();
498            geoCoords.add(new Point2D.Double(longitude, latitude));
499        }
500        ArrayList<POINT2> tgPoints = null;
501        IPointConversion ipc = null;
502
503        //Deutch moved section 6-29-11
504        Double left = 0.0;
505        Double right = 0.0;
506        Double top = 0.0;
507        Double bottom = 0.0;
508        Point2D temp = null;
509        Point2D ptGeoUL = null;
510        int width = 0;
511        int height = 0;
512        int leftX = 0;
513        int topY = 0;
514        int bottomY = 0;
515        int rightX = 0;
516        int j = 0;
517        ArrayList<Point2D> bboxCoords = null;
518        if (bbox != null && bbox.equals("") == false) {
519            String[] bounds = null;
520            if (bbox.contains(" "))//trapezoid
521            {
522                bboxCoords = new ArrayList<Point2D>();
523                double x = 0;
524                double y = 0;
525                String[] coords = bbox.split(" ");
526                String[] arrCoord;
527                for (String coord : coords) {
528                    arrCoord = coord.split(",");
529                    x = Double.valueOf(arrCoord[0]);
530                    y = Double.valueOf(arrCoord[1]);
531                    bboxCoords.add(new Point2D.Double(x, y));
532                }
533                //use the upper left corner of the MBR containing geoCoords
534                //to set the converter
535                ptGeoUL = MultiPointHandler.getGeoUL(bboxCoords);
536                left = ptGeoUL.getX();
537                top = ptGeoUL.getY();
538                String bbox2 = MultiPointHandler.getBboxFromCoords(bboxCoords);
539                scale = MultiPointHandler.getReasonableScale(bbox2, scale);
540                ipc = new PointConverter(left, top, scale);
541                Point2D ptPixels = null;
542                Point2D ptGeo = null;
543                int n = bboxCoords.size();
544                //for (j = 0; j < bboxCoords.size(); j++)
545                for (j = 0; j < n; j++) {
546                    ptGeo = bboxCoords.get(j);
547                    ptPixels = ipc.GeoToPixels(ptGeo);
548                    x = ptPixels.getX();
549                    y = ptPixels.getY();
550                    if (x < 20) {
551                        x = 20;
552                    }
553                    if (y < 20) {
554                        y = 20;
555                    }
556                    ptPixels.setLocation(x, y);
557                    //end section
558                    bboxCoords.set(j, (Point2D) ptPixels);
559                }
560            } else//rectangle
561            {
562                bounds = bbox.split(",");
563                left = Double.valueOf(bounds[0]);
564                right = Double.valueOf(bounds[2]);
565                top = Double.valueOf(bounds[3]);
566                bottom = Double.valueOf(bounds[1]);
567                scale = MultiPointHandler.getReasonableScale(bbox, scale);
568                ipc = new PointConverter(left, top, scale);
569            }
570
571            Point2D pt2d = null;
572            if (bboxCoords == null) {
573                pt2d = new Point2D.Double(left, top);
574                temp = ipc.GeoToPixels(pt2d);
575
576                leftX = (int) temp.getX();
577                topY = (int) temp.getY();
578
579                pt2d = new Point2D.Double(right, bottom);
580                temp = ipc.GeoToPixels(pt2d);
581
582                bottomY = (int) temp.getY();
583                rightX = (int) temp.getX();
584
585                width = (int) Math.abs(rightX - leftX);
586                height = (int) Math.abs(bottomY - topY);
587
588                rect = new Rectangle(leftX, topY, width, height);
589            }
590        } else {
591            rect = null;
592        }
593
594        if (ipc == null) {
595            Point2D ptCoordsUL = MultiPointHandler.getGeoUL(geoCoords);
596            ipc = new PointConverter(ptCoordsUL.getX(), ptCoordsUL.getY(), scale);
597        }
598
599        ArrayList<Point2D> geoCoords2 = new ArrayList<Point2D>();
600        geoCoords2.add(new Point2D.Double(left, top));
601        geoCoords2.add(new Point2D.Double(right, bottom));
602
603//        if (normalize) {
604//            NormalizeGECoordsToGEExtents(0, 360, geoCoords2);
605//        }
606
607        try {
608
609            //String fillColor = null;
610            MilStdSymbol mSymbol = new MilStdSymbol(symbolCode, null, geoCoords, null);
611
612            if (format == WebRenderer.OUTPUT_FORMAT_GEOSVG) {
613                // Use dash array and hatch pattern fill for SVG output
614                symbolAttributes.put(MilStdAttributes.UseDashArray, "true");
615                symbolAttributes.put(MilStdAttributes.UsePatternFill, "true");
616            }
617
618            if (symbolModifiers != null || symbolAttributes != null) {
619                MultiPointHandler.populateModifiers(symbolModifiers, symbolAttributes, mSymbol);
620            } else {
621                mSymbol.setFillColor(null);
622            }
623
624            TGLight tg = clsRenderer.createTGLightFromMilStdSymbolBasicShape(mSymbol, ipc, basicShapeType);
625            ArrayList<ShapeInfo> shapeInfos = new ArrayList();
626            ArrayList<ShapeInfo> modifierShapeInfos = new ArrayList();
627            Object clipArea;
628            if (bboxCoords == null) {
629                clipArea = rect;
630            } else {
631                clipArea = bboxCoords;
632            }
633            if (clsRenderer.intersectsClipArea(tg, ipc, clipArea)) {
634                clsRenderer.render_GE(tg, shapeInfos, modifierShapeInfos, ipc, clipArea);
635            }
636            mSymbol.setSymbolShapes(shapeInfos);
637            mSymbol.setModifierShapes(modifierShapeInfos);
638            mSymbol.set_WasClipped(tg.get_WasClipped());
639
640            // Convert 2D shape to 3D
641            // Confirm there are at least two altitudes per shape
642            ArrayList<Double> altitudes = mSymbol.getModifiers_AM_AN_X(Modifiers.X_ALTITUDE_DEPTH);
643            if (altitudes.size() == 1) {
644                altitudes.add(0, 0.0);
645            }
646            if (basicShapeType == Basic3DShapes.ROUTE){
647                altitudes = new ArrayList<>(altitudes.subList(0, 2));
648            }
649            final Double lastAlt = altitudes.get(altitudes.size() - 1);
650            final Double nextToLastAlt = altitudes.get(altitudes.size() - 2);
651            while (altitudes.size() < mSymbol.getSymbolShapes().size() * 2) {
652                altitudes.add(nextToLastAlt);
653                altitudes.add(lastAlt);
654            }
655            for (int shapeIndex = 0; shapeIndex < mSymbol.getSymbolShapes().size(); shapeIndex++) {
656                final Double minAlt = altitudes.get(shapeIndex * 2);
657                final Double maxAlt = altitudes.get((shapeIndex * 2) + 1);
658                final ShapeInfo oldShape = mSymbol.getSymbolShapes().get(shapeIndex);
659
660                ShapeInfo3D bottomShape = new ShapeInfo3D();
661                bottomShape.setShapeType(oldShape.getShapeType());
662                bottomShape.setStroke(oldShape.getStroke());
663                bottomShape.setLineColor(oldShape.getLineColor());
664                bottomShape.setFillColor(oldShape.getFillColor());
665                bottomShape.setPatternFillImage(oldShape.getPatternFillImage());
666                bottomShape.setPolylines3D(new ArrayList<>());
667                ShapeInfo3D topShape = new ShapeInfo3D();
668                topShape.setShapeType(oldShape.getShapeType());
669                topShape.setStroke(oldShape.getStroke());
670                topShape.setLineColor(oldShape.getLineColor());
671                topShape.setFillColor(oldShape.getFillColor());
672                topShape.setPatternFillImage(oldShape.getPatternFillImage());
673                topShape.setPolylines3D(new ArrayList<>());
674
675                for (int polyLineIndex = 0; polyLineIndex < oldShape.getPolylines().size(); polyLineIndex++) {
676                    final ArrayList<Point2D> polyline = oldShape.getPolylines().get(polyLineIndex);
677                    bottomShape.getPolylines3D().add(new ArrayList<Point3D>());
678                    topShape.getPolylines3D().add(new ArrayList<Point3D>());
679                    for (int ptIndex = 0; ptIndex < polyline.size(); ptIndex++) {
680                        final Point2D pt = polyline.get(ptIndex);
681                        final Point2D pt2 = polyline.get((ptIndex + 1) % polyline.size());
682                        bottomShape.getPolylines3D().get(polyLineIndex).add(new Point3D(pt, minAlt));
683                        topShape.getPolylines3D().get(polyLineIndex).add(new Point3D(pt, maxAlt));
684
685                        ShapeInfo3D sideShape = new ShapeInfo3D();
686                        sideShape.setShapeType(oldShape.getShapeType());
687                        sideShape.setStroke(oldShape.getStroke());
688                        sideShape.setLineColor(oldShape.getLineColor());
689                        sideShape.setFillColor(oldShape.getFillColor());
690                        sideShape.setPatternFillImage(oldShape.getPatternFillImage());
691                        sideShape.setPolylines3D(new ArrayList<ArrayList<Point3D>>());
692                        sideShape.getPolylines3D().add(new ArrayList<>());
693                        sideShape.getPolylines3D().get(0).add(new Point3D(pt, minAlt));
694                        sideShape.getPolylines3D().get(0).add(new Point3D(pt2, minAlt));
695                        sideShape.getPolylines3D().get(0).add(new Point3D(pt2, maxAlt));
696                        sideShape.getPolylines3D().get(0).add(new Point3D(pt, maxAlt));
697                        sideShape.getPolylines3D().get(0).add(new Point3D(pt, minAlt));
698                        shapes.add(sideShape);
699                    }
700                }
701                shapes.add(bottomShape);
702                shapes.add(topShape);
703            }
704
705            if (!mSymbol.getSymbolShapes().isEmpty() && !mSymbol.getModifierShapes().isEmpty()) {
706                final double modifierAlt = Collections.max(altitudes.subList(0, mSymbol.getSymbolShapes().size() * 2));
707                for (ShapeInfo oldShape : mSymbol.getModifierShapes()) {
708                    ShapeInfo3D modShape = new ShapeInfo3D();
709                    modShape.setModifierString(oldShape.getModifierString());
710                    modShape.setModifierPosition(new Point3D(oldShape.getModifierPosition(), modifierAlt));
711                    modShape.setModifierAngle(oldShape.getModifierAngle());
712                    modShape.setTextJustify(oldShape.getTextJustify());
713                    modShape.setModifierImage(oldShape.getModifierImage());
714                    modifiers.add(modShape);
715                }
716            }
717
718            if (format == WebRenderer.OUTPUT_FORMAT_KML) {
719                Color textColor = mSymbol.getTextColor();
720                if (textColor == null)
721                    textColor = mSymbol.getLineColor();
722
723                jsonContent = KMLize(id, name, description, symbolCode, shapes, modifiers, ipc, normalize, textColor, altitudeMode, mSymbol.get_WasClipped());
724                jsonOutput.append(jsonContent);
725            } else if (format == WebRenderer.OUTPUT_FORMAT_GEOJSON) {
726                jsonOutput.append("{\"type\":\"FeatureCollection\",\"features\":");
727                jsonContent = GeoJSONize(shapes, modifiers, ipc, normalize, mSymbol.getTextColor(), mSymbol.getTextBackgroundColor());
728                jsonOutput.append(jsonContent);
729
730                //moving meta data properties to the last feature with no coords as feature collection doesn't allow properties
731                jsonOutput.replace(jsonOutput.toString().length() - 1, jsonOutput.toString().length(), "");
732                if (jsonContent.length() > 2)
733                    jsonOutput.append(",");
734                jsonOutput.append("{\"type\": \"Feature\",\"geometry\": { \"type\": \"Polygon\",\"coordinates\": [ ]}");
735
736                jsonOutput.append(",\"properties\":{\"id\":\"");
737                jsonOutput.append(id);
738                jsonOutput.append("\",\"name\":\"");
739                jsonOutput.append(name);
740                jsonOutput.append("\",\"description\":\"");
741                jsonOutput.append(description);
742                jsonOutput.append("\",\"symbolID\":\"");
743                jsonOutput.append(symbolCode);
744                jsonOutput.append("\",\"wasClipped\":\"");
745                jsonOutput.append(String.valueOf(mSymbol.get_WasClipped()));
746                //jsonOutput.append("\"}}");
747
748                jsonOutput.append("\"}}]}");
749            }
750        } catch (Exception exc) {
751            String st = JavaRendererUtilities.getStackTrace(exc);
752            jsonOutput = new StringBuilder();
753            jsonOutput.append("{\"type\":\"error\",\"error\":\"There was an error creating the 3D MilStdSymbol " + symbolCode + ": " + "- ");
754            jsonOutput.append(exc.getMessage()).append(" - ");
755            jsonOutput.append(st);
756            jsonOutput.append("\"}");
757
758            ErrorLogger.LogException("Shape3DHandler", "RenderBasic3DShape", exc);
759        }
760
761        boolean debug = false;
762        if (debug == true) {
763            System.out.println("Symbol Code: " + symbolCode);
764            System.out.println("Scale: " + scale);
765            System.out.println("BBOX: " + bbox);
766            if (controlPoints != null) {
767                System.out.println("Geo Points: " + controlPoints);
768            }
769            if (bbox != null) {
770                System.out.println("geo bounds: " + bbox);
771            }
772            if (rect != null) {
773                System.out.println("pixel bounds: " + rect.toString());
774            }
775            if (jsonOutput != null) {
776                System.out.println(jsonOutput.toString());
777            }
778        }
779
780        ErrorLogger.LogMessage("Shape3DHandler", "RenderBasic3DShape()", "exit RenderBasic3DShape", Level.FINER);
781        return jsonOutput.toString();
782
783    }
784
785    private static String KMLize(String id,
786                                 String name,
787                                 String description,
788                                 String symbolCode,
789                                 ArrayList<ShapeInfo3D> shapes,
790                                 ArrayList<ShapeInfo3D> modifiers,
791                                 IPointConversion ipc,
792                                 boolean normalize,
793                                 Color textColor,
794                                 String altitudeMode,
795                                 boolean wasClipped) {
796        java.lang.StringBuilder kml = new java.lang.StringBuilder();
797        ShapeInfo3D tempModifier = null;
798        String cdataStart = "<![CDATA[";
799        String cdataEnd = "]]>";
800        int len = shapes.size();
801        kml.append("<Folder id=\"").append(id).append("\">");
802        kml.append("<name>").append(cdataStart).append(name).append(cdataEnd).append("</name>");
803        kml.append("<visibility>1</visibility>");
804        kml.append("<description>").append(cdataStart).append(description).append(cdataEnd).append("</description>");
805        kml.append("<ExtendedData>");
806        kml.append("<Data name=\"symbolID\"><value>").append(cdataStart).append(symbolCode).append(cdataEnd).append("</value></Data>");
807        kml.append("<Data name=\"wasClipped\"><value>").append(cdataStart).append(wasClipped).append(cdataEnd).append("</value></Data>");
808        kml.append("</ExtendedData>");
809        for (int i = 0; i < len; i++) {
810            String shapesToAdd = ShapeToKMLString(shapes.get(i), ipc, normalize, altitudeMode);
811            kml.append(shapesToAdd);
812        }
813
814        int len2 = modifiers.size();
815
816        for (int j = 0; j < len2; j++) {
817
818            tempModifier = modifiers.get(j);
819
820            //if(geMap)//if using google earth
821            //assume kml text is going to be centered
822            //AdjustModifierPointToCenter(tempModifier);
823
824            String labelsToAdd = LabelToKMLString(tempModifier, ipc, normalize, textColor, altitudeMode);
825            kml.append(labelsToAdd);
826        }
827
828        kml.append("</Folder>");
829        return kml.toString();
830    }
831
832    private static String ShapeToKMLString(ShapeInfo3D shapeInfo,
833                                           IPointConversion ipc,
834                                           boolean normalize,
835                                           String altitudeMode) {
836        java.lang.StringBuilder kml = new java.lang.StringBuilder();
837        Color lineColor = null;
838        Color fillColor = null;
839        String googleLineColor = null;
840        String googleFillColor = null;
841        BasicStroke stroke = null;
842        int lineWidth = 4;
843
844        kml.append("<Placemark>");
845        kml.append("<Style>");
846
847        lineColor = shapeInfo.getLineColor();
848        if (lineColor != null) {
849            googleLineColor = Integer.toHexString(shapeInfo.getLineColor().toARGB());
850
851            stroke = shapeInfo.getStroke();
852
853            if (stroke != null) {
854                lineWidth = (int) stroke.getLineWidth();
855            }
856
857            googleLineColor = JavaRendererUtilities.ARGBtoABGR(googleLineColor);
858
859            kml.append("<LineStyle>");
860            kml.append("<color>").append(googleLineColor).append("</color>");
861            kml.append("<colorMode>normal</colorMode>");
862            kml.append("<width>").append(String.valueOf(lineWidth)).append("</width>");
863            kml.append("</LineStyle>");
864        }
865
866        fillColor = shapeInfo.getFillColor();
867        Bitmap fillPattern = shapeInfo.getPatternFillImage();
868        if (fillColor != null || fillPattern != null) {
869            kml.append("<PolyStyle>");
870
871            if (fillColor != null) {
872                googleFillColor = Integer.toHexString(shapeInfo.getFillColor().toARGB());
873                googleFillColor = JavaRendererUtilities.ARGBtoABGR(googleFillColor);
874                kml.append("<color>").append(googleFillColor).append("</color>");
875                kml.append("<colorMode>normal</colorMode>");
876            }
877            if (fillPattern != null) {
878                kml.append("<shader>").append(MultiPointHandler.bitmapToString(fillPattern)).append("</shader>");
879            }
880
881            kml.append("<fill>1</fill>");
882            if (lineColor != null) {
883                kml.append("<outline>1</outline>");
884            } else {
885                kml.append("<outline>0</outline>");
886            }
887            kml.append("</PolyStyle>");
888        }
889
890        kml.append("</Style>");
891
892        ArrayList<ArrayList<Point3D>> shapesArray = shapeInfo.getPolylines3D();
893        int len = shapesArray.size();
894        kml.append("<MultiGeometry>");
895
896        for (int i = 0; i < len; i++) {
897            ArrayList<Point3D> shape = shapesArray.get(i);
898            normalize = normalizePoints(shape, ipc);
899            if (lineColor != null && fillColor == null) {
900                kml.append("<Polygon>");
901                kml.append("<tessellate>1</tessellate>");
902                kml.append("<altitudeMode>").append(altitudeMode).append("</altitudeMode>");
903                kml.append("<outerBoundaryIs><LinearRing><coordinates>");
904                int n = shape.size();
905                //for (int j = 0; j < shape.size(); j++)
906                for (int j = 0; j < n; j++) {
907                    Point3D coord = shape.get(j);
908                    Point2D geoCoord = ipc.PixelsToGeo(coord);
909                    if (normalize) {
910                        geoCoord = MultiPointHandler.NormalizeCoordToGECoord(geoCoord);
911                    }
912
913                    double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
914                    double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
915                    double altitude = coord.getZ();
916
917                    kml.append(longitude);
918                    kml.append(",");
919                    kml.append(latitude);
920                    kml.append(",");
921                    kml.append(altitude);
922                    if (j < shape.size() - 1) kml.append(" ");
923                }
924
925                kml.append("</coordinates></LinearRing></outerBoundaryIs>");
926                kml.append("</Polygon>");
927            }
928
929            if (fillColor != null) {
930
931                if (i == 0) {
932                    kml.append("<Polygon>");
933                    kml.append("<tessellate>1</tessellate>");
934                    kml.append("<altitudeMode>").append(altitudeMode).append("</altitudeMode>");
935                }
936                //kml.append("<outerBoundaryIs>");
937                if (i == 1 && len > 1) {
938                    kml.append("<innerBoundaryIs>");
939                } else {
940                    kml.append("<outerBoundaryIs>");
941                }
942                kml.append("<LinearRing>");
943                kml.append("<coordinates>");
944
945                int n = shape.size();
946                //for (int j = 0; j < shape.size(); j++)
947                for (int j = 0; j < n; j++) {
948                    Point3D coord = shape.get(j);
949                    Point2D geoCoord = ipc.PixelsToGeo(coord);
950
951                    double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
952                    double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
953                    double altitude = coord.getZ();
954
955                    //fix for fill crossing DTL
956                    if (normalize) {
957                        if (longitude > 0) {
958                            longitude -= 360;
959                        }
960                    }
961
962                    kml.append(longitude);
963                    kml.append(",");
964                    kml.append(latitude);
965                    kml.append(",");
966                    kml.append(altitude);
967                    if (j < shape.size() - 1) kml.append(" ");
968                }
969
970                kml.append("</coordinates>");
971                kml.append("</LinearRing>");
972                if (i == 1 && len > 1) {
973                    kml.append("</innerBoundaryIs>");
974                } else {
975                    kml.append("</outerBoundaryIs>");
976                }
977                if (i == len - 1) {
978                    kml.append("</Polygon>");
979                }
980            }
981        }
982
983        kml.append("</MultiGeometry>");
984        kml.append("</Placemark>");
985
986        return kml.toString();
987    }
988
989    private static String LabelToKMLString(ShapeInfo3D shapeInfo,
990                                           IPointConversion ipc,
991                                           boolean normalize,
992                                           Color textColor,
993                                           String altitudeMode) {
994        java.lang.StringBuilder kml = new java.lang.StringBuilder();
995
996        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
997        Point3D coord = new Point3D(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY(), shapeInfo.getModifierPosition().getZ());
998        Point2D geoCoord = ipc.PixelsToGeo(coord);
999        //M. Deutch 9-26-11
1000        if (normalize) {
1001            geoCoord = MultiPointHandler.NormalizeCoordToGECoord(geoCoord);
1002        }
1003        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
1004        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
1005        double altitude = coord.getZ();
1006        long angle = Math.round(shapeInfo.getModifierAngle());
1007
1008        String text = shapeInfo.getModifierString();
1009
1010        String cdataStart = "<![CDATA[";
1011        String cdataEnd = "]]>";
1012
1013        String color = Integer.toHexString(textColor.toARGB());
1014        color = JavaRendererUtilities.ARGBtoABGR(color);
1015        float kmlScale = RendererSettings.getInstance().getKMLLabelScale();
1016
1017        if (kmlScale > 0 && text != null && text.equals("") == false) {
1018            kml.append("<Placemark>");//("<Placemark id=\"" + id + "_lp" + i + "\">");
1019            kml.append("<name>").append(cdataStart).append(text).append(cdataEnd).append("</name>");
1020            kml.append("<Style>");
1021            kml.append("<IconStyle>");
1022            kml.append("<scale>").append(kmlScale).append("</scale>");
1023            kml.append("<heading>").append(angle).append("</heading>");
1024            kml.append("<Icon>");
1025            kml.append("<href></href>");
1026            kml.append("</Icon>");
1027            kml.append("</IconStyle>");
1028            kml.append("<LabelStyle>");
1029            kml.append("<color>").append(color).append("</color>");
1030            kml.append("<scale>").append(String.valueOf(kmlScale)).append("</scale>");
1031            kml.append("</LabelStyle>");
1032            kml.append("</Style>");
1033            kml.append("<Point>");
1034            kml.append("<extrude>0</extrude>");
1035            kml.append("<altitudeMode>").append(altitudeMode).append("</altitudeMode>");
1036            kml.append("<coordinates>");
1037            kml.append(longitude);
1038            kml.append(",");
1039            kml.append(latitude);
1040            kml.append(",");
1041            kml.append(altitude);
1042            kml.append("</coordinates>");
1043            kml.append("</Point>");
1044            kml.append("</Placemark>");
1045        } else {
1046            return "";
1047        }
1048
1049        return kml.toString();
1050    }
1051
1052
1053    private static String GeoJSONize(ArrayList<ShapeInfo3D> shapes,
1054                                     ArrayList<ShapeInfo3D> modifiers,
1055                                     IPointConversion ipc,
1056                                     boolean normalize,
1057                                     Color textColor,
1058                                     Color textBackgroundColor) {
1059
1060        ShapeInfo3D tempModifier = null;
1061        StringBuilder fc = new StringBuilder();//JSON feature collection
1062
1063        fc.append("[");
1064
1065        int len = shapes.size();
1066        for (int i = 0; i < len; i++) {
1067
1068            String shapesToAdd = ShapeToGeoJSONString(shapes.get(i), ipc, normalize);
1069            if (shapesToAdd.length() > 0) {
1070                fc.append(shapesToAdd);
1071                if (i < len - 1) {
1072                    fc.append(",");
1073                }
1074            }
1075        }
1076
1077        int len2 = modifiers.size();
1078
1079        for (int j = 0; j < len2; j++) {
1080            tempModifier = modifiers.get(j);
1081
1082            String modifiersToAdd = null;
1083            if (modifiers.get(j).getModifierImage() != null) {
1084                modifiersToAdd = ImageToGeoJSONString(tempModifier, ipc, normalize);
1085            } else {
1086                modifiersToAdd = LabelToGeoJSONString(tempModifier, ipc, normalize, textColor, textBackgroundColor);
1087            }
1088            if (modifiersToAdd.length() > 0) {
1089                if (fc.length() > 1) fc.append(",");
1090                fc.append(modifiersToAdd);
1091            }
1092        }
1093        fc.append("]");
1094        String GeoJSON = fc.toString();
1095        return GeoJSON;
1096    }
1097
1098    private static String ShapeToGeoJSONString(ShapeInfo3D shapeInfo, IPointConversion ipc, boolean normalize) {
1099        StringBuilder JSONed = new StringBuilder();
1100        StringBuilder properties = new StringBuilder();
1101        StringBuilder geometry = new StringBuilder();
1102        String geometryType = null;
1103        String sda = null;
1104        /*
1105         NOTE: Google Earth / KML colors are backwards.
1106         They are ordered Alpha,Blue,Green,Red, not Red,Green,Blue,Aplha like the rest of the world
1107         * */
1108        Color lineColor = shapeInfo.getLineColor();
1109        Color fillColor = shapeInfo.getFillColor();
1110
1111        if (shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_FILL || fillColor != null || shapeInfo.getPatternFillImage() != null) {
1112            geometryType = "\"Polygon\"";
1113        } else //if(shapeInfo.getShapeType() == ShapeInfo.SHAPE_TYPE_POLYLINE)
1114        {
1115            geometryType = "\"MultiLineString\"";
1116        }
1117
1118        BasicStroke stroke = null;
1119        stroke = shapeInfo.getStroke();
1120        int lineWidth = 4;
1121
1122        if (stroke != null) {
1123            lineWidth = (int) stroke.getLineWidth();
1124        }
1125
1126        //generate JSON properties for feature
1127        properties.append("\"properties\":{");
1128        properties.append("\"label\":\"\",");
1129        if (lineColor != null) {
1130            properties.append("\"strokeColor\":\"").append(RendererUtilities.colorToHexString(lineColor, false)).append("\",");
1131            properties.append("\"lineOpacity\":").append(String.valueOf(lineColor.getAlpha() / 255f)).append(",");
1132        }
1133        if (fillColor != null) {
1134            properties.append("\"fillColor\":\"").append(RendererUtilities.colorToHexString(fillColor, false)).append("\",");
1135            properties.append("\"fillOpacity\":").append(String.valueOf(fillColor.getAlpha() / 255f)).append(",");
1136        }
1137        if (shapeInfo.getPatternFillImage() != null) {
1138            properties.append("\"fillPattern\":\"").append(MultiPointHandler.bitmapToString(shapeInfo.getPatternFillImage())).append("\",");
1139        }
1140        if (stroke.getDashArray() != null) {
1141            float[] arrSDA = stroke.getDashArray();
1142            sda = "[";
1143            sda += String.valueOf(arrSDA[0]);
1144            if (arrSDA.length > 1) {
1145                for (int i = 1; i < arrSDA.length; i++) {
1146                    sda = sda + ", " + String.valueOf(arrSDA[i]);
1147                }
1148            }
1149            sda += "]";
1150            sda = "\"strokeDasharray\":" + sda + ",";
1151            properties.append(sda);
1152        }
1153
1154        int lineCap = stroke.getEndCap();
1155        properties.append("\"lineCap\":").append(lineCap).append(",");
1156
1157        String strokeWidth = String.valueOf(lineWidth);
1158        properties.append("\"strokeWidth\":").append(strokeWidth).append(",");
1159        properties.append("\"strokeWeight\":").append(strokeWidth);
1160        properties.append("},");
1161
1162
1163        properties.append("\"style\":{");
1164        if (lineColor != null) {
1165            properties.append("\"stroke\":\"").append(RendererUtilities.colorToHexString(lineColor, false)).append("\",");
1166            properties.append("\"line-opacity\":").append(String.valueOf(lineColor.getAlpha() / 255f)).append(",");
1167        }
1168        if (fillColor != null) {
1169            properties.append("\"fill\":\"").append(RendererUtilities.colorToHexString(fillColor, false)).append("\",");
1170            properties.append("\"fill-opacity\":").append(String.valueOf(fillColor.getAlpha() / 255f)).append(",");
1171        }
1172        if (stroke.getDashArray() != null) {
1173            float[] da = stroke.getDashArray();
1174            sda = String.valueOf(da[0]);
1175            if (da.length > 1) {
1176                for (int i = 1; i < da.length; i++) {
1177                    sda = sda + " " + String.valueOf(da[i]);
1178                }
1179            }
1180            sda = "\"stroke-dasharray\":\"" + sda + "\",";
1181            properties.append(sda);
1182            sda = null;
1183        }
1184
1185        if (lineCap == BasicStroke.CAP_SQUARE) properties.append("\"stroke-linecap\":\"square\",");
1186        else if (lineCap == BasicStroke.CAP_ROUND)
1187            properties.append("\"stroke-linecap\":\"round\",");
1188        else if (lineCap == BasicStroke.CAP_BUTT) properties.append("\"stroke-linecap\":\"butt\",");
1189
1190        strokeWidth = String.valueOf(lineWidth);
1191        properties.append("\"stroke-width\":").append(strokeWidth);
1192        properties.append("}");
1193
1194
1195        //generate JSON geometry for feature
1196        geometry.append("\"geometry\":{\"type\":");
1197        geometry.append(geometryType);
1198        geometry.append(",\"coordinates\":[");
1199
1200        ArrayList<ArrayList<Point3D>> shapesArray = shapeInfo.getPolylines3D();
1201
1202        for (int i = 0; i < shapesArray.size(); i++) {
1203            ArrayList<Point3D> pointList = shapesArray.get(i);
1204
1205            normalize = normalizePoints(pointList, ipc);
1206
1207            geometry.append("[");
1208
1209            //System.out.println("Pixel Coords:");
1210            for (int j = 0; j < pointList.size(); j++) {
1211                Point3D coord = pointList.get(j);
1212                Point2D geoCoord = ipc.PixelsToGeo(coord);
1213                //M. Deutch 9-27-11
1214                if (normalize) {
1215                    geoCoord = MultiPointHandler.NormalizeCoordToGECoord(geoCoord);
1216                }
1217                double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
1218                double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
1219                double altitude = coord.getZ();
1220
1221                //fix for fill crossing DTL
1222                if (normalize && fillColor != null) {
1223                    if (longitude > 0) {
1224                        longitude -= 360;
1225                    }
1226                }
1227
1228                //diagnostic M. Deutch 10-18-11
1229                //set the point as geo so that the
1230                //coord.setLocation(longitude, latitude);
1231                coord = new Point3D(longitude, latitude, altitude);
1232                pointList.set(j, coord);
1233                //end section
1234
1235                geometry.append("[");
1236                geometry.append(longitude);
1237                geometry.append(",");
1238                geometry.append(latitude);
1239                geometry.append(",");
1240                geometry.append(altitude);
1241                geometry.append("]");
1242
1243                if (j < (pointList.size() - 1)) {
1244                    geometry.append(",");
1245                }
1246            }
1247
1248            geometry.append("]");
1249
1250            if (i < (shapesArray.size() - 1)) {
1251                geometry.append(",");
1252            }
1253        }
1254        geometry.append("]}");
1255
1256        JSONed.append("{\"type\":\"Feature\",");
1257        JSONed.append(properties.toString());
1258        JSONed.append(",");
1259        JSONed.append(geometry.toString());
1260        JSONed.append("}");
1261
1262        return JSONed.toString();
1263    }
1264
1265    private static String ImageToGeoJSONString(ShapeInfo3D shapeInfo, IPointConversion ipc, boolean normalize) {
1266        StringBuilder JSONed = new StringBuilder();
1267
1268        //AffineTransform at = shapeInfo.getAffineTransform();
1269        //Point2D coord = (Point2D)new Point2D.Double(at.getTranslateX(), at.getTranslateY());
1270        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
1271        Point3D coord = new Point3D(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY(), shapeInfo.getModifierPosition().getZ());
1272        Point2D geoCoord = ipc.PixelsToGeo(coord);
1273        //M. Deutch 9-27-11
1274        if (normalize) {
1275            geoCoord = MultiPointHandler.NormalizeCoordToGECoord(geoCoord);
1276        }
1277        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
1278        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
1279        double altitude = coord.getZ();
1280        double angle = shapeInfo.getModifierAngle();
1281        coord.setLocation(longitude, latitude);
1282
1283        //diagnostic M. Deutch 10-18-11
1284        shapeInfo.setGlyphPosition(coord);
1285
1286        Bitmap image = shapeInfo.getModifierImage();
1287
1288        RendererSettings RS = RendererSettings.getInstance();
1289
1290        if (image != null) {
1291
1292            JSONed.append("{\"type\":\"Feature\",\"properties\":{\"image\":\"");
1293            JSONed.append(MultiPointHandler.bitmapToString(image));
1294            JSONed.append("\",\"rotation\":");
1295            JSONed.append(angle);
1296            JSONed.append(",\"angle\":");
1297            JSONed.append(angle);
1298            JSONed.append("},");
1299            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
1300            JSONed.append(longitude);
1301            JSONed.append(",");
1302            JSONed.append(latitude);
1303            JSONed.append(",");
1304            JSONed.append(altitude);
1305            JSONed.append("]");
1306            JSONed.append("}}");
1307
1308        } else {
1309            return "";
1310        }
1311
1312        return JSONed.toString();
1313    }
1314
1315    private static String LabelToGeoJSONString(ShapeInfo3D shapeInfo, IPointConversion ipc, boolean normalize, Color textColor, Color textBackgroundColor) {
1316        StringBuilder JSONed = new StringBuilder();
1317
1318        Color outlineColor = MultiPointHandler.getIdealTextBackgroundColor(textColor);
1319        if (textBackgroundColor != null) outlineColor = textBackgroundColor;
1320
1321        //AffineTransform at = shapeInfo.getAffineTransform();
1322        //Point2D coord = (Point2D)new Point2D.Double(at.getTranslateX(), at.getTranslateY());
1323        //Point2D coord = (Point2D) new Point2D.Double(shapeInfo.getGlyphPosition().getX(), shapeInfo.getGlyphPosition().getY());
1324        Point3D coord = new Point3D(shapeInfo.getModifierPosition().getX(), shapeInfo.getModifierPosition().getY(), shapeInfo.getModifierPosition().getZ());
1325        Point2D geoCoord = ipc.PixelsToGeo(coord);
1326        //M. Deutch 9-27-11
1327        if (normalize) {
1328            geoCoord = MultiPointHandler.NormalizeCoordToGECoord(geoCoord);
1329        }
1330        double latitude = Math.round(geoCoord.getY() * 100000000.0) / 100000000.0;
1331        double longitude = Math.round(geoCoord.getX() * 100000000.0) / 100000000.0;
1332        double altitude = coord.getZ();
1333        double angle = shapeInfo.getModifierAngle();
1334        coord.setLocation(longitude, latitude);
1335
1336        //diagnostic M. Deutch 10-18-11
1337        shapeInfo.setGlyphPosition(coord);
1338
1339        String text = shapeInfo.getModifierString();
1340
1341        int justify = shapeInfo.getTextJustify();
1342        String strJustify = "left";
1343        if (justify == 0) strJustify = "left";
1344        else if (justify == 1) strJustify = "center";
1345        else if (justify == 2) strJustify = "right";
1346
1347
1348        RendererSettings RS = RendererSettings.getInstance();
1349
1350        if (text != null && text.equals("") == false) {
1351
1352            JSONed.append("{\"type\":\"Feature\",\"properties\":{\"label\":\"");
1353            JSONed.append(text);
1354            JSONed.append("\",\"pointRadius\":0,\"fontColor\":\"");
1355            JSONed.append(RendererUtilities.colorToHexString(textColor, false));
1356            JSONed.append("\",\"fontSize\":\"");
1357            JSONed.append(String.valueOf(RS.getMPLabelFontSize())).append("pt\"");
1358            JSONed.append(",\"fontFamily\":\"");
1359            JSONed.append(RS.getMPLabelFontName());
1360            JSONed.append(", sans-serif");
1361
1362            if (RS.getMPLabelFontType() == Typeface.BOLD) {
1363                JSONed.append("\",\"fontWeight\":\"bold\"");
1364            } else {
1365                JSONed.append("\",\"fontWeight\":\"normal\"");
1366            }
1367
1368            //JSONed.append(",\"labelAlign\":\"lm\"");
1369            JSONed.append(",\"labelAlign\":\"");
1370            JSONed.append(strJustify);
1371            JSONed.append("\",\"labelBaseline\":\"alphabetic\"");
1372            JSONed.append(",\"labelXOffset\":0");
1373            JSONed.append(",\"labelYOffset\":0");
1374            JSONed.append(",\"labelOutlineColor\":\"");
1375            JSONed.append(RendererUtilities.colorToHexString(outlineColor, false));
1376            JSONed.append("\",\"labelOutlineWidth\":");
1377            JSONed.append("4");
1378            JSONed.append(",\"rotation\":");
1379            JSONed.append(angle);
1380            JSONed.append(",\"angle\":");
1381            JSONed.append(angle);
1382            JSONed.append("},");
1383
1384            JSONed.append("\"geometry\":{\"type\":\"Point\",\"coordinates\":[");
1385            JSONed.append(longitude);
1386            JSONed.append(",");
1387            JSONed.append(latitude);
1388            JSONed.append(",");
1389            JSONed.append(altitude);
1390            JSONed.append("]");
1391            JSONed.append("}}");
1392
1393        } else {
1394            return "";
1395        }
1396
1397        return JSONed.toString();
1398    }
1399
1400    /**
1401     * copy of {@link MultiPointHandler#normalizePoints(ArrayList, IPointConversion)} with Point3D
1402     */
1403    static Boolean normalizePoints(ArrayList<Point3D> shape, IPointConversion ipc) {
1404        ArrayList geoCoords = new ArrayList();
1405        int n = shape.size();
1406        //for (int j = 0; j < shape.size(); j++)
1407        for (int j = 0; j < n; j++) {
1408            Point2D coord = shape.get(j);
1409            Point2D geoCoord = ipc.PixelsToGeo(coord);
1410            geoCoord = MultiPointHandler.NormalizeCoordToGECoord(geoCoord);
1411            double latitude = geoCoord.getY();
1412            double longitude = geoCoord.getX();
1413            Point2D pt2d = new Point2D.Double(longitude, latitude);
1414            geoCoords.add(pt2d);
1415        }
1416        Boolean normalize = MultiPointHandler.crossesIDL(geoCoords);
1417        return normalize;
1418    }
1419}