package com.google.maps.android.kml;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.text.Html;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.GroundOverlay;
import com.google.android.gms.maps.model.GroundOverlayOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.PolygonOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.maps.android.R;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
/**
* Renders all visible KmlPlacemark and KmlGroundOverlay objects onto the GoogleMap as Marker,
* Polyline, Polygon, GroundOverlay objects. Also removes objects from the map.
*/
/* package */ class KmlRenderer {
private static final String LOG_TAG = "KmlRenderer";
private static final int LRU_CACHE_SIZE = 50;
private final LruCache<String, Bitmap> mImagesCache;
private final ArrayList<String> mMarkerIconUrls;
private final ArrayList<String> mGroundOverlayUrls;
private GoogleMap mMap;
private HashMap<KmlPlacemark, Object> mPlacemarks;
private HashMap<String, String> mStyleMaps;
private ArrayList<KmlContainer> mContainers;
private HashMap<String, KmlStyle> mStyles;
private HashMap<String, KmlStyle> mStylesRenderer;
private HashMap<KmlGroundOverlay, GroundOverlay> mGroundOverlays;
private boolean mLayerVisible;
private boolean mMarkerIconsDownloaded;
private boolean mGroundOverlayImagesDownloaded;
private Context mContext;
/* package */ KmlRenderer(GoogleMap map, Context context) {
mContext = context;
mMap = map;
mImagesCache = new LruCache<String, Bitmap>(LRU_CACHE_SIZE);
mMarkerIconUrls = new ArrayList<String>();
mGroundOverlayUrls = new ArrayList<String>();
mStylesRenderer = new HashMap<String, KmlStyle>();
mLayerVisible = false;
mMarkerIconsDownloaded = false;
mGroundOverlayImagesDownloaded = false;
}
/**
* Gets the visibility of the placemark if it is specified. A visibility value of "1"
* corresponds as "true", a visibility value of "0" corresponds as false. If the
* visibility is not set, the method returns "true".
*
* @param placemark Placemark to obtain visibility from.
* @return False if a Placemark has a visibility value of "1", true otherwise.
*/
private static boolean getPlacemarkVisibility(KmlPlacemark placemark) {
boolean isPlacemarkVisible = true;
if (placemark.hasProperty("visibility")) {
String placemarkVisibility = placemark.getProperty("visibility");
if (Integer.parseInt(placemarkVisibility) == 0) {
isPlacemarkVisible = false;
}
}
return isPlacemarkVisible;
}
/**
* Scales a Bitmap to a specified float.
*
* @param unscaledBitmap Unscaled bitmap image to scale.
* @param scale Scale value. A "1.0" scale value corresponds to the original size of the Bitmap
* @return A scaled bitmap image
*/
private static BitmapDescriptor scaleIcon(Bitmap unscaledBitmap, Double scale) {
int width = (int) (unscaledBitmap.getWidth() * scale);
int height = (int) (unscaledBitmap.getHeight() * scale);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(unscaledBitmap, width, height, false);
return BitmapDescriptorFactory.fromBitmap(scaledBitmap);
}
/**
* Removes all given KML placemarks from the map and clears all stored placemarks.
*
* @param placemarks placemarks to remove
*/
private static void removePlacemarks(HashMap<KmlPlacemark, Object> placemarks) {
// Remove map object from the map
for (Object mapObject : placemarks.values()) {
if (mapObject instanceof Marker) {
((Marker) mapObject).remove();
} else if (mapObject instanceof Polyline) {
((Polyline) mapObject).remove();
} else if (mapObject instanceof Polygon) {
((Polygon) mapObject).remove();
}
}
}
/**
* Gets the visibility of the container
*
* @param kmlContainer container to check visibility of
* @param isParentContainerVisible true if the parent container is visible, false otherwise
* @return true if this container is visible, false otherwise
*/
/*package*/
static boolean getContainerVisibility(KmlContainer kmlContainer, boolean
isParentContainerVisible) {
boolean isChildContainerVisible = true;
if (kmlContainer.hasProperty("visibility")) {
String placemarkVisibility = kmlContainer.getProperty("visibility");
if (Integer.parseInt(placemarkVisibility) == 0) {
isChildContainerVisible = false;
}
}
return (isParentContainerVisible && isChildContainerVisible);
}
/**
* Removes all ground overlays in the given hashmap
*
* @param groundOverlays hashmap of ground overlays to remove
*/
private void removeGroundOverlays(HashMap<KmlGroundOverlay, GroundOverlay> groundOverlays) {
for (GroundOverlay groundOverlay : groundOverlays.values()) {
groundOverlay.remove();
}
}
/**
* Removes all the KML data from the map and clears all the stored placemarks of those which
* are in a container.
*/
private void removeContainers(Iterable<KmlContainer> containers) {
for (KmlContainer container : containers) {
removePlacemarks(container.getPlacemarksHashMap());
removeGroundOverlays(container.getGroundOverlayHashMap());
removeContainers(container.getContainers());
}
}
/**
* Iterates a list of styles and assigns a style
*/
/*package*/ void assignStyleMap(HashMap<String, String> styleMap,
HashMap<String, KmlStyle> styles) {
for (String styleMapKey : styleMap.keySet()) {
String styleMapValue = styleMap.get(styleMapKey);
if (styles.containsKey(styleMapValue)) {
styles.put(styleMapKey, styles.get(styleMapValue));
}
}
}
/**
* Stores all given data and adds it onto the map
*
* @param styles hashmap of styles
* @param styleMaps hashmap of style maps
* @param placemarks hashmap of placemarks
* @param folders array of containers
* @param groundOverlays hashmap of ground overlays
*/
/* package */ void storeKmlData(HashMap<String, KmlStyle> styles,
HashMap<String, String> styleMaps,
HashMap<KmlPlacemark, Object> placemarks, ArrayList<KmlContainer> folders,
HashMap<KmlGroundOverlay, GroundOverlay> groundOverlays) {
mStyles = styles;
mStyleMaps = styleMaps;
mPlacemarks = placemarks;
mContainers = folders;
mGroundOverlays = groundOverlays;
}
/* package */ void addLayerToMap() {
mStylesRenderer.putAll(mStyles);
assignStyleMap(mStyleMaps, mStylesRenderer);
addGroundOverlays(mGroundOverlays, mContainers);
addContainerGroupToMap(mContainers, true);
addPlacemarksToMap(mPlacemarks);
if (!mGroundOverlayImagesDownloaded) {
downloadGroundOverlays();
}
if (!mMarkerIconsDownloaded) {
downloadMarkerIcons();
}
mLayerVisible = true;
}
/**
* Gets the map that objects are being placed on
*
* @return map
*/
/* package */ GoogleMap getMap() {
return mMap;
}
/**
* Sets the map that objects are being placed on
*
* @param map map to place placemark, container, style and ground overlays on
*/
/* package */ void setMap(GoogleMap map) {
removeLayerFromMap();
mMap = map;
addLayerToMap();
}
/**
* Checks if the layer contains placemarks
*
* @return true if there are placemarks, false otherwise
*/
/* package */ boolean hasKmlPlacemarks() {
return mPlacemarks.size() > 0;
}
/**
* Gets an iterable of KmlPlacemark objects
*
* @return iterable of KmlPlacemark objects
*/
/* package */ Iterable<KmlPlacemark> getKmlPlacemarks() {
return mPlacemarks.keySet();
}
/**
* Checks if the layer contains any KmlContainers
*
* @return true if there is at least 1 container within the KmlLayer, false otherwise
*/
/* package */ boolean hasNestedContainers() {
return mContainers.size() > 0;
}
/**
* Gets an iterable of KmlContainerInterface objects
*
* @return iterable of KmlContainerInterface objects
*/
/* package */ Iterable<KmlContainer> getNestedContainers() {
return mContainers;
}
/**
* Gets an iterable of KmlGroundOverlay objects
*
* @return iterable of KmlGroundOverlay objects
*/
/* package */ Iterable<KmlGroundOverlay> getGroundOverlays() {
return mGroundOverlays.keySet();
}
/**
* Removes all the KML data from the map and clears all the stored placemarks
*/
/* package */ void removeLayerFromMap() {
removePlacemarks(mPlacemarks);
removeGroundOverlays(mGroundOverlays);
if (hasNestedContainers()) {
removeContainers(getNestedContainers());
}
mLayerVisible = false;
mStylesRenderer.clear();
}
/**
* Iterates over the placemarks, gets its style or assigns a default one and adds it to the map
*/
private void addPlacemarksToMap(HashMap<KmlPlacemark, Object> placemarks) {
for (KmlPlacemark kmlPlacemark : placemarks.keySet()) {
boolean isPlacemarkVisible = getPlacemarkVisibility(kmlPlacemark);
Object mapObject = addPlacemarkToMap(kmlPlacemark, isPlacemarkVisible);
// Placemark stores a KmlPlacemark as a key, and GoogleMap Object as its value
placemarks.put(kmlPlacemark, mapObject);
}
}
/**
* Combines style and visibility to apply to a placemark geometry object and adds it to the map
*
* @param placemark Placemark to obtain geometry object to add to the map
* @param placemarkVisibility boolean value, where true indicates the placemark geometry is
* shown initially on the map, false for not shown initially on the
* map.
* @return Google Map Object of the placemark geometry after it has been added to the map.
*/
private Object addPlacemarkToMap(KmlPlacemark placemark, boolean placemarkVisibility) {
//If the placemark contains a geometry, then we add it to the map
//If it doesnt contain a geometry, we do not add anything to the map and just store values
if (placemark.getGeometry() != null) {
String placemarkId = placemark.getStyleId();
KmlGeometry geometry = placemark.getGeometry();
KmlStyle style = getPlacemarkStyle(placemarkId);
KmlStyle inlineStyle = placemark.getInlineStyle();
return addToMap(placemark, geometry, style, inlineStyle, placemarkVisibility);
}
return null;
}
/**
* Adds placemarks with their corresponding styles onto the map
*
* @param kmlContainers An arraylist of folders
*/
private void addContainerGroupToMap(Iterable<KmlContainer> kmlContainers,
boolean containerVisibility) {
for (KmlContainer container : kmlContainers) {
boolean isContainerVisible = getContainerVisibility(container, containerVisibility);
if (container.getStyles() != null) {
// Stores all found styles from the container
mStylesRenderer.putAll(container.getStyles());
}
if (container.getStyleMap() != null) {
// Stores all found style maps from the container
assignStyleMap(container.getStyleMap(), mStylesRenderer);
}
addContainerObjectToMap(container, isContainerVisible);
if (container.hasContainers()) {
addContainerGroupToMap(container.getContainers(), isContainerVisible);
}
}
}
/**
* Goes through the every placemark, style and properties object within a <Folder> tag
*
* @param kmlContainer Folder to obtain placemark and styles from
*/
private void addContainerObjectToMap(KmlContainer kmlContainer, boolean isContainerVisible) {
for (KmlPlacemark placemark : kmlContainer.getPlacemarks()) {
boolean isPlacemarkVisible = getPlacemarkVisibility(placemark);
boolean isObjectVisible = isContainerVisible && isPlacemarkVisible;
Object mapObject = addPlacemarkToMap(placemark, isObjectVisible);
kmlContainer.setPlacemark(placemark, mapObject);
}
}
/**
* Obtains the styleUrl from a placemark and finds the corresponding style in a list
*
* @param styleId StyleUrl from a placemark
* @return Style which corresponds to an ID
*/
private KmlStyle getPlacemarkStyle(String styleId) {
KmlStyle style = mStylesRenderer.get(null);
if (mStylesRenderer.get(styleId) != null) {
style = mStylesRenderer.get(styleId);
}
return style;
}
/**
* Sets the marker icon if there was a url that was found
*
* @param styleUrl The style which we retrieve the icon url from
* @param markerOptions The marker which is displaying the icon
*/
private void addMarkerIcons(String styleUrl, MarkerOptions markerOptions) {
if (mImagesCache.get(styleUrl) != null) {
// Bitmap stored in cache
Bitmap bitmap = mImagesCache.get(styleUrl);
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap));
} else if (!mMarkerIconUrls.contains(styleUrl)) {
mMarkerIconUrls.add(styleUrl);
}
}
/**
* Determines if there are any icons to add to markers
*/
private void downloadMarkerIcons() {
mMarkerIconsDownloaded = true;
for (Iterator<String> iterator = mMarkerIconUrls.iterator(); iterator.hasNext(); ) {
String markerIconUrl = iterator.next();
new MarkerIconImageDownload(markerIconUrl).execute();
iterator.remove();
}
}
/**
* Adds the marker icon stored in mMarkerIconCache, to the {@link com.google.android.gms.maps.model.Marker}
*
* @param iconUrl icon url of icon to add to markers
*/
private void addIconToMarkers(String iconUrl, HashMap<KmlPlacemark, Object> placemarks) {
for (KmlPlacemark placemark : placemarks.keySet()) {
KmlStyle urlStyle = mStylesRenderer.get(placemark.getStyleId());
KmlStyle inlineStyle = placemark.getInlineStyle();
if ("Point".equals(placemark.getGeometry().getGeometryType())) {
boolean isInlineStyleIcon = inlineStyle != null && iconUrl
.equals(inlineStyle.getIconUrl());
boolean isPlacemarkStyleIcon = urlStyle != null && iconUrl
.equals(urlStyle.getIconUrl());
if (isInlineStyleIcon) {
scaleBitmap(inlineStyle, placemarks, placemark);
} else if (isPlacemarkStyleIcon) {
scaleBitmap(urlStyle, placemarks, placemark);
}
}
}
}
/**
* Enlarges or shrinks a bitmap image based on the scale provided
* @param style Style to retrieve iconUrl and scale from
* @param placemark Placemark object to set the image to
*/
private void scaleBitmap(KmlStyle style, HashMap<KmlPlacemark, Object> placemarks,
KmlPlacemark placemark) {
double bitmapScale = style.getIconScale();
String bitmapUrl = style.getIconUrl();
Bitmap bitmapImage = mImagesCache.get(bitmapUrl);
BitmapDescriptor scaledBitmap = scaleIcon(bitmapImage, bitmapScale);
((Marker) placemarks.get(placemark)).setIcon(scaledBitmap);
}
/**
* Assigns icons to markers with a url if put in a placemark tag that is nested in a folder.
*
* @param iconUrl url to obtain marker image
* @param kmlContainers kml container which contains the marker
*/
private void addContainerGroupIconsToMarkers(String iconUrl,
Iterable<KmlContainer> kmlContainers) {
for (KmlContainer container : kmlContainers) {
addIconToMarkers(iconUrl, container.getPlacemarksHashMap());
if (container.hasContainers()) {
addContainerGroupIconsToMarkers(iconUrl, container.getContainers());
}
}
}
/**
* Adds a single geometry object to the map with its specified style
*
* @param geometry defines the type of object to add to the map
* @param style defines styling properties to add to the object when added to the map
* @return the object that was added to the map, this is a Marker, Polyline, Polygon or an array
* of either objects
*/
private Object addToMap(KmlPlacemark placemark, KmlGeometry geometry, KmlStyle style,
KmlStyle inlineStyle, boolean isVisible) {
String geometryType = geometry.getGeometryType();
if (geometryType.equals("Point")) {
Marker marker = addPointToMap(placemark, (KmlPoint) geometry, style, inlineStyle);
marker.setVisible(isVisible);
return marker;
} else if (geometryType.equals("LineString")) {
Polyline polyline = addLineStringToMap((KmlLineString) geometry, style, inlineStyle);
polyline.setVisible(isVisible);
return polyline;
} else if (geometryType.equals("Polygon")) {
Polygon polygon = addPolygonToMap((KmlPolygon) geometry, style, inlineStyle);
polygon.setVisible(isVisible);
return polygon;
} else if (geometryType.equals("MultiGeometry")) {
return addMultiGeometryToMap(placemark, (KmlMultiGeometry) geometry, style, inlineStyle,
isVisible);
}
return null;
}
/**
* Adds a KML Point to the map as a Marker by combining the styling and coordinates
*
* @param point contains coordinates for the Marker
* @param style contains relevant styling properties for the Marker
* @return Marker object
*/
private Marker addPointToMap(KmlPlacemark placemark, KmlPoint point, KmlStyle style,
KmlStyle markerInlineStyle) {
MarkerOptions markerUrlStyle = style.getMarkerOptions();
markerUrlStyle.position(point.getGeometryObject());
if (markerInlineStyle != null) {
setInlinePointStyle(markerUrlStyle, markerInlineStyle, style.getIconUrl());
} else if (style.getIconUrl() != null) {
// Use shared style
addMarkerIcons(style.getIconUrl(), markerUrlStyle);
}
Marker marker = mMap.addMarker(markerUrlStyle);
setMarkerInfoWindow(style, marker, placemark);
return marker;
}
/**
* Sets a marker info window if no <text> tag was found in the KML document. This method sets
* the marker title as the text found in the <name> start tag and the snippet as <description>
*
* @param style Style to apply
*/
private void setMarkerInfoWindow(KmlStyle style, Marker marker,
final KmlPlacemark placemark) {
boolean hasName = placemark.hasProperty("name");
boolean hasDescription = placemark.hasProperty("description");
boolean hasBalloonOptions = style.hasBalloonStyle();
boolean hasBalloonText = style.getBalloonOptions().containsKey("text");
if (hasBalloonOptions && hasBalloonText) {
marker.setTitle(style.getBalloonOptions().get("text"));
createInfoWindow();
} else if (hasBalloonOptions && hasName) {
marker.setTitle(placemark.getProperty("name"));
createInfoWindow();
} else if (hasName && hasDescription) {
marker.setTitle(placemark.getProperty("name"));
marker.setSnippet(placemark.getProperty("description"));
createInfoWindow();
} else if (hasDescription) {
marker.setTitle(placemark.getProperty("description"));
createInfoWindow();
}
}
/**
* Creates a new InfoWindowAdapter and sets text if marker snippet or title is set. This allows
* the info window to have custom HTML.
*/
private void createInfoWindow() {
mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
public View getInfoWindow(Marker arg0) {
return null;
}
public View getInfoContents(Marker arg0) {
View view = LayoutInflater.from(mContext).inflate(R.layout.amu_info_window, null);
TextView infoWindowText = (TextView) view.findViewById(R.id.window);
if (arg0.getSnippet() != null) {
infoWindowText.setText(Html.fromHtml(arg0.getTitle() + "<br>" + arg0.getSnippet()));
} else {
infoWindowText.setText(Html.fromHtml(arg0.getTitle()));
}
return view;
}
});
}
/**
* Sets the inline point style by copying over the styles that have been set
*
* @param markerOptions marker options object to add inline styles to
* @param inlineStyle inline styles to apply
* @param markerUrlIconUrl default marker icon URL from shared style
*/
private void setInlinePointStyle(MarkerOptions markerOptions, KmlStyle inlineStyle,
String markerUrlIconUrl) {
MarkerOptions inlineMarkerOptions = inlineStyle.getMarkerOptions();
if (inlineStyle.isStyleSet("heading")) {
markerOptions.rotation(inlineMarkerOptions.getRotation());
}
if (inlineStyle.isStyleSet("hotSpot")) {
markerOptions
.anchor(inlineMarkerOptions.getAnchorU(), inlineMarkerOptions.getAnchorV());
}
if (inlineStyle.isStyleSet("markerColor")) {
markerOptions.icon(inlineMarkerOptions.getIcon());
}
if (inlineStyle.isStyleSet("iconUrl")) {
addMarkerIcons(inlineStyle.getIconUrl(), markerOptions);
} else if (markerUrlIconUrl != null) {
// Inline style with no icon defined
addMarkerIcons(markerUrlIconUrl, markerOptions);
}
}
/**
* Adds a KML LineString to the map as a Polyline by combining the styling and coordinates
*
* @param lineString contains coordinates for the Polyline
* @param style contains relevant styling properties for the Polyline
* @return Polyline object
*/
private Polyline addLineStringToMap(KmlLineString lineString, KmlStyle style,
KmlStyle inlineStyle) {
PolylineOptions polylineOptions = style.getPolylineOptions();
polylineOptions.addAll(lineString.getGeometryObject());
if (inlineStyle != null) {
setInlineLineStringStyle(polylineOptions, inlineStyle);
} else if (style.isLineRandomColorMode()) {
polylineOptions.color(KmlStyle.computeRandomColor(polylineOptions.getColor()));
}
return mMap.addPolyline(polylineOptions);
}
/**
* Sets the inline linestring style by copying over the styles that have been set
*
* @param polylineOptions polygon options object to add inline styles to
* @param inlineStyle inline styles to apply
*/
private void setInlineLineStringStyle(PolylineOptions polylineOptions, KmlStyle inlineStyle) {
PolylineOptions inlinePolylineOptions = inlineStyle.getPolylineOptions();
if (inlineStyle.isStyleSet("outlineColor")) {
polylineOptions.color(inlinePolylineOptions.getColor());
}
if (inlineStyle.isStyleSet("width")) {
polylineOptions.width(inlinePolylineOptions.getWidth());
}
if (inlineStyle.isLineRandomColorMode()) {
polylineOptions.color(KmlStyle.computeRandomColor(inlinePolylineOptions.getColor()));
}
}
/**
* Adds a KML Polygon to the map as a Polygon by combining the styling and coordinates
*
* @param polygon contains coordinates for the Polygon
* @param style contains relevant styling properties for the Polygon
* @return Polygon object
*/
private Polygon addPolygonToMap(KmlPolygon polygon, KmlStyle style, KmlStyle inlineStyle) {
PolygonOptions polygonOptions = style.getPolygonOptions();
polygonOptions.addAll(polygon.getOuterBoundaryCoordinates());
for (ArrayList<LatLng> innerBoundary : polygon.getInnerBoundaryCoordinates()) {
polygonOptions.addHole(innerBoundary);
}
if (inlineStyle != null) {
setInlinePolygonStyle(polygonOptions, inlineStyle);
} else if (style.isPolyRandomColorMode()) {
polygonOptions.fillColor(KmlStyle.computeRandomColor(polygonOptions.getFillColor()));
}
return mMap.addPolygon(polygonOptions);
}
/**
* Sets the inline polygon style by copying over the styles that have been set
*
* @param polygonOptions polygon options object to add inline styles to
* @param inlineStyle inline styles to apply
*/
private void setInlinePolygonStyle(PolygonOptions polygonOptions, KmlStyle inlineStyle) {
PolygonOptions inlinePolygonOptions = inlineStyle.getPolygonOptions();
if (inlineStyle.hasFill() && inlineStyle.isStyleSet("fillColor")) {
polygonOptions.fillColor(inlinePolygonOptions.getFillColor());
}
if (inlineStyle.hasOutline()) {
if (inlineStyle.isStyleSet("outlineColor")) {
polygonOptions.strokeColor(inlinePolygonOptions.getStrokeColor());
}
if (inlineStyle.isStyleSet("width")) {
polygonOptions.strokeWidth(inlinePolygonOptions.getStrokeWidth());
}
}
if (inlineStyle.isPolyRandomColorMode()) {
polygonOptions.fillColor(KmlStyle.computeRandomColor(inlinePolygonOptions.getFillColor()));
}
}
/**
* Adds all the geometries within a KML MultiGeometry to the map. Supports recursive
* MultiGeometry. Combines styling of the placemark with the coordinates of each geometry.
*
* @param multiGeometry contains array of geometries for the MultiGeometry
* @param urlStyle contains relevant styling properties for the MultiGeometry
* @return array of Marker, Polyline and Polygon objects
*/
private ArrayList<Object> addMultiGeometryToMap(KmlPlacemark placemark,
KmlMultiGeometry multiGeometry, KmlStyle urlStyle, KmlStyle inlineStyle,
boolean isContainerVisible) {
ArrayList<Object> mapObjects = new ArrayList<Object>();
ArrayList<KmlGeometry> kmlObjects = multiGeometry.getGeometryObject();
for (KmlGeometry kmlGeometry : kmlObjects) {
mapObjects.add(addToMap(placemark, kmlGeometry, urlStyle, inlineStyle,
isContainerVisible));
}
return mapObjects;
}
/**
* Adds a ground overlay adds all the ground overlays onto the map and recursively adds all
* ground overlays stored in the given containers
*
* @param groundOverlays ground overlays to add to the map
* @param kmlContainers containers to check for ground overlays
*/
private void addGroundOverlays(HashMap<KmlGroundOverlay, GroundOverlay> groundOverlays,
Iterable<KmlContainer> kmlContainers) {
addGroundOverlays(groundOverlays);
for (KmlContainer container : kmlContainers) {
addGroundOverlays(container.getGroundOverlayHashMap(),
container.getContainers());
}
}
/**
* Adds all given ground overlays to the map
*
* @param groundOverlays hashmap of ground overlays to add to the map
*/
private void addGroundOverlays(HashMap<KmlGroundOverlay, GroundOverlay> groundOverlays) {
for (KmlGroundOverlay groundOverlay : groundOverlays.keySet()) {
String groundOverlayUrl = groundOverlay.getImageUrl();
if (groundOverlayUrl != null && groundOverlay.getLatLngBox() != null) {
// Can't draw overlay if url and coordinates are missing
if (mImagesCache.get(groundOverlayUrl) != null) {
addGroundOverlayToMap(groundOverlayUrl, mGroundOverlays, true);
} else if (!mGroundOverlayUrls.contains(groundOverlayUrl)) {
mGroundOverlayUrls.add(groundOverlayUrl);
}
}
}
}
/**
* Downloads images of all ground overlays
*/
private void downloadGroundOverlays() {
mGroundOverlayImagesDownloaded = true;
for (Iterator<String> iterator = mGroundOverlayUrls.iterator(); iterator.hasNext(); ) {
String groundOverlayUrl = iterator.next();
new GroundOverlayImageDownload(groundOverlayUrl).execute();
iterator.remove();
}
}
/**
* Adds ground overlays from a given URL onto the map
*
* @param groundOverlayUrl url of ground overlay
* @param groundOverlays hashmap of ground overlays to add to the map
*/
private void addGroundOverlayToMap(String groundOverlayUrl,
HashMap<KmlGroundOverlay, GroundOverlay> groundOverlays, boolean containerVisibility) {
BitmapDescriptor groundOverlayBitmap = BitmapDescriptorFactory
.fromBitmap(mImagesCache.get(groundOverlayUrl));
for (KmlGroundOverlay kmlGroundOverlay : groundOverlays.keySet()) {
if (kmlGroundOverlay.getImageUrl().equals(groundOverlayUrl)) {
GroundOverlayOptions groundOverlayOptions = kmlGroundOverlay.getGroundOverlayOptions()
.image(groundOverlayBitmap);
GroundOverlay mapGroundOverlay = mMap.addGroundOverlay(groundOverlayOptions);
if (containerVisibility == false) {
mapGroundOverlay.setVisible(false);
}
groundOverlays.put(kmlGroundOverlay, mapGroundOverlay);
}
}
}
/**
* Adds ground overlays in containers from a given URL onto the map
*
* @param groundOverlayUrl url of ground overlay
* @param kmlContainers containers containing ground overlays to add to the map
*/
private void addGroundOverlayInContainerGroups(String groundOverlayUrl,
Iterable<KmlContainer> kmlContainers, boolean containerVisibility) {
for (KmlContainer container : kmlContainers) {
boolean isContainerVisible = getContainerVisibility(container, containerVisibility);
addGroundOverlayToMap(groundOverlayUrl, container.getGroundOverlayHashMap(), isContainerVisible);
if (container.hasContainers()) {
addGroundOverlayInContainerGroups(groundOverlayUrl,
container.getContainers(), isContainerVisible);
}
}
}
/**
* Downloads images for use as marker icons
*/
private class MarkerIconImageDownload extends AsyncTask<String, Void, Bitmap> {
private final String mIconUrl;
/**
* Creates a new IconImageDownload object
*
* @param iconUrl URL of the marker icon to download
*/
public MarkerIconImageDownload(String iconUrl) {
mIconUrl = iconUrl;
}
/**
* Downloads the marker icon in another thread
*
* @param params String varargs not used
* @return Bitmap object downloaded
*/
@Override
protected Bitmap doInBackground(String... params) {
try {
return BitmapFactory.decodeStream((InputStream) new URL(mIconUrl).getContent());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Adds the bitmap to the cache and adds the bitmap to the markers
*
* @param bitmap bitmap downloaded
*/
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap == null) {
Log.e(LOG_TAG, "Image at this URL could not be found " + mIconUrl);
} else {
mImagesCache.put(mIconUrl, bitmap);
if (mLayerVisible) {
addIconToMarkers(mIconUrl, mPlacemarks);
addContainerGroupIconsToMarkers(mIconUrl, mContainers);
}
}
}
}
/**
* Downloads images for use as ground overlays
*/
private class GroundOverlayImageDownload extends AsyncTask<String, Void, Bitmap> {
private final String mGroundOverlayUrl;
public GroundOverlayImageDownload(String groundOverlayUrl) {
mGroundOverlayUrl = groundOverlayUrl;
}
/**
* Downloads the ground overlay image in another thread
*
* @param params String varargs not used
* @return Bitmap object downloaded
*/
@Override
protected Bitmap doInBackground(String... params) {
try {
return BitmapFactory
.decodeStream((InputStream) new URL(mGroundOverlayUrl).getContent());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Adds the bitmap to the ground overlay and places it on a map
*
* @param bitmap bitmap downloaded
*/
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap == null) {
Log.e(LOG_TAG, "Image at this URL could not be found " + mGroundOverlayUrl);
} else {
mImagesCache.put(mGroundOverlayUrl, bitmap);
if (mLayerVisible) {
addGroundOverlayToMap(mGroundOverlayUrl, mGroundOverlays, true);
addGroundOverlayInContainerGroups(mGroundOverlayUrl, mContainers, true);
}
}
}
}
}