001package sec.geo.kml;
002
003import armyc2.c2sd.graphics2d.PathIterator;
004import sec.sun.awt.geom.AreaIterator;
005import java.util.ArrayList;
006import sec.geo.utilities.StringBuilder;
007
008import sec.geo.shape.*;
009
010public class KmlRenderer {
011
012    private static final String EXCEPTION = "EXCEPTION:";
013
014    private static final String colorDefault = "ff003fff";
015    private static final String descriptionField = "#DESCRIPTION#";
016    private static final String extendedData = "#EXTENDEDDATA#";
017    private static final String colorField = "#COLOR#";
018    private static final String nameField = "#NAME#";
019    private static final String idField = "#ID#";
020    private static final String placemarkidField = "#PLACEMARKID#";
021
022    private static final String KML_START = "<Folder id=\"" + idField + "\">\n";
023    private static final String KML_END = "</Folder>\n";
024
025    private static final String PLACEMARK_START = ""
026            + "         <Placemark id=\"" + placemarkidField + "\">\n"
027            + "                      <Style>\n"
028            + "                          <PolyStyle>\n"
029            + "                              <color>" + colorField + "</color>\n"
030            + "                          </PolyStyle>\n"
031            + "                          <LineStyle>\n"
032            + "                              <color>" + colorField + "</color>\n"
033            + "                          </LineStyle>\n"
034            + "                       </Style>\n"
035            + "                 <name>" + nameField + "</name>\n"
036            + "                 <description>" + descriptionField + "</description>\n"
037            + "                 <ExtendedData>" + extendedData + "</ExtendedData>\n"
038            + "                 <MultiGeometry>\n";
039
040    private static final String PLACEMARK_END = ""
041            + "                 </MultiGeometry>\n"
042            + "         </Placemark>\n";
043
044        //private static final String DEFAULT_EXDAT = "<Data name='sid'><value>#ID#</value></Data><Data name='shapeType'><value>#SHAPETYPE#</value></Data><Data name='lat'><value>#LAT#</value></Data><Data name='lon'><value>#LON#</value></Data><Data name='alt'><value>#ALT#</value></Data>";
045    //private static final String DEFAULT_BLSTY = "<!" + "[CDAT" + "A[" + "$[sid]" +"]]"+">";
046    public ArrayList<KmlPolygon> renderPolygons(AExtObject ext) {
047        ArrayList<KmlPolygon> polys = new ArrayList<KmlPolygon>();
048
049        ext.setMaxDistance(200000);
050        //ext.setFlatness(1);
051        //ext.setLimit(3);
052        ext.setFlatness(2);
053        ext.setLimit(8);
054
055        // Render perimeter polys
056        ArrayList<Point> perimeterPoints = new ArrayList<Point>();
057        PathIterator it = null;
058        AreaIterator ait = null;
059        //PathIterator it = ext.getShape().getPathIterator(null);
060        Object oit = ext.getPathIterator(null);
061        if (oit instanceof PathIterator) {
062            it = (PathIterator) oit;
063        } else if (oit instanceof AreaIterator) {
064            ait = (AreaIterator) oit;
065        }
066
067        Point pre = null;
068        if (it != null) {
069            while (!it.isDone()) {
070
071                double[] strokePoints = new double[6];
072                int type = it.currentSegment(strokePoints);
073
074                double longitudeDegrees = strokePoints[0];
075                double latitudeDegrees = strokePoints[1];
076                if (longitudeDegrees < -180) {
077                    longitudeDegrees += 360;
078                }
079                switch (type) {
080                    case PathIterator.SEG_MOVETO:
081                    case PathIterator.SEG_LINETO:
082                        if (pre != null) {
083                            ArrayList<Point> ps = new ArrayList<Point>();
084                            ps.add(new Point(pre.getLongitude(), pre.getLatitude(), ext.getMinAltitude()));
085                            ps.add(new Point(pre.getLongitude(), pre.getLatitude(), ext.getMaxAltitude()));
086                            ps.add(new Point(longitudeDegrees, latitudeDegrees, ext.getMaxAltitude()));
087                            ps.add(new Point(longitudeDegrees, latitudeDegrees, ext.getMinAltitude()));
088                            ps.add(new Point(pre.getLongitude(), pre.getLatitude(), ext.getMinAltitude()));
089                            polys.add(new KmlPolygon(ps, ext.getAltitudeMode()));
090                        }
091                        pre = new Point(longitudeDegrees, latitudeDegrees);
092                        perimeterPoints.add(pre);
093                }
094                it.next();
095            }
096        } else if (ait != null) {
097            while (!ait.isDone()) {
098
099                double[] strokePoints = new double[6];
100                int type = ait.currentSegment(strokePoints);
101
102                double longitudeDegrees = strokePoints[0];
103                double latitudeDegrees = strokePoints[1];
104                if (longitudeDegrees < -180) {
105                    longitudeDegrees += 360;
106                }
107                switch (type) {
108                    case PathIterator.SEG_MOVETO:
109                    case PathIterator.SEG_LINETO:
110                        if (pre != null) {
111                            ArrayList<Point> ps = new ArrayList<Point>();
112                            ps.add(new Point(pre.getLongitude(), pre.getLatitude(), ext.getMinAltitude()));
113                            ps.add(new Point(pre.getLongitude(), pre.getLatitude(), ext.getMaxAltitude()));
114                            ps.add(new Point(longitudeDegrees, latitudeDegrees, ext.getMaxAltitude()));
115                            ps.add(new Point(longitudeDegrees, latitudeDegrees, ext.getMinAltitude()));
116                            ps.add(new Point(pre.getLongitude(), pre.getLatitude(), ext.getMinAltitude()));
117                            polys.add(new KmlPolygon(ps, ext.getAltitudeMode()));
118                        }
119                        pre = new Point(longitudeDegrees, latitudeDegrees);
120                        perimeterPoints.add(pre);
121                }
122                ait.next();
123            }
124        }
125        // Render top and bottom poly if the perimeter is complete
126        if (perimeterPoints.size() > 0) {
127                    // In some weird cases, for routes, when it builds an area, it will drop the closing
128            // point in the shape.   Route uses an area.  This causes this condition to not execute.
129            // adding the first point to the perimeterPoints fixes the issue, not sure if it causes any
130            // side effects.
131            if (perimeterPoints.get(0).equals(perimeterPoints.get(perimeterPoints.size() - 1))) {
132
133                polys.add(new KmlPolygon(transformPoints(perimeterPoints, ext.getMinAltitude()), ext.getAltitudeMode()));
134                polys.add(new KmlPolygon(transformPoints(perimeterPoints, ext.getMaxAltitude()), ext.getAltitudeMode()));
135            } else {
136                perimeterPoints.add(perimeterPoints.get(0));
137
138                polys.add(new KmlPolygon(transformPoints(perimeterPoints, ext.getMinAltitude()), ext.getAltitudeMode()));
139                polys.add(new KmlPolygon(transformPoints(perimeterPoints, ext.getMaxAltitude()), ext.getAltitudeMode()));
140            }
141        }
142
143        return polys;
144    }
145
146    //public String getPlacemarkKml(AExtrusion ext, String id, String name, String description, String lineColor, String fillColor) {
147    public String getPlacemarkKml(AExtObject ext, String id, String name, String description, String lineColor, String fillColor) {
148
149        try {
150            StringBuilder sb = new StringBuilder();
151            ArrayList<KmlPolygon> polys = renderPolygons(ext);
152            sb.append(PLACEMARK_START);
153            int placemarkIdIndex = sb.indexOf(placemarkidField);
154            int placemarkIdLength = placemarkidField.length();
155            if (id != null) {
156                sb.replace(placemarkIdIndex, placemarkIdIndex + placemarkIdLength, id + "_mg");
157            }
158
159            int descriptionIndex = sb.indexOf(descriptionField);
160            int descriptionLength = descriptionField.length();
161            if (description != null) {
162                sb.replace(descriptionIndex, descriptionIndex + descriptionLength, description);
163            }
164            int colorIndex = sb.indexOf(colorField);
165            int colorLength = colorField.length();
166            if (fillColor != null) {
167                sb.replace(colorIndex, colorIndex + colorLength, fillColor);
168            } else {
169                sb.replace(colorIndex, colorIndex + colorLength, colorDefault);
170            }
171
172            int lineColorIndex = sb.indexOf2(colorField, colorIndex + colorLength);
173            if (lineColor != null) {
174                sb.replace(lineColorIndex, lineColorIndex + colorLength, lineColor);
175            } else {
176                sb.replace(lineColorIndex, lineColorIndex + colorLength, colorDefault);
177            }
178
179            int nameIndex = sb.indexOf(nameField);
180            int nameLength = nameField.length();
181            if (name != null) {
182                sb.replace(nameIndex, nameIndex + nameLength, name);
183            }
184
185            for (KmlPolygon poly : polys) {
186
187                sb.append(poly.toString());
188            }
189
190            sb.append(PLACEMARK_END);
191            return sb.toString();
192        } catch (Exception e) {
193            e.printStackTrace();
194            return EXCEPTION + e.getMessage();
195        }
196
197    }
198
199    //public String getKml(AExtrusion ext, String id, String name, String description, String lineColor, String fillColor) {
200
201    public String getKml(Object ext, String id, String name, String description, String lineColor, String fillColor) {
202        try {
203            AExtObject aext = getAExtObject(ext);
204            if (aext.getElements() != null) {
205                return (getTrackKml(ext, id, name, description, lineColor, fillColor));
206            }
207            StringBuilder sb = new StringBuilder();
208            sb.append(KML_START);
209            int idIndex = sb.indexOf(idField);
210            int idLength = idField.length();
211            sb.replace(idIndex, idIndex + idLength, id);
212            sb.append(getPlacemarkKml(aext, id, name, description, lineColor, fillColor));
213            sb.append(KML_END);
214            return sb.toString();
215        } catch (Exception e) {
216            e.printStackTrace();
217            return EXCEPTION + e.getMessage();
218        }
219    }
220
221    public String getTrackKml(Object ext, String id, String name, String description, String lineColor, String fillColor) {
222        try {
223            AExtObject aext = getAExtObject(ext);
224            StringBuilder sb = new StringBuilder();
225            sb.append(this.KML_START);
226            int idIndex = sb.indexOf(this.idField);
227            int idLength = idField.length();
228            sb.replace(idIndex, idIndex + idLength, id);
229
230            ArrayList elements = aext.getElements();
231            int j = 0;
232            int n = elements.size();
233            //for (j = 0; j < elements.size(); j++)
234            for (j = 0; j < n; j++) {
235                Route route = (Route) elements.get(j);
236                AExtObject aext2 = new sec.geo.shape.AExtObject(route);
237                sb.append(this.getPlacemarkKml(aext2, id, name, description, lineColor, fillColor));
238            }
239
240            sb.append(this.KML_END);
241            return sb.toString();
242        } catch (Exception e) {
243            e.printStackTrace();
244            return EXCEPTION + e.getMessage();
245        }
246    }
247
248    public String getCakeKml(Cake com, String id, String name, String description, String lineColor, String fillColor) {
249        StringBuilder sb = new StringBuilder();
250        //Set<KmlPolygon> polys;
251        sb.append(KML_START);
252        int idIndex = sb.indexOf(idField);
253        int idLength = idField.length();
254        sb.replace(idIndex, idIndex + idLength, id);
255
256        for (Object ext : com.getElements()) {
257            AExtObject aext = new AExtObject(ext);
258            String extStr = getPlacemarkKml(aext, id, name, description, lineColor, fillColor);
259
260            if (!extStr.startsWith(EXCEPTION)) {
261                sb.append(extStr);
262            }
263        }
264
265        sb.append(KML_END);
266        return sb.toString();
267    }
268
269    private AExtObject getAExtObject(Object obj) {
270        Line line = null;
271        Route route = null;
272        Circle circle = null;
273        Polyarc polyarc = null;
274        Polygon polygon = null;
275        Orbit orbit = null;
276        Radarc radarc = null;
277        Track track = null;
278        Cake cake = null;
279        Point point = null;
280        AExtObject ext = null;
281        if (obj instanceof Line) {
282            line = (Line) obj;
283            ext = new AExtObject(line);
284        } else if (obj instanceof Route) {
285            route = (Route) obj;
286            ext = new AExtObject(route);
287        } else if (obj instanceof Polyarc) {
288            polyarc = (Polyarc) obj;
289            ext = new AExtObject(polyarc);
290        } else if (obj instanceof Orbit) {
291            orbit = (Orbit) obj;
292            ext = new AExtObject(orbit);
293        } else if (obj instanceof Polygon) {
294            polygon = (Polygon) obj;
295            ext = new AExtObject(polygon);
296        } else if (obj instanceof Circle) {
297            circle = (Circle) obj;
298            ext = new AExtObject(circle);
299        } else if (obj instanceof Radarc) {
300            radarc = (Radarc) obj;
301            ext = new AExtObject(radarc);
302        } else if (obj instanceof Track) {
303            track = (Track) obj;
304            ext = new AExtObject(track);
305        } else if (obj instanceof Cake) {
306            cake = (Cake) obj;
307            ext = new AExtObject(cake);
308        } else if (obj instanceof Point) {
309            point = (Point) obj;
310            ext = new AExtObject(point);
311        }
312        return ext;
313    }
314
315    public String[] getCoords(Object obj) {
316        AExtObject ext = getAExtObject(obj);
317        ArrayList<KmlPolygon> polys = renderPolygons(ext);
318
319        // Iterate through the polygons and produce an array of KML coordinates
320        String[] coords = new String[polys.size()];
321        int i = 0;
322        for (KmlPolygon poly : polys) {
323            coords[i] = poly.toCoordString();
324            i++;
325        }
326        return coords;
327    }
328
329    private ArrayList<Point> transformPoints(ArrayList<Point> points, double altitudeMeters) {
330        ArrayList<Point> returnPoints = new ArrayList<Point>();
331        for (Point p : points) {
332            returnPoints.add(new Point(p.getLongitude(), p.getLatitude(), altitudeMeters));
333        }
334        return returnPoints;
335    }
336}