001package sec.geo.kml;
002
003import java.util.ArrayList;
004import org.gavaghan.geodesy.Ellipsoid;
005import sec.geo.kml.KmlOptions.AltitudeMode;
006import sec.geo.shape.Point;
007import sec.geo.utilities.StringBuilder;
008
009public class KmlPolygon {
010
011    private final ArrayList<Point> points;
012
013    private AltitudeMode altitudeMode = AltitudeMode.ABSOLUTE;
014
015    protected static final Ellipsoid REFERENCE_ELLIPSOID = Ellipsoid.WGS84;
016    private static final String altitudeModeField = "#ALTITUDEMODE#";
017
018    private static final String PREFIX = ""
019            + "                         <Polygon>\n"
020            + "                                 <tessellate>1</tessellate>\n"
021            + "                                 <altitudeMode>" + altitudeModeField + "</altitudeMode>\n"
022            + "                                 <outerBoundaryIs><LinearRing><coordinates>";
023    private static final String SUFFIX = ""
024            + "                                 </coordinates></LinearRing></outerBoundaryIs>\n"
025            + "                         </Polygon>\n";
026
027    public KmlPolygon() {
028        points = new ArrayList<Point>();
029    }
030
031    public KmlPolygon(ArrayList<Point> points, AltitudeMode altitudeMode) {
032        this();
033        this.points.addAll(points);
034        this.altitudeMode = altitudeMode;
035    }
036
037    public void addPoint(Point point) {
038        points.add(point);
039    }
040
041    public void addPoints(ArrayList<Point> points) {
042        this.points.addAll(points);
043    }
044
045    @Override
046    public String toString() {
047        StringBuilder sb = new StringBuilder();
048
049        sb.append(PREFIX);
050        sb.append(toCoordString());
051        sb.append(SUFFIX);
052
053        int altitudeModeIndex = sb.indexOf(altitudeModeField);
054        int altitudeModeLength = altitudeModeField.length();
055        if (altitudeMode != null) {
056            sb.replace(altitudeModeIndex, altitudeModeIndex + altitudeModeLength, altitudeMode.toString());
057        }
058
059        return sb.toString();
060    }
061
062    public String toCoordString() {
063        StringBuilder sb = new StringBuilder();
064
065        ArrayList<Point> orderedPoints = getPointsCounterClockwise();
066        if (orderedPoints == null) {
067            return "";
068        }
069
070        for (Point point : orderedPoints) {
071            sb.append(point.getLongitude());
072            sb.append(",");
073            sb.append(point.getLatitude());
074            sb.append(",");
075            sb.append(point.getAltitude());
076            sb.append(" ");
077        }
078
079        // Close off the list of coordinates if necessary
080        Point point = orderedPoints.get(0);
081        if (!point.equals(orderedPoints.get(orderedPoints.size() - 1))) {
082            sb.append(point.getLongitude());
083            sb.append(",");
084            sb.append(point.getLatitude());
085            sb.append(",");
086            sb.append(point.getAltitude());
087            sb.append(" ");
088        }
089
090        return sb.toString();
091    }
092
093    public AltitudeMode getAltitudeMode() {
094        return altitudeMode;
095    }
096
097    public void setAltitudeMode(AltitudeMode altitudeMode) {
098        this.altitudeMode = altitudeMode;
099    }
100
101    public ArrayList<Point> getPointsClockwise() {  //did return List
102        if (points == null || points.size() < 3) {
103            return null;
104        }
105
106        //List<Point> result = points.subList(0, points.size()-1);
107        ArrayList<Point> result = subList(points, 0, points.size() - 1);
108        int order = getPointOrder();
109        if (order < 0) {
110            //Collections.reverse(result);
111            result = reverse(result);
112            return result;
113        } else {
114            return result;
115        }
116    }
117
118    public ArrayList<Point> getPointsCounterClockwise() {   //did return List
119        if (points == null || points.size() < 3) {
120            return null;
121        }
122
123        //List<Point> result = points.subList(0, points.size()-1);
124        ArrayList<Point> result = subList(points, 0, points.size() - 1);
125        int order = getPointOrder();
126        if (order > 0) {
127            //Collections.reverse(result);
128            result = reverse(result);
129            return result;
130        } else {
131            return result;
132        }
133    }
134
135    public int getPointOrder() {
136        if (points == null || points.size() < 3) {
137            return 0;
138        }
139
140        int n = points.size();
141        int j, k, count = 0;
142        double z;
143        for (int i = 0; i < n; i++) {
144            j = (i + 1) % n;
145            k = (i + 2) % n;
146            z = (points.get(j).getLongitude() - points.get(i).getLongitude()) * (points.get(k).getLatitude() - points.get(i).getLatitude());
147            z -= (points.get(j).getLatitude() - points.get(i).getLatitude()) * (points.get(k).getLongitude() - points.get(i).getLongitude());
148            if (z < 0) {
149                count--;
150            } else if (z > 0) {
151                count++;
152            }
153        }
154        if (count > 0) {
155            return -1;  //counterclockwise
156        } else if (count < 0) {
157            return 1;   //clockwise
158        } else {
159            return 0;   //invalid
160        }
161    }
162    /*
163     * ArrayList from fromIndex, inclusive to toIndex, exclusive
164     * 
165     */
166
167    private ArrayList subList(ArrayList al, int fromIndex, int toIndex) {
168        ArrayList result = new ArrayList();
169        int j = 0;
170        for (j = fromIndex; j < toIndex; j++) {
171            result.add(al.get(j));
172        }
173
174        return result;
175    }
176    /*
177     * reverses the order of an arraylist
178     */
179
180    private ArrayList reverse(ArrayList al) {
181        int j = 0;
182        ArrayList result = new ArrayList();
183        int n = al.size();
184        //for(j=al.size()-1;j>=0;j--)            
185        for (j = n - 1; j >= 0; j--) {
186            result.add(al.get(j));
187        }
188        return result;
189    }
190
191}