package org.osmdroid.bonuspack.kml; import android.content.Context; import android.os.Parcel; import com.google.gson.JsonObject; import org.osmdroid.bonuspack.kml.KmlFeature.Styler; import org.osmdroid.bonuspack.utils.BonusPackHelper; import org.osmdroid.util.BoundingBox; import org.osmdroid.util.GeoPoint; import org.osmdroid.views.MapView; import org.osmdroid.views.overlay.Overlay; import org.osmdroid.views.overlay.Polyline; import org.osmdroid.views.overlay.infowindow.BasicInfoWindow; import java.io.IOException; import java.io.Writer; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; /** * KML gx:Track * @author M.Kergall */ public class KmlTrack extends KmlGeometry { static int mDefaultLayoutResId = BonusPackHelper.UNDEFINED_RES_ID; public ArrayList<Date> mWhen; //public ArrayList<GeoPoint> mAngles; //TODO later... public KmlTrack(){ super(); mCoordinates = new ArrayList<>(); mWhen = new ArrayList<>(); } /** * @param sGxCoord gx:Coord, string with "lon lat alt", comma-separated. * @return coord as a GeoPoint, or null if the element is empty or if parsing issue. */ public static GeoPoint parseKmlGxCoord(String sGxCoord) { int end1 = sGxCoord.indexOf(' '); int end2 = sGxCoord.indexOf(' ', end1+1); try { double lon = Double.parseDouble(sGxCoord.substring(0, end1)); double lat = Double.parseDouble(sGxCoord.substring(end1+1, end2)); double alt = Double.parseDouble(sGxCoord.substring(end2+1, sGxCoord.length())); return new GeoPoint(lat, lon, alt); } catch (NumberFormatException e) { return null; } catch (IndexOutOfBoundsException e) { return null; } } public void addGxCoord(String sGxCoord){ mCoordinates.add(parseKmlGxCoord(sGxCoord)); } /** * @param sWhen "when" string, in one of the KML dateTime formats. Local time format not supported yet. * @return java Date if success, or null */ public static Date parseKmlWhen(String sWhen) { SimpleDateFormat ft; switch (sWhen.length()) { case 4: ft = new SimpleDateFormat("yyyy"); break; case 7: ft = new SimpleDateFormat("yyyy-MM"); break; case 10: ft = new SimpleDateFormat("yyyy-MM-dd"); break; case 19: ft = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); break; case 20: ft = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); break; default: return null; } Date when; try { when = ft.parse(sWhen); return when; } catch (ParseException e) { return null; } } public void addWhen(String sWhen){ if (mWhen == null) mWhen = new ArrayList<>(); mWhen.add(parseKmlWhen(sWhen)); } /** * Add a time element (coord+when) to the track. * * @param coord * @param when */ public void add(GeoPoint coord, Date when) { if (coord == null) mCoordinates.add(coord); else mCoordinates.add(coord.clone()); mWhen.add(when); } public void applyDefaultStyling(Polyline lineStringOverlay, Style defaultStyle, KmlPlacemark kmlPlacemark, KmlDocument kmlDocument, MapView map){ Context context = map.getContext(); Style style = kmlDocument.getStyle(kmlPlacemark.mStyle); if (style != null){ lineStringOverlay.setColor(style.getOutlinePaint().getColor()); lineStringOverlay.setWidth(style.getOutlinePaint().getStrokeWidth()); } else if (defaultStyle!=null && defaultStyle.mLineStyle!=null){ lineStringOverlay.setColor(defaultStyle.getOutlinePaint().getColor()); lineStringOverlay.setWidth(defaultStyle.getOutlinePaint().getStrokeWidth()); } if ((kmlPlacemark.mName!=null && !"".equals(kmlPlacemark.mName)) || (kmlPlacemark.mDescription!=null && !"".equals(kmlPlacemark.mDescription)) || (lineStringOverlay.getSubDescription()!=null && !"".equals(lineStringOverlay.getSubDescription())) ){ if (mDefaultLayoutResId == BonusPackHelper.UNDEFINED_RES_ID){ String packageName = context.getPackageName(); mDefaultLayoutResId = context.getResources().getIdentifier("layout/bonuspack_bubble", null, packageName); } lineStringOverlay.setInfoWindow(new BasicInfoWindow(mDefaultLayoutResId, map)); } lineStringOverlay.setEnabled(kmlPlacemark.mVisibility); } /** Build the corresponding overlay. * Currently: a Polyline of gx:coords */ @Override public Overlay buildOverlay(MapView map, Style defaultStyle, Styler styler, KmlPlacemark kmlPlacemark, KmlDocument kmlDocument){ Polyline lineStringOverlay = new Polyline(); lineStringOverlay.setGeodesic(true); lineStringOverlay.setPoints(mCoordinates); lineStringOverlay.setTitle(kmlPlacemark.mName); lineStringOverlay.setSnippet(kmlPlacemark.mDescription); lineStringOverlay.setSubDescription(kmlPlacemark.getExtendedDataAsText()); if (styler != null) styler.onTrack(lineStringOverlay, kmlPlacemark, this); else { applyDefaultStyling(lineStringOverlay, defaultStyle, kmlPlacemark, kmlDocument, map); } return lineStringOverlay; } static final SimpleDateFormat KML_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); @Override public void saveAsKML(Writer writer){ try { writer.write("<gx:Track>\n"); //write when: for (Date when:mWhen){ writer.write("<when>"); if (when != null) writer.write(KML_DATE_FORMAT.format(when)); writer.write("</when>\n"); } //write coords: for (GeoPoint coord:mCoordinates){ writer.write("<gx:coord>"); if (coord != null) writer.write(coord.getLongitude() + " " + coord.getLatitude() + " " + coord.getAltitude()); writer.write("</gx:coord>\n"); } writer.write("</gx:Track>\n"); } catch (IOException e) { e.printStackTrace(); } } @Override public JsonObject asGeoJSON(){ JsonObject json = new JsonObject(); json.addProperty("type", "LineString"); json.add("coordinates", KmlGeometry.geoJSONCoordinates(mCoordinates)); return json; } @Override public BoundingBox getBoundingBox() { return BoundingBox.fromGeoPoints(mCoordinates); } //Cloneable implementation ------------------------------------ @Override public KmlTrack clone(){ KmlTrack cloned = (KmlTrack) super.clone(); cloned.mWhen = new ArrayList<>(mWhen.size()); for (Date d : mWhen) cloned.mWhen.add((Date) d.clone()); return cloned; } //Parcelable implementation ------------ @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeList(mWhen); } public static final Creator<KmlTrack> CREATOR = new Creator<KmlTrack>() { @Override public KmlTrack createFromParcel(Parcel source) { return new KmlTrack(source); } @Override public KmlTrack[] newArray(int size) { return new KmlTrack[size]; } }; public KmlTrack(Parcel in){ super(in); mWhen = in.readArrayList(Date.class.getClassLoader()); } }