HERE Maps API - Converting from JavaScript to KML
KML has emerged as the de facto standard for online mapping data. This article explains how to convert an existing JavaScript-based Map into a KML-based solution and details the likely benefits to be gained by implementing such a solution
Contents |
Introduction : What is KML
Keyhole Markup Language (KML) is an XML notation for geographic applications. It allows both two-dimensional maps and three-dimensional maps to display additional information overlaying the basic map. The current version of the Nokia Maps API for JavaScript is KML compliant and is able to parse KML files. By separating the geographical application data (the model) from the method of rendering that data on the map (the view), the same KML data may be used be multiple applications. For example, most major search engines, such as Google, Bing and Ask are able to read KML files directly and return the map in their search results. Also since KML is a de facto standard, mapping libraries are optimised to use it, therefore converting from a Home-brew JavaScript solution to a KML solution should result in a faster rendering performance for basic map data.
What aspects of KML are relevant for two-dimensional maps?
KML was originally developed for three-dimensional Earth viewers, therefore only a subset of KML is strictly relevant to two-dimensional maps. Fortunately this also covers a high percentage of 2d-mapping use cases.
KML is able to:
- Specify icons and labels to identify locations.
- Specify additional information (e.g. address, phone number) to associate with a specific location.
- Define other elements such as polygons and polylines and attach them to the map.
- Define styles to specify feature appearance.
- Write HTML descriptions of features, including hyperlinks and embedded images
- Use folders for hierarchical grouping of features
Comparison between Nokia Map elements and the KML syntax
Map Marker
A MapMarker is a KML <Placemark> with a <Point> geometry and an <IconStyle>
| Nokia Map | KML Syntax |
|---|---|
marker = new nokia.maps.map.Marker( |
<Placemark> |
As can be seen, there is a one-to-one correspondence between the Marker attributes and the KML <Point> attributes. The coordinates of the marker are specified in the <coordinates> tag , although longitude and latitude are reversed, and altitude is added. Since altitude is irrelevant for a two-dimensional map, a placeholder of zero can be used. The icon of the marker is defined by the <href> tag. The anchor of the marker is specified in the <hotSpot> tag, with the Point offsets held in the x and y attributes. Note that <hotSpot> can be defined in three different ways, correspond directly,the xunit atttibute and the yunit attribute must be set to "pixels"
Map Standard Marker
A Map StandardMarker can be defined as KML <Placemark> with a <Point> geometry and a <styleUrl>
| Nokia Map | KML Syntax |
|---|---|
<script type="text/javascript" |
<Style id='FF0000'> |
KML does not have the concept of a StandardMarker. If a <Point> is defined without styling, it is up to the rendering library to decide what icon to use on the marker. Obviously if Nokia Maps is used, the <Point> will default to the Nokia StandardMarker. Nokia's StandardMarker has a brush attribute which changes the color of the marker. This cannot be replicated directly in KML. However, it is possible to approximate this using a KML <Style>.
Firstly we need to create custom icons for the various default RGB colors:
| Icon | Color | Style Id | URL |
|---|---|---|---|
| |
Black | 000000 | http://www.developer.nokia.com/Community/Wiki/images/0/02/000000.png |
| |
Blue | 0000FF | http://www.developer.nokia.com/Community/Wiki/images/e/e5/0000FF.png |
| |
Red | FF0000 | http://www.developer.nokia.com/Community/Wiki/images/9/99/FF0000.png |
| |
Yellow | FFFF00 | http://www.developer.nokia.com/Community/Wiki/images/6/68/FFFF00.png |
| |
White | 000000 | http://www.developer.nokia.com/Community/Wiki/images/d/de/FFFFFF.png |
Thereafter, we can use the <styleURL> element of the <PlaceMark> to render the <Point> using the icon defined in the <Style> Marker.
InfoBubble
An Infobubble can be defined as KML <Placemark> with a <description> tag
| Nokia Map | KML Syntax |
|---|---|
function addInfoBubble(infoBubbles, marker, html) { <script type="text/javascript" |
<Placemark> |
The <description> tag is defined as being "User-supplied content that appears in the description balloon". As such it is analogous to the Nokia Map Infobubble. The usual way to create an InfoBubble is to associate additional data with the marker (in this case by adding an .html attribute) and then displaying the info bubble by adding an onclick event.
Many KML feeds make the assumption that the consumer of the KML file will render HTML, and therefore place HTML tags directly in the description - this could be avoided by using KML placeholders and creating a <BalloonStyle>, but this is beyond the scope of this article.
Polyline
A Polyline can be defined as KML <Placemark> with a <LineString> geometry and a <LineStyle>
| Nokia Map | KML Syntax |
|---|---|
var roadCoords=[ |
<Placemark> |
Again there is a one-to-one correspondence between the Polyline attributes and the KML <LineString> attributes. The coordinates of the route are specified in the <coordinates> tag, although longitude and latitude are reversed, and altitude is added - this can be left as a placeholder of zero if desired. The lineWidth of the route is defined by the <width> tag. Similarly the the strokeColor of the route corresponds to the <color> tag. There is however a difference in the encoding here, as Nokia Maps uses RGB encoding, and the KML standard uses ABGR encoding, so the first two bytes define the opacity (which can be defaulted to FF) and the red and blue ordering is reversed.
The saveMapObjects function - a JavaScript library to convert the Map to KML
Nokia Map data is stored in map.objects - it is a relatively simple matter to traverse the markers and obtain the necessary data to create KML <Point>s. Note that since the additional data element for the pop-up description is arbitrary, the .html attribute in this example may need to be altered to cope with other maps.
var markerData = new Object();
markerData.latitude = map.objects.get(i).coordinate.latitude;
markerData.longitude = map.objects.get(i).coordinate.longitude;
markerData.description = map.objects.get(i).html;
markerData.href = map.objects.get(i).icon.src;
if ( markerData.href === undefined ){
markerData.color = map.objects.get(i).brush.color;
}
Similarly the data from a Polyline can be collected to hold the necessary information for a KML <LineString>
var lineData = new Object();
var path = map.objects.get(i).path.asArray();
var geocoords = new Array();
for (j=0; j< path.length; j = j + 3){
var geocoord = new Object();
geocoord.latitude = path[j];
geocoord.longitude = path[j+ 1];
geocoords.push(geocoord);
}
lineData.coordinates = geocoords;
lineData.color = map.objects.get(i).pen.strokeColor;
lineData.width = map.objects.get(i).pen.lineWidth;
The collected data may then be written out as KML <Placemark>s using the method below:
var kmlOutput = "<?xml version='1.0' encoding='UTF-8'?><kml xmlns='http://www.opengis.net/kml/2.2'><Document>\n";
// Output all the routes
for (i=0; i< lines.length; i ++){
kmlOutput = kmlOutput + "<Placemark>\n";
kmlOutput = kmlOutput + "<LineString><coordinates>"
for (j=0; j< lines[i].coordinates.length; j++){
kmlOutput = kmlOutput + lines[i].coordinates[j].longitude + "," + lines[i].coordinates[j].latitude + ",0\n";
}
var RGBcolor = lines[i].color.replace("#", "");
kmlOutput = kmlOutput + "</coordinates></LineString>\n";
kmlOutput = kmlOutput + "<Style><LineStyle>";
kmlOutput = kmlOutput + "<width>" + lines[i].width + "</width>";
kmlOutput = kmlOutput + "<color>ff" + RGBcolor.substring(4,6)+ RGBcolor.substring(2,4)+ RGBcolor.substring(0,2)+ "</color>";
kmlOutput = kmlOutput + "</LineStyle></Style>\n";
kmlOutput = kmlOutput + "</Placemark>\n";
}
// Output all the markers
for (i=0; i< markers.length; i ++){
kmlOutput = kmlOutput + "<Placemark>\n";
if ( markers[i].description === undefined ){
kmlOutput = kmlOutput + "<description/>\n";
} else {
kmlOutput = kmlOutput + "<description><![CDATA[" + markers[i].description +"]]></description>\n";
}
kmlOutput = kmlOutput + "<Point><coordinates>" + markers[i].longitude + "," + markers[i].latitude + ",0</coordinates></Point>\n";
if ( markers[i].href === undefined ){
kmlOutput = kmlOutput + "<styleUrl>" + markers[i].color + "</styleUrl>\n";
} else {
kmlOutput = kmlOutput + "<Style><IconStyle><Icon>";
kmlOutput = kmlOutput + "<href>" + markers[i].href + "</href>";
kmlOutput = kmlOutput + " </Icon></IconStyle></Style>\n";
}
kmlOutput = kmlOutput + "</Placemark>\n";
}
// Add in coloured standard markers as Icon Styles
kmlOutput = kmlOutput + "<Style id='000000'>\n";
kmlOutput = kmlOutput + "<IconStyle><Icon><href>http://www.developer.nokia.com/Community/Wiki/images/0/02/000000.png</href></Icon></IconStyle>\n";
kmlOutput = kmlOutput + "</Style>\n";
kmlOutput = kmlOutput + "<Style id='0000FF'>\n";
kmlOutput = kmlOutput + "<IconStyle><Icon><href>http://www.developer.nokia.com/Community/Wiki/images/e/e5/0000FF.png</href></Icon></IconStyle>\n";
kmlOutput = kmlOutput + "</Style>\n";
kmlOutput = kmlOutput + "<Style id='FF0000'>\n";
kmlOutput = kmlOutput + "<IconStyle><Icon><href>http://www.developer.nokia.com/Community/Wiki/images/9/99/FF0000.png</href></Icon></IconStyle>\n";
kmlOutput = kmlOutput + "</Style>\n";
kmlOutput = kmlOutput + "<Style id='FFFF00'>\n";
kmlOutput = kmlOutput + "<IconStyle><Icon><href>http://www.developer.nokia.com/Community/Wiki/images/6/68/FFFF00.png</href></Icon></IconStyle>\n";
kmlOutput = kmlOutput + "</Style>\n";
kmlOutput = kmlOutput + "<Style id='FFFFFF'>\n";
kmlOutput = kmlOutput + "<IconStyle><Icon><href>http://www.developer.nokia.com/Community/Wiki/images/d/de/FFFFFF.png</href></Icon></IconStyle>\n";
kmlOutput = kmlOutput + "</Style>\n";
// Complete the document
kmlOutput = kmlOutput + "</Document></kml>";
// Output to screen
document.getElementById("kmloutput").value = kmlOutput;
Working Example : Premiership Teams
For the working example the final line of the JavaScript will need to be altered to:
kml.parseKML("http://example.com/premiership.kml");
The attached coded example - Media:Premiership.zip - contains three versions of the same map:
- a Nokia Map written creating Markers directly - which also contains the code to generate its own KML
- a Nokia Map which loads the KML file - this code is take directly from the Load KML Example of the API explorer - note that the the location of the KML file will have to be altered as necessary. Most browsers will complain if you attempt to load KML from the local drive.
- a Google Map for the same data set written by creating the Markers directly. This is for comparison.
Each file also contains standard timing code, to display the length of time taken to load the map. Obviously the rate at which map data is generated on screen will depend upon a variety of factors such as the performance of the browser and the load of the Internet at the time of test. For the attached coded example, the following metric was generated for three runs of the maps using the same browser. In all cases, the cache was not cleared for each run.
Firefox
| Initial Run | Second Run | Third Run | |
|---|---|---|---|
| Nokia Map | 0.551 s | 0.97 s | 0.584 s |
| Nokia Map KML | 0.194 s | 0.101 s | 0.138 s |
| Google Map | 1.087 s | 0.894 s | 0.74 s |
The obvious conclusion to be drawn, is that using a KML file should increase the performance of the rendering of the map.
Additional Benefits
Since KML is a de facto standard, the same data file can be used by multiple applications. Most major search engines, such as Google, Bing and Ask are able to crawl KML files if the website contains an appropriate sitemap.xml file. For full details of the protocol, please read the definitions on: http://www.sitemaps.org/
Example Sitemap.xml File
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:geo="http://www.google.com/geo/schemas/sitemap/1.0">
<url>
<loc>http://www.example.com/premiership.kml</loc>
<geo:geo>
<geo:format>kml</geo:format>
</geo:geo>
</url>
</urlset>
Additionally since KML is merely a subset of XML, Java ME mobile phone applications can read the same data using JSR-172. The screen shot below shows the same KML file embedded in a KML Reader running in the Java ME emulator:
Summary
Converting to KML format is relatively simple, and the process can be automated. The benefits of using KML format include faster rendering and the separation of mapping data from the code. Use of a standard format promotes cross-application use of the same data, and allows faster development of mapping applications across multiple platforms.
Article Metadata
Code Example
Tested with
Compatibility
Article



