-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: KMZ support #625
feat: KMZ support #625
Changes from all commits
186195f
8e2011a
6b25de1
731334f
6865f13
f7c71f2
30a8940
fcfb833
7550b61
e25546e
ddba065
1977299
832954f
ec4280d
07aabf5
19cb117
084c0ca
e9d8f24
f99a787
f9bd8ba
4fccc05
06de5c7
482c748
3d1d0e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,10 @@ | |
*/ | ||
package com.google.maps.android.data.kml; | ||
|
||
import android.graphics.Bitmap; | ||
import android.graphics.BitmapFactory; | ||
import android.util.Log; | ||
|
||
import androidx.fragment.app.FragmentActivity; | ||
|
||
import com.google.android.gms.maps.GoogleMap; | ||
|
@@ -28,8 +32,12 @@ | |
import org.xmlpull.v1.XmlPullParserException; | ||
import org.xmlpull.v1.XmlPullParserFactory; | ||
|
||
import java.io.BufferedInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.HashMap; | ||
import java.util.zip.ZipEntry; | ||
import java.util.zip.ZipInputStream; | ||
|
||
/** | ||
* Document class allows for users to input their KML data and output it onto the map | ||
|
@@ -39,8 +47,10 @@ public class KmlLayer extends Layer { | |
/** | ||
* Creates a new KmlLayer object - addLayerToMap() must be called to trigger rendering onto a map. | ||
* | ||
* Constructor may be called on a background thread, as I/O and parsing may be long-running. | ||
* | ||
* @param map GoogleMap object | ||
* @param resourceId Raw resource KML file | ||
* @param resourceId Raw resource KML or KMZ file | ||
* @param activity Activity object | ||
* @throws XmlPullParserException if file cannot be parsed | ||
* @throws IOException if I/O error | ||
|
@@ -53,8 +63,10 @@ public KmlLayer(GoogleMap map, int resourceId, FragmentActivity activity) | |
/** | ||
* Creates a new KmlLayer object - addLayerToMap() must be called to trigger rendering onto a map. | ||
* | ||
* Constructor may be called on a background thread, as I/O and parsing may be long-running. | ||
* | ||
* @param map GoogleMap object | ||
* @param stream InputStream containing KML file | ||
* @param stream InputStream containing KML or KMZ file | ||
* @param activity Activity object | ||
* @throws XmlPullParserException if file cannot be parsed | ||
* @throws IOException if I/O error | ||
|
@@ -67,11 +79,13 @@ public KmlLayer(GoogleMap map, InputStream stream, FragmentActivity activity) | |
/** | ||
* Creates a new KmlLayer object - addLayerToMap() must be called to trigger rendering onto a map. | ||
* | ||
* Constructor may be called on a background thread, as I/O and parsing may be long-running. | ||
* | ||
* Use this constructor with shared object managers in order to handle multiple layers with | ||
* their own event handlers on the map. | ||
* | ||
* @param map GoogleMap object | ||
* @param resourceId Raw resource KML file | ||
* @param resourceId Raw resource KML or KMZ file | ||
* @param activity Activity object | ||
* @param markerManager marker manager to create marker collection from | ||
* @param polygonManager polygon manager to create polygon collection from | ||
|
@@ -88,11 +102,13 @@ public KmlLayer(GoogleMap map, int resourceId, FragmentActivity activity, Marker | |
/** | ||
* Creates a new KmlLayer object - addLayerToMap() must be called to trigger rendering onto a map. | ||
* | ||
* Constructor may be called on a background thread, as I/O and parsing may be long-running. | ||
* | ||
* Use this constructor with shared object managers in order to handle multiple layers with | ||
* their own event handlers on the map. | ||
* | ||
* @param map GoogleMap object | ||
* @param stream InputStream containing KML file | ||
* @param stream InputStream containing KML or KMZ file | ||
* @param activity Activity object | ||
* @param markerManager marker manager to create marker collection from | ||
* @param polygonManager polygon manager to create polygon collection from | ||
|
@@ -106,14 +122,53 @@ public KmlLayer(GoogleMap map, InputStream stream, FragmentActivity activity, Ma | |
if (stream == null) { | ||
throw new IllegalArgumentException("KML InputStream cannot be null"); | ||
} | ||
KmlRenderer mRenderer = new KmlRenderer(map, activity, markerManager, polygonManager, polylineManager, groundOverlayManager); | ||
KmlRenderer renderer = new KmlRenderer(map, activity, markerManager, polygonManager, polylineManager, groundOverlayManager); | ||
|
||
BufferedInputStream bis = new BufferedInputStream(stream); | ||
bis.mark(1024); | ||
ZipInputStream zip = new ZipInputStream(bis); | ||
try { | ||
KmlParser parser = null; | ||
ZipEntry entry = zip.getNextEntry(); | ||
if (entry != null) { // is a KMZ zip file | ||
HashMap<String, Bitmap> images = new HashMap<>(); | ||
while (entry != null) { | ||
if (parser == null && entry.getName().toLowerCase().endsWith(".kml")) { | ||
parser = parseKml(zip); | ||
} else { | ||
Bitmap bitmap = BitmapFactory.decodeStream(zip); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since other file types might also be used (e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this handle subdirectories?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, subdirectories are handled. The name includes the full file path within the zip archive. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added warning log: 3d1d0e7 |
||
if (bitmap != null) { | ||
images.put(entry.getName(), bitmap); | ||
} else { | ||
Log.w("KmlLayer", "Unsupported KMZ contents file type: " + entry.getName()); | ||
} | ||
} | ||
entry = zip.getNextEntry(); | ||
} | ||
if (parser == null) { | ||
throw new IllegalArgumentException("KML not found in InputStream"); | ||
} | ||
renderer.storeKmzData(parser.getStyles(), parser.getStyleMaps(), parser.getPlacemarks(), | ||
parser.getContainers(), parser.getGroundOverlays(), images); | ||
} else { // is a KML | ||
bis.reset(); | ||
parser = parseKml(bis); | ||
renderer.storeKmlData(parser.getStyles(), parser.getStyleMaps(), parser.getPlacemarks(), | ||
parser.getContainers(), parser.getGroundOverlays()); | ||
} | ||
storeRenderer(renderer); | ||
} finally { | ||
stream.close(); | ||
bis.close(); | ||
zip.close(); | ||
} | ||
} | ||
|
||
private static KmlParser parseKml(InputStream stream) throws XmlPullParserException, IOException { | ||
XmlPullParser xmlPullParser = createXmlParser(stream); | ||
KmlParser parser = new KmlParser(xmlPullParser); | ||
parser.parseKml(); | ||
stream.close(); | ||
mRenderer.storeKmlData(parser.getStyles(), parser.getStyleMaps(), parser.getPlacemarks(), | ||
parser.getContainers(), parser.getGroundOverlays()); | ||
storeRenderer(mRenderer); | ||
return parser; | ||
} | ||
|
||
/** | ||
|
@@ -132,7 +187,7 @@ private static XmlPullParser createXmlParser(InputStream stream) throws XmlPullP | |
} | ||
|
||
/** | ||
* Adds the KML data to the map | ||
* Adds the KML data to the map - must be called on the main UI thread | ||
*/ | ||
@Override | ||
public void addLayerToMap() { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be simpler if another constructor was created, with the following signature, to handle the KMZ files explicitly:
This way, it's left to the caller to use the correct constructor for their use-case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about that. Since we can differentiate between the two types with the logic I implemented, I thought a single universal constructor was simpler for the caller to use. Also, since there are two constructors currently, the other supports opening a KML by resource ID, how could we implement opening a KMZ by resource ID? We'd have to imply the resource ID is either a KML or KMZ or we'd need the same logic to differentiate between them and call the appropriate stream constructor anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point about invoking by resource ID. Let's stick to this implementation.