-
Notifications
You must be signed in to change notification settings - Fork 205
Tutorial_4
(we assume you already followed Tutorial_1 and Tutorial_2)
KML is a de-facto standard for handling overlays on maps, from a simple list of markers, to complex structures.
Both Google Maps and Bing Maps allow people to create their "personal maps" by putting markers, lines and polygons. Such personal maps can be exported as KML content. More advanced tools can be used to create and display KML content, like Google Earth or ArcGIS Explorer.
OSMBonusPack provides the basic toolbox to handle KML content: read (from a url or a local file), save locally, display, and edit.
Let see it in action. I have a Url to a KML file, and I want to show it on my OSM map.
(Parenthesis - If you don't have such Url, pick one of those:
- Paris Tour: http://mapsengine.google.com/map/kml?forcekml=1&mid=z6IJfj90QEd4.kUUY9FoHFRdE
- Chicago Transit map (a sample from Google Maps JavaScript API v3 doc): http://mapsengine.google.com/map/kml?forcekml=1&mid=12phHnQP7CcPH07FaT9p1YyaNvCE
- a message in the desert: http://mapsengine.google.com/map/kml?forcekml=1&mid=z6IJfj90QEd4.kcfEKhi8r5LQ
- Or a guided visit of the "Puy du Fou Grand Parc" - a famous Theme Park in France: http://mapsengine.google.com/map/kml?forcekml=1&mid=z6IJfj90QEd4.kABxpiwxshvA
End of parenthesis)
First of all, you need a KmlDocument:
KmlDocument kmlDocument = new KmlDocument();
Then you can use it to read and parse your KML content:
kmlDocument.parseUrl(url);
This KmlDocument now contains the whole KML Document. In particular, it contains a KmlFolder: mKmlRoot, which is the entry point to the KML hierarchy. Don't hesitate to have a look at KML Wiki page: you will need to understand the KML classes if you want to go further and manipulate your KML structure.
But for now, we mainly wish to see the graphics on the screen.
So we build the overlays:
FolderOverlay kmlOverlay = (FolderOverlay)kmlDocument.mKmlRoot.buildOverlay(map, null, null, kmlDocument);
Yes, this is doing the job. This FolderOverlay will be the container for all overlays that will be found in the KML structure:
- KML Point, handled as an OSMBonusPack Marker
- KML LineString, handled as OSMBonusPack Polyline
- KML Polygon, handled as OSMBonusPack Polygon
- and KML Folder, handled as OSMBonusPack FolderOverlay, allowing to keep the hierarchical organisation of the KML structure.
Now, add this folder overlay on the map:
map.getOverlays().add(kmlOverlay);
And don't forget to redraw your map view:
map.invalidate();
Now give this a try.
You see nothing? Try to move your map at the right place. Or set automatically the view on the whole stuff, using its bounding box:
BoundingBoxE6 bb = kmlDocument.mKmlRoot.getBoundingBox()
map.zoomToBoundingBox(bb);
Well, in fact, there is a known issue in osmdroid: you cannot call map.zoomToBoundingBox() in onCreate. What you can do is centering this way:
map.getController().setCenter(bb.getCenter());
The result when loading "Paris Tour" URL:
In chapter 12, you used KML samples including styles, and the overlays were reflecting these styles. This is not always the case. You may have content without style. Or you may want to get rid of defined styles and apply your own strategy.
Example: YOURS service produces routes in KML format. Here is a sample: http://www.yournavigation.org/api/1.0/gosmore.php?format=kml&flat=52.215676&flon=5.963946&tlat=52.2573&tlon=6.1799'>http://www.yournavigation.org/api/1.0/gosmore.php?format=kml&flat=52.215676&flon=5.963946&tlat=52.2573&tlon=6.1799
This KML contains no style at all, and all "visible" elements are set to false.
So, what can we do?
First level of design customization is to specify a default style.
You can use the simplified Style constructor where you just specify
- a default Marker icon,
- a default line color and line width,
- and a default fill color
Drawable defaultMarker = getResources().getDrawable(R.drawable.marker_kml_point);
Bitmap defaultBitmap = ((BitmapDrawable)defaultMarker).getBitmap();
Style defaultStyle = new Style(defaultBitmap, 0x901010AA, 3.0f, 0x20AA1010);
Then you specify this default style when building the overlays:
FolderOverlay kmlOverlay = (FolderOverlay)mKmlDocument.mKmlRoot.buildOverlay(map, defaultStyle, null, mKmlDocument);
Everytime a Placemark has no style defined, the default style will be used to build its Overlay.
If you need more advanced mechanisms, then you can specify a "Styler". This is an interface providing methods to be applied to each kind of KML Feature or Geometry during the Overlays building.
First step is to implement your Styler:
class MyKmlStyler implements KmlFeature.Styler {
}
Then you write the methods. Here is a LineString styler setting the width of the line according to its number of points:
@Override public void onLineString(Polyline polyline, KmlPlacemark kmlPlacemark, KmlLineString kmlLineString){
polyline.setWidth(Math.max(kmlLineString.mCoordinates.size()/200.0f, 3.0f));
polyline.setColor(Color.GREEN);
}
If you have nothing to do, do nothing:
@Override public void onFeature(Overlay overlay, KmlFeature kmlFeature) {
}
Now you can use this styler when building overlays, this way:
KmlFeature.Styler styler = new MyKmlStyler();
FolderOverlay kmlOverlay = (FolderOverlay)mKmlDocument.mKmlRoot.buildOverlay(map, null, styler, mKmlDocument);
It's important to understand the styling strategy:
- If there is a styler, it is called, and NO other styling is applied.
- Else, if there is a style defined in the KML Feature, this style is applied.
- Else, if there is a default style specified, it is applied.
- Else, hard-coded default styling (quite ugly) is applied.
It may happen that you would like to both use a Styler, AND still have access to standard styling. In this case, in the Styler methods, you can still call #applyDefaultStyling, which is available in most KML classes.
In our example, if we have no particular style specificities for Points, we can define onPoint this way:
@Override public void onPoint(Marker marker, KmlPlacemark kmlPlacemark, KmlPoint kmlPoint) {
kmlPoint.applyDefaultStyling(marker, mDefaultStyle, kmlPlacemark, mKmlDocument, map);
}
As you will notice, you will have to set some objects as class members (the default style, the KmlDocument).
Note that you can also create Styles programmatically inside the KML structure:
Style panda_area = new Style(pandaBitmap, 0x901010AA, 3.0f, 0x2010AA10);
mKmlDocument.putStyle("panda_area", panda_area);
And you can assign a Style to KML features. For instance this way:
@Override public void onPoint(Marker marker, KmlPlacemark kmlPlacemark, KmlPoint kmlPoint) {
if ("panda_area".equals(kmlPlacemark.getExtendedData("category")))
kmlPlacemark.mStyle = "panda_area";
kmlPoint.applyDefaultStyling(marker, mDefaultStyle, kmlPlacemark, mKmlDocument, map);
}
You can "grab" an overlay and add it in an existing KML folder. For example, we can grab the route shape overlay we built in chapter 1:
kmlDocument.mKmlRoot.addOverlay(roadOverlay, kmlDocument);
This is working for Polyline overlays, but also for Markers, FolderOverlays and MarkerClusterers. If you put your road node markers in a roadNodes FolderOverlay (as we did for POI Markers in chapter 5), you can grab all of them once:
kmlDocument.mKmlRoot.addOverlay(roadNodes, kmlDocument);
And we can save the final result locally, in a KML file:
File localFile = kmlDocument.getDefaultPathForAndroid("my_route.kml");
kmlDocument.saveAsKML(localFile);
The default path for KML files is in the external storage, in a "kml" directory. Now, you have a "my_route.kml" file, containing the whole KML content loaded from a url in chapter 12, plus the route shape and route nodes of chapter 1.
GeoJSON is more or less the JSON equivalent to KML. Very easy to use in a HTML/JavaScript context, it rapidly reached a large audience.
All KML objects support loading of GeoJSON content, and saving locally in GeoJSON format.
Let save in GeoJSON the KML structure built in chapters 12 and 13:
File localFile = kmlDocument.getDefaultPathForAndroid("my_route.json");
kmlDocument.saveAsGeoJSON(localFile);
You can refer to GeoJSON Wiki page for more details.
Note that GeoJSON format doesn't provide styling attributes. So the chapter 13 above about styling will be very useful if you want to display GeoJSON content.
See you soon on Tutorial_5, to discover Map events handling, Polygons and GroundOverlays.