×
Namespaces

Variants
Actions
Revision as of 18:50, 24 April 2013 by jasfox (Talk | contribs)

HERE Maps API - Converting from JavaScript to KML

From Nokia Developer Wiki
Jump to: navigation, search
Featured Article
15 Jan
2012

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 HERE 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(
new nokia.maps.geo.Coordinate(52.5091331805643,-1.88484460115433), {
icon: "http://www.example.com/icon.png",
anchor: new nokia.maps.util.Point(16, 32)
});
<Placemark>
<Point>
<coordinates>-1.88484460115433,52.5091331805643,0</coordinates>
</Point>
<Style>
<IconStyle>
<Icon>
<href>http://www.example.com/icon.png</href>
</Icon>
<hotSpot x="16" y="32" xunits="pixels" yunits="pixels">
</IconStyle>
</Style>
</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"
var red = "#FF0000";
marker = new nokia.maps.map.StandardMarker(
new nokia.maps.geo.Coordinate(53.463043359365,-2.29132533073425),
{brush: {color: red}
});
</script>
<Style id='FF0000'>
<IconStyle>
<Icon>
<href>http://www.example.com/Red.png</href>
</Icon>
</IconStyle>
</Style>
<Placemark>
<Point>
<coordinates>-2.29132533073425,53.463043359365,0</coordinates>
</Point>
<styleUrl>#FFFF00</styleUrl>
</Placemark>

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 HERE 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
000000.png Black 000000 images/0/02/000000.png|- 0000FF.png Blue 0000FF images/e/e5/0000FF.png|- FF0000.png Red FF0000 http://www.developer.nokia.com/Community/Wiki/images/9/99/FF0000.png
FFFF00.png Yellow FFFF00 images/6/68/FFFF00.png|- FFFFFF.png White 000000 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.

Note.pngNote: The RGB value of the color is the same as the id of the <Style> containing the image of a marker with that color. This is how the <Style> and <styleUrl> can link together.

InfoBubble

An Infobubble can be defined as KML <Placemark> with a <description> tag

Nokia Map KML Syntax
function addInfoBubble(infoBubbles, marker, html) {
marker.html = html;
marker.addListener("click" , function(evt) {
infoBubbles.addBubble(evt.target.html, evt.target.coordinate);
}, false);
}
<script type="text/javascript"
marker = new nokia.maps.map.StandardMarker(
new nokia.maps.geo.Coordinate(53.4393381986981,-2.2211828827858));
addInfoBubble( infoBubbles, marker, "<div>HTML Description</div>");
</script>
<Placemark>
<description><![CDATA[<div>HTML Description</div>]]></description>
<Point>
<coordinates>-2.2211828827858,53.4393381986981,0</coordinates>
</Point>
</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.

Note.pngNote: The naming of the additional data element is arbitrary, but frequently something like .html or .description will be used.

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=[
new nokia.maps.geo.Coordinate(37.385433,-8.375983)
,new nokia.maps.geo.Coordinate(37.385577,-8.376008)
,new nokia.maps.geo.Coordinate(37.385674,-8.376025)
];
 
map.objects.add(new nokia.maps.map.Polyline(
roadCoords,
{pen: {
strokeColor: "#8dc72d",
lineWidth: 5
}
}));
 <Placemark>
<LineString>
<coordinates>
-8.375983,37.385433,0
-8.376008,37.385577,0
-8.376025,37.385674,0
</coordinates>
</LineString>
<Style>
<LineStyle>
<width>5</width>
<color>ff2dc78d</color>
</LineStyle>
</Style>
</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 HERE 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

Warning.pngWarning: in order to load a KML file successfully, the KML file should be hosted on the same domain as the JavaScript or the results may be unpredictable. Some browsers will automatically prohibit cross-domain access.

For the working example the final line of the JavaScript will need to be altered to:

kml.parseKML("http://example.com/premiership.kml");
where http://example.com is replaced with the domain name of the server where premiershipKML.html is hosted.

Tip.pngTip: The KML reader of the HERE Maps API for JavaScript will ignore elements it is unable to interpret. KML readers vary and are more or less forgiving in the strictness of interpreting the KML specifications. It is recommended that you validate your KML syntax yourself through an online validator such as: http://feedvalidator.org


PremiershipKML.png
<Placemark>
<description>
<![CDATA[<div ><a href='http://www.mfc.premiumtv.co.uk' >Middlesbrough</a></div>
<div >Riverside Stadium<br>Capacity: 35,100</div>]]>
</description>
<Point>
<coordinates>-1.21696650981903,54.5783022217098,0</coordinates>
</Point>
<styleUrl>#FF0000</styleUrl>
</Placemark>


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:

PremiershipJavaME.png

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.


{{ArticleMetaData

sourcecode= [[Media:Premiership.zip installfile= devices= Internet Explorer, Google Chrome, Firefox, Opera sdk= platform= Web devicecompatability= dependencies= HERE Maps 2.2.3 signing= capabilities= keywords= HERE Maps, JavaScript, KML language= translated-by= translated-from-title= translated-from-id= review-by= review-timestamp= update-by= update-timestamp= creationdate= 20120105 author= jasfox

}}

4509 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×