package org.osmdroid.bonuspack.kml;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.osmdroid.bonuspack.clustering.MarkerClusterer;
import org.osmdroid.bonuspack.overlays.GroundOverlay;
import org.osmdroid.bonuspack.utils.BonusPackHelper;
import org.osmdroid.util.BoundingBox;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.FolderOverlay;
import org.osmdroid.views.overlay.Marker;
import org.osmdroid.views.overlay.Overlay;
import org.osmdroid.views.overlay.Polygon;
import org.osmdroid.views.overlay.Polyline;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
/**
* KML Folder
* @author M.Kergall
*/
public class KmlFolder extends KmlFeature implements Cloneable, Parcelable {
/** List of KML Features it contains */
public ArrayList<KmlFeature> mItems;
public KmlFolder(){
super();
mItems = new ArrayList<KmlFeature>();
}
public KmlFolder(FolderOverlay overlay, KmlDocument kmlDoc){
this();
addOverlays(overlay.getItems(), kmlDoc);
mName = overlay.getName();
mDescription = overlay.getDescription();
mVisibility = overlay.isEnabled();
}
public KmlFolder(MarkerClusterer overlay, KmlDocument kmlDoc){
this();
addOverlays(overlay.getItems(), kmlDoc);
mName = overlay.getName();
mDescription = overlay.getDescription();
mVisibility = overlay.isEnabled();
}
/** GeoJSON constructor */
public KmlFolder(JsonObject json){
this();
if (json.has("features")){
JsonArray features = json.get("features").getAsJsonArray();
for (JsonElement jsonFeature:features) {
KmlFeature feature = KmlFeature.parseGeoJSON(jsonFeature.getAsJsonObject());
add(feature);
}
}
}
@Override public BoundingBox getBoundingBox(){
BoundingBox BB = null;
for (KmlFeature item:mItems) {
BoundingBox itemBB = item.getBoundingBox();
if (itemBB != null){
if (BB == null){
BB = BonusPackHelper.cloneBoundingBox(itemBB);
} else {
BB = BonusPackHelper.concatBoundingBox(itemBB, BB);
}
}
}
return BB;
}
/**
* Converts the overlay to a KmlFeature and add it inside this.
* Conversion from Overlay subclasses to KML Features is as follow: <br>
* FolderOverlay, MarkerClusterer => Folder<br>
* Marker => Point<br>
* Polygon => Polygon<br>
* Polyline => LineString<br>
* GroundOverlay => GroundOverlay<br>
* Else, add nothing.
* @param overlay to convert and add
* @param kmlDoc for style handling.
* @return true if OK, false if the overlay has not been added.
*/
public boolean addOverlay(Overlay overlay, KmlDocument kmlDoc){
if (overlay == null)
return false;
KmlFeature kmlItem;
if (overlay instanceof GroundOverlay){
kmlItem = new KmlGroundOverlay((GroundOverlay)overlay);
} else if (overlay instanceof FolderOverlay){
kmlItem = new KmlFolder((FolderOverlay)overlay, kmlDoc);
} else if (overlay instanceof MarkerClusterer){
kmlItem = new KmlFolder((MarkerClusterer)overlay, kmlDoc);
} else if (overlay instanceof Marker){
Marker marker = (Marker)overlay;
kmlItem = new KmlPlacemark(marker);
} else if (overlay instanceof Polygon){
Polygon polygon = (Polygon)overlay;
kmlItem = new KmlPlacemark(polygon, kmlDoc);
} else if (overlay instanceof Polyline){
Polyline polyline = (Polyline)overlay;
kmlItem = new KmlPlacemark(polyline, kmlDoc);
} else {
return false;
}
mItems.add(kmlItem);
return true;
}
/**
* Adds all overlays inside this, converting them in KmlFeatures.
* @param overlays list of overlays to add
* @param kmlDoc
*/
public void addOverlays(List<? extends Overlay> overlays, KmlDocument kmlDoc){
if (overlays != null){
for (Overlay item:overlays){
addOverlay(item, kmlDoc);
}
}
}
/** Add an item in the KML Folder, at the end. */
public void add(KmlFeature item){
mItems.add(item);
}
/**
* remove the item at itemPosition. No check for bad usage (itemPosition out of rank)
* @param itemPosition position of the item, starting from 0.
* @return item removed
*/
public KmlFeature removeItem(int itemPosition){
return mItems.remove(itemPosition);
}
/**
* Find a feature with a specific id in the folder.
* @param id feature id to find.
* @param recurse set to true if you want to find
* @return the feature found, or null if none has been found.
*/
public KmlFeature findFeatureId(String id, boolean recurse){
for (KmlFeature f:mItems) {
if (f.mId != null && f.mId.equals(id))
return f;
if (recurse && f instanceof KmlFolder) {
//if it's a folder, search recursively inside:
KmlFeature ff = findFeatureId(id, recurse);
if (ff != null)
return ff;
}
}
return null;
}
/**
* Build a FolderOverlay, containing (recursively) overlays from all items of this Folder.
* @param map
* @param defaultStyle to apply when an item has no Style defined.
* @param styler to apply
* @param kmlDocument for Styles
* @return the FolderOverlay built
*/
@Override public Overlay buildOverlay(MapView map, Style defaultStyle, Styler styler, KmlDocument kmlDocument){
FolderOverlay folderOverlay = new FolderOverlay();
for (KmlFeature k:mItems){
Overlay overlay = k.buildOverlay(map, defaultStyle, styler, kmlDocument);
if (overlay != null)
folderOverlay.add(overlay);
}
if (styler == null)
folderOverlay.setEnabled(mVisibility);
else
styler.onFeature(folderOverlay, this);
return folderOverlay;
}
@Override public void writeKMLSpecifics(Writer writer){
try {
if (!mOpen)
writer.write("<open>0</open>\n");
for (KmlFeature item:mItems){
item.writeAsKML(writer, false, null);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public JsonObject geoJSONNamedCRS(String crsName){
JsonObject crs = new JsonObject();
crs.addProperty("type", "name");
JsonObject properties = new JsonObject();
properties.addProperty("name", crsName);
crs.add("properties", properties);
return crs;
}
/**
* Set isRoot to true if this is the root of the final GeoJSON structure.
* Set isRoot to false if there is an enclosing FeatureCollection.
* As GeoJSON doesn't support nested FeatureCollection, sub-items will be inserted directly in the result.
* This is flattening the resulting GeoJSON hierarchy.
* @return this as a GeoJSON FeatureCollection object.
*/
@Override public JsonObject asGeoJSON(boolean isRoot){
JsonObject json = new JsonObject();
if (isRoot){
json.add("crs", geoJSONNamedCRS("urn:ogc:def:crs:OGC:1.3:CRS84"));
}
JsonArray features = new JsonArray();
for (KmlFeature item:mItems){
JsonObject subJson = item.asGeoJSON(false);
if (item instanceof KmlFolder){
//Flatten the item contents:
JsonArray subFeatures = subJson.getAsJsonArray("features");
if (subFeatures != null){
for (int i=0; i<subFeatures.size(); i++){
JsonElement j = subFeatures.get(i);
features.add(j);
}
}
} else if (subJson != null) {
features.add(subJson);
}
}
json.add("features", features);
json.addProperty("type", "FeatureCollection");
return json;
}
//Cloneable implementation ------------------------------------
public KmlFolder clone(){
KmlFolder kmlFolder = (KmlFolder)super.clone();
if (mItems != null){
kmlFolder.mItems = new ArrayList<KmlFeature>(mItems.size());
for (KmlFeature item:mItems)
kmlFolder.mItems.add(item.clone());
}
return kmlFolder;
}
//Parcelable implementation ------------
@Override public int describeContents() {
return 0;
}
@Override public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeList(mItems);
}
public static final Creator<KmlFolder> CREATOR = new Creator<KmlFolder>() {
@Override public KmlFolder createFromParcel(Parcel source) {
return new KmlFolder(source);
}
@Override public KmlFolder[] newArray(int size) {
return new KmlFolder[size];
}
};
public KmlFolder(Parcel in){
super(in);
mItems = in.readArrayList(KmlFeature.class.getClassLoader());
}
}