×
Namespaces

Variants
Actions
Revision as of 11:47, 19 January 2012 by jasfox (Talk | contribs)

HERE Maps API - Converting any data file to KML

From Nokia Developer Wiki
Jump to: navigation, search

Underconstruction.pngUnder Construction: (20120118141431) This article is under construction and it may have outstanding issues. If you have any comments please use the comments tab.

This article explains how to read address data from an arbitrary file format, and create a KML file for display on a map.

Note.pngNote: in order to load a KML file successfully, the generated 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 example, if you are hosting at example.com, the final line of the JavaScript to load the KML will need to be:

kml.parseKML("http://example.com/" + "generated_kml_data_file.kml")
and both the KML loading HTML and the generated_kml_data_file.kml should be placed on http://example.com/

Article Metadata
Code ExampleTested with
Devices(s): Firefox 9.0.1
Compatibility
Platform(s): Web Browser
Article
Keywords: Nokia Maps, JavaScript, KML
Created: jasfox (18 Jan 2012)
Last edited: jasfox (19 Jan 2012)

Introduction

Keyhole Markup Language (KML) is an XML notation for geographic applications. The advantages of using KML are numerous, and have been listed in a previous article. A typical enterprise may wish to add some markers representing addresses onto a map for their website, but without necessarily learning too much about geocoding or KML. It is likely that the address data they have is already held in a file or spreadsheet somewhere.

This example aims to take the pain out of creating a KML dataset. It aims to take any data format and attempt to the locate addresses given by specified fields from the file. The addresses are then transformed into KML <Placemarks> with associated <name> and <description> elements taken from other fields from the same record. The generated KML data can be inspected and edited using the editor example given here and then displayed using the code from the How to display KML data example. In summary, this article demonstrates a real world use of the geocoding service and is an example of making sequential asynchronous JavaScript calls to obtain longitude and latitude.


Defining the issue

For a typical data file, the first line in the file will be a header line which defines the fields in the records below. Each field will be separated from the next by some arbitrary separator character. For a CSV file for example, the separator character will be a comma (,) for other data formats it may be a space( ), a pipe (|) or some other character. At the end of the header line there will be some form of terminating character (typically a new line). Thereafter each subsequent line of data will hold a record of separated fields of data, with each field being associated to the definition of the field held in the header.

For example the following spreadsheet

serial name unused address_street address_city address_district description
13556 Millenium Stage Something 401 Bay Drive New York This is an example
3243 The Chambers Something else 53 Bothwell Street Watford Hertfordshire Some more text here
9954 4th Dimension More data 226 Myrtle Ave Toronto
8645 E.K.B.R. Even more data Invalidenstrasse 117 Berlin Hier gibts etwas


could be written out as a CSV file in the following format:

 serial, name , unused,address_street, address_city, address_district, description
13556, Millenium Stage, Something, 401 Bay Drive,New York, This is an example
3243, The Chambers, Something else, 53 Bothwell Street, Watford,Hertfordshire, Some more text here
9954, 4th Dimension, More data, 226 Myrtle Ave , Toronto,,
8645, E.K.B.R, Even more data, Invalidenstrasse 117, Berlin,, Hier gibts etwas

The problem comes in several parts:

  • Deciding where the header line starts - in the case above, there is no preamble, but potentially the data could have a prefix or some white space before it.
  • Deciding what the field and line terminators are
  • Deciding where the data starts - in the case above, again there is no premable, but potentially there could be a gao between the data and the header.
  • Deciding which fields are needed in the KML
  • Deciding which fields form the address to geocode.
  • Deciding what do do if the geocoding fails.


   // Set up variables for later use
// These varibales will define which fields are extracted from the data.
var headerStart ;
var dataStart;
var lineSep;
var fieldSep;
var addressFields;
var descriptionFields;
var nameFields ;
var idFields;
var styleURLFields;
 
// These variables will hold the data for the current record being processed
var id ="";
var data;
var headers;
var address = "";
var name = "";
var description = "";
var styleURL = "";
 
// We need to keep a count of which record we are processing, and if we are
// trying a fallback geocoding option.
var addressingAttempt = 0;
var currentRecord = 0;
 
// Search Manager taken directly from playground examples.
var searchManager = new nokia.maps.search.Manager();
searchManager.addObserver("state", function (observedManager, key, value) {
// If the search has finished we can process the result.
if (value == "finished") {
// We check that at least one location has been found
if (observedManager.locations.length > 0) {
var markerData = new Object();
// Since we have an address we can add the current data to the map
// as the addressing data has been found to be valid.
markerData.coords = observedManager.locations[0].displayPosition;
markerData.id = id;
markerData.title = map.objects.getLength() + 1;
markerData.description = description.trim();
markerData.name = name.trim();
markerData.address = address.trim();
markerData.styleURL = styleURL.trim();
 
addMarker(markerData);
// Center on the new marker and start to process the next record.
map.setCenter(observedManager.locations[0].displayPosition);
addressingAttempt = 0;
currentRecord++;
lat.innerHTML= "Found: " + address;
} else {
// Try again with geocoding the same data, using alternate fields
// to define the address.
lat.innerHTML= "Not Found: " + address;
addressingAttempt++;
if (addressingAttempt == addressFields.length){
// There are no more fall back addressing options.
// Move on to the next record regardless.
addressingAttempt = 0;
currentRecord++;
}
}
// Find the next address, either a new record or using new address fields.
doNextGeoCode();
 
} else if (value == "failed") {
// Geocoding has failed.
lat.innerHTML = "The search request failed.";
}
 
});


   // Decide which fields are to be placed in which part of the KML.
// Split the header from the data and process each record in turn.
function doSplit (){
 
var dataInput = document.getElementById('dataInput').value;
 
headerStart = document.getElementById('headerStart').value; //"#DEFINITION#\n" for Right Move.
headerStart = headerStart.replace("\\n", "\n").replace("\\n", "\n"); // Convert up to two \n into carriage returns
dataStart = document.getElementById('dataStart').value; // "#DATA#\n" for Right Move.
dataStart = dataStart.replace("\\n", "\n").replace("\\n", "\n"); // Convert up to two \n into carriage returns
lineSep = document.getElementById('lineSep').value;// ; "|\n\n" for Right Move.
lineSep = lineSep.replace("\\n", "\n").replace("\\n", "\n"); // Convert up to two \n into carriage returns
 
fieldSep = document.getElementById('fieldSep').value;
addressFields = new Array();
 
// Each of these address strategies will be tried in turn.
addressFields.push(document.getElementById('addressAttempt1').value.split(fieldSep));
addressFields.push(document.getElementById('addressAttempt2').value.split(fieldSep));
addressFields.push(document.getElementById('addressAttempt3').value.split(fieldSep));
addressFields.push(document.getElementById('addressAttempt4').value.split(fieldSep));
 
// Any fields added here will be appended to the <address> element.
descriptionFields = document.getElementById('descriptionFields').value.split(fieldSep);
// Any fields added here will be appended to the <name> element.
nameFields = document.getElementById('nameFields').value.split(fieldSep);
// This field will be the id of the <Placemark> element.
idFields = document.getElementById('idField').value.split(fieldSep);
// These fields will make up the <styleURL> of the <Placemark>
styleURLFields = document.getElementById('styleURLFields').value.split(fieldSep);
 
 
if (headerStart != ""){
// Remove any pre-amble before the header record.
var from = dataInput.search (headerStart) + headerStart.length;
dataInput = dataInput.substr(from);
}
data = dataInput.split (lineSep);
headers = data[0].split(fieldSep);
 
if (dataStart != ""){
// Remove any preable prior to the first data record.
var from = dataInput.search (dataStart) + dataStart.length;
dataInput = dataInput.substr(from);
}
 
data = dataInput.split (lineSep);
 
// Find the FIRST address. The next one will be chained from the
// search manager.
currentRecord = 0;
addressingAttempt = 0;
doNextGeoCode();
}
// Standard Library for spliting up Comma Delimited Texts.  
String.prototype.splitCSV = function(sep) {
for (var foo = this.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) {
if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) == '"') {
if ((tl = foo[x].replace(/^\s+"/, '"')).length > 1 && tl.charAt(0) == '"') {
foo[x] = foo[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"');
} else if (x) {
foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep));
} else foo = foo.shift().split(sep).concat(foo);
} else foo[x].replace(/""/g, '"');
}
return foo;
};
  // Obtains the Longitude and Latitude of the next record
// Based upon the data in the chosen fields of that recod.
function doNextGeoCode(){
 
if (currentRecord < data.length){
 
var dataRecord = data[currentRecord].splitCSV(fieldSep);
 
 
address = getFieldsFromDefinition(addressFields[addressingAttempt], headers, dataRecord );
description = getFieldsFromDefinition(descriptionFields, headers, dataRecord );
name = getFieldsFromDefinition(nameFields, headers, dataRecord );
id = getFieldsFromDefinition(idFields, headers, dataRecord );
styleURL = getFieldsFromDefinition(styleURLFields, headers, dataRecord);
 
 
// Assuming we have an address to try, we should geocode it.
if (address != "" ){
searchManager.geocode(address);
} else {
// Otherwise we need to try another addressing strategy.
addressingAttempt++;
if (addressingAttempt == addressFields.length){
// Since we have run out of addressing strategies,
// try the next record.
addressingAttempt = 0;
currentRecord++;
}
doNextGeoCode();
}
} else {
// We can generate the KML
saveMapObjects(map)
}
}
function getFieldsFromDefinition(definition, headerFields, dataRecord ){
var result = "";
for (var defFieldCount = 0; defFieldCount < definition.length ; defFieldCount++){
for (var headerFieldCount = 0; headerFieldCount < headerFields.length ; headerFieldCount++){
 
if (headerFields[headerFieldCount] == definition [defFieldCount]){
if (headerFieldCount <= dataRecord.length){
result = result + dataRecord[headerFieldCount] + " ";
}
break;
}
if (headerFieldCount == headerFields.length - 1){
if ( headerFieldCount <= dataRecord.length){
result = result + definition [defFieldCount];
}
}
}
 
}
return result.trim();
}


Summary

Add categories below. Remove Category:Draft when the page is complete or near complete

192 page views in the last 30 days.
×