1 /* 2 * Timemap.js Copyright 2010 Nick Rabinowitz. 3 * Licensed under the MIT License (see LICENSE.txt) 4 */ 5 6 /** 7 * @fileOverview 8 * KML Loader 9 * 10 * @author Nick Rabinowitz (www.nickrabinowitz.com) 11 */ 12 13 /*globals GXml, TimeMap */ 14 15 /** 16 * @class 17 * KML loader: load KML files. 18 * 19 * <p>This is a loader class for KML files. Currently supports all geometry 20 * types (point, polyline, polygon, and overlay) and multiple geometries. Supports loading 21 * <a href="http://code.google.com/apis/kml/documentation/extendeddata.html">ExtendedData</a> 22 * through the extendedData parameter. 23 * </p> 24 * 25 * @augments TimeMap.loaders.xml 26 * @requires loaders/xml.js 27 * @requires param.js 28 * @borrows TimeMap.loaders.kml.parse as #parse 29 * 30 * @example 31 TimeMap.init({ 32 datasets: [ 33 { 34 title: "KML Dataset", 35 type: "kml", 36 options: { 37 url: "mydata.kml" // Must be local 38 } 39 } 40 ], 41 // etc... 42 }); 43 * @see <a href="../../examples/kenya.html">KML Example</a> 44 * @see <a href="../../examples/kml_extendeddata.html">KML ExtendedData Example</a> 45 * 46 * @param {Object} options All options for the loader 47 * @param {String} options.url URL of KML file to load (NB: must be local address) 48 * @param {String[]} [options.extendedData] Array of names for ExtendedData data elements 49 * @param {mixed} [options[...]] Other options (see {@link TimeMap.loaders.xml}) 50 * @return {TimeMap.loaders.xml} Loader configured for KML 51 */ 52 TimeMap.loaders.kml = function(options) { 53 var loader = new TimeMap.loaders.xml(options), 54 tagMap = options.tagMap || {}, 55 extendedData = options.extendedData || [], 56 tagName, x; 57 58 // Add ExtendedData parameters to extra params 59 for (x=0; x < extendedData.length; x++) { 60 tagName = extendedData[x]; 61 loader.extraParams.push( 62 new TimeMap.params.ExtendedDataParam(tagMap[tagName] || tagName, tagName) 63 ); 64 } 65 66 // set custom parser 67 loader.parse = TimeMap.loaders.kml.parse; 68 return loader; 69 }; 70 71 /** 72 * Static function to parse KML with time data. 73 * 74 * @param {XML string} kml KML to be parsed 75 * @return {TimeMapItem Array} Array of TimeMapItems 76 */ 77 TimeMap.loaders.kml.parse = function(kml) { 78 var items = [], data, kmlnode, placemarks, pm, i, j; 79 kmlnode = GXml.parse(kml); 80 81 // get TimeMap utilty functions 82 // assigning to variables should compress better 83 var util = TimeMap.util, 84 getTagValue = util.getTagValue, 85 getNodeList = util.getNodeList, 86 makePoint = util.makePoint, 87 makePoly = util.makePoly, 88 formatDate = util.formatDate; 89 90 // recursive time data search 91 var findNodeTime = function(n, data) { 92 var check = false; 93 // look for instant timestamp 94 var nList = getNodeList(n, "TimeStamp"); 95 if (nList.length > 0) { 96 data.start = getTagValue(nList[0], "when"); 97 check = true; 98 } 99 // otherwise look for span 100 else { 101 nList = getNodeList(n, "TimeSpan"); 102 if (nList.length > 0) { 103 data.start = getTagValue(nList[0], "begin"); 104 data.end = getTagValue(nList[0], "end") || 105 // unbounded spans end at the present time 106 formatDate(new Date()); 107 check = true; 108 } 109 } 110 // try looking recursively at parent nodes 111 if (!check) { 112 var pn = n.parentNode; 113 if (pn.nodeName == "Folder" || pn.nodeName=="Document") { 114 findNodeTime(pn, data); 115 } 116 pn = null; 117 } 118 }; 119 120 // look for placemarks 121 placemarks = getNodeList(kmlnode, "Placemark"); 122 for (i=0; i<placemarks.length; i++) { 123 pm = placemarks[i]; 124 data = { options: {} }; 125 // get title & description 126 data.title = getTagValue(pm, "name"); 127 data.options.description = getTagValue(pm, "description"); 128 // get time information 129 findNodeTime(pm, data); 130 // find placemark(s) 131 var nList, coords, pmobj; 132 data.placemarks = []; 133 // look for marker 134 nList = getNodeList(pm, "Point"); 135 for (j=0; j<nList.length; j++) { 136 pmobj = { point: {} }; 137 // get lat/lon 138 coords = getTagValue(nList[j], "coordinates"); 139 pmobj.point = makePoint(coords, 1); 140 data.placemarks.push(pmobj); 141 } 142 // look for polylines 143 nList = getNodeList(pm, "LineString"); 144 for (j=0; j<nList.length; j++) { 145 pmobj = { polyline: [] }; 146 // get lat/lon 147 coords = getTagValue(nList[j], "coordinates"); 148 pmobj.polyline = makePoly(coords, 1); 149 data.placemarks.push(pmobj); 150 } 151 // look for polygons 152 nList = getNodeList(pm, "Polygon"); 153 for (j=0; j<nList.length; j++) { 154 pmobj = { polygon: [] }; 155 // get lat/lon 156 coords = getTagValue(nList[j], "coordinates"); 157 pmobj.polygon = makePoly(coords, 1); 158 // XXX: worth closing unclosed polygons? 159 data.placemarks.push(pmobj); 160 } 161 // look for any extra tags and/or ExtendedData specified 162 this.parseExtra(data, pm); 163 164 items.push(data); 165 } 166 167 // look for ground overlays 168 placemarks = getNodeList(kmlnode, "GroundOverlay"); 169 for (i=0; i<placemarks.length; i++) { 170 pm = placemarks[i]; 171 data = { options: {}, overlay: {} }; 172 // get title & description 173 data.title = getTagValue(pm, "name"); 174 data.options.description = getTagValue(pm, "description"); 175 // get time information 176 findNodeTime(pm, data); 177 // get image 178 nList = getNodeList(pm, "Icon"); 179 data.overlay.image = getTagValue(nList[0], "href"); 180 // get coordinates 181 nList = getNodeList(pm, "LatLonBox"); 182 data.overlay.north = getTagValue(nList[0], "north"); 183 data.overlay.south = getTagValue(nList[0], "south"); 184 data.overlay.east = getTagValue(nList[0], "east"); 185 data.overlay.west = getTagValue(nList[0], "west"); 186 // look for any extra tags and/or ExtendedData specified 187 this.parseExtra(data, pm); 188 items.push(data); 189 } 190 191 // clean up 192 kmlnode = placemarks = pm = nList = null; 193 194 return items; 195 }; 196 197 /** 198 * @class 199 * Class for parameters loaded from KML ExtendedData elements 200 * 201 * @augments TimeMap.params.OptionParam 202 * 203 * @constructor 204 * @param {String} paramName String name of the parameter 205 * @param {String} [tagName] Tag name, if different 206 */ 207 TimeMap.params.ExtendedDataParam = function(paramName, tagName) { 208 return new TimeMap.params.OptionParam(paramName, { 209 210 /** 211 * Set a config object based on an ExtendedData element 212 * @name TimeMap.params.ExtendedDataParam#setConfigKML 213 * @function 214 * 215 * @param {Object} config Config object to modify 216 * @param {XML NodeList} node Parent node to look for tags in 217 */ 218 setConfigXML: function(config, node) { 219 var util = TimeMap.util, 220 nList = util.getNodeList(node, "Data"), 221 i; 222 for (i=0; i<nList.length; i++) { 223 if (nList[i].getAttribute("name") == tagName) { 224 this.setConfig(config, util.getTagValue(nList[i], "value")) 225 } 226 } 227 node = nList = null; 228 }, 229 230 sourceName: tagName 231 232 }); 233 }; 234