1 /*
  2  * Timemap.js Copyright 2010 Nick Rabinowitz.
  3  * Licensed under the MIT License (see LICENSE.txt)
  4  */
  5  
  6 /**
  7  * @fileOverview
  8  * GeoRSS Loader
  9  *
 10  * @author Nick Rabinowitz (www.nickrabinowitz.com)
 11  */
 12 
 13 /*globals GXml, TimeMap, TimeMapDataset */
 14 
 15 /**
 16  * @class
 17  * GeoRSS loader: Load GeoRSS feeds.
 18  *
 19  * <p> Parsing is complicated by the 
 20  * diversity of GeoRSS formats; this parser handles:</p>
 21  * <ul>
 22  *  <li>RSS feeds</li>
 23  *  <li>Atom feeds</li>
 24  * </ul>
 25  * <p>and looks for geographic information in the following formats:</p>
 26  * <ul>
 27  *  <li>GeoRSS-Simple</li>
 28  *  <li>GML </li>
 29  *  <li>W3C Geo</li>
 30  * </ul>
 31  * <p>At the moment, this only supports points, polygons, polylines; boxes
 32  * may be added at some later point.</p>
 33  *
 34  * @augments TimeMap.loaders.xml
 35  * @requires loaders/xml.js
 36  * @requires param.js
 37  * @borrows TimeMap.loaders.georss.parse as #parse
 38  *
 39  * @example
 40 TimeMap.init({
 41     datasets: [
 42         {
 43             title: "GeoRSS Dataset",
 44             type: "georss", // Data to be loaded in GeoRSS
 45             options: {
 46                 url: "mydata.rss" // GeoRSS file to load - must be a local URL
 47             }
 48         }
 49     ],
 50     // etc...
 51 });
 52  * @see <a href="../../examples/earthquake_georss.html">GeoRSS Example</a>
 53  *
 54  * @param {Object} options          All options for the loader:
 55  * @param {String} options.url          URL of GeoRSS file to load (NB: must be local address)
 56  * @param {mixed} [options[...]]        Other options (see {@link TimeMap.loaders.xml})
 57  */
 58 TimeMap.loaders.georss = function(options) {
 59     var loader = new TimeMap.loaders.xml(options);
 60     loader.parse = TimeMap.loaders.georss.parse;
 61     return loader;
 62 };
 63 
 64 /**
 65  * Static function to parse GeoRSS
 66  *
 67  * @param {String} rss      GeoRSS string to be parsed
 68  * @return {TimeMapItem[]}  Array of TimeMapItems
 69  */
 70 TimeMap.loaders.georss.parse = function(rss) {
 71     var items = [], data, node, placemarks, pm, i;
 72     node = GXml.parse(rss);
 73     
 74     // get TimeMap utilty functions
 75     // assigning to variables should compress better
 76     var util = TimeMap.util,
 77         getTagValue = util.getTagValue,
 78         getNodeList = util.getNodeList,
 79         makePoint = util.makePoint,
 80         makePoly = util.makePoly,
 81         formatDate = util.formatDate,
 82         nsMap = util.nsMap;
 83     
 84     // define namespaces
 85     nsMap.georss = 'http://www.georss.org/georss';
 86     nsMap.gml = 'http://www.opengis.net/gml';
 87     nsMap.geo = 'http://www.w3.org/2003/01/geo/wgs84_pos#';
 88     nsMap.kml = 'http://www.opengis.net/kml/2.2';
 89     
 90     // determine whether this is an Atom feed or an RSS feed
 91     var feedType = (node.firstChild.tagName == 'rss') ? 'rss' : 'atom';
 92     
 93     // look for placemarks
 94     var tName = (feedType == 'rss' ? "item" : "entry");
 95     placemarks = getNodeList(node, tName);
 96     for (i=0; i<placemarks.length; i++) {
 97         pm = placemarks[i];
 98         data = { options: {} };
 99         // get title & description
100         data.title = getTagValue(pm, "title");
101         tName = (feedType == 'rss' ? "description" : "summary");
102         data.options.description = getTagValue(pm, tName);
103         // get time information, allowing KML-namespaced time elements
104         var nList = getNodeList(pm, "TimeStamp", "kml");
105         if (nList.length > 0) {
106             data.start = getTagValue(nList[0], "when", "kml");
107         }
108         // look for timespan
109         if (!data.start) {
110             nList = getNodeList(pm, "TimeSpan", "kml");
111             if (nList.length > 0) {
112                 data.start = getTagValue(nList[0], "begin", "kml");
113                 data.end = getTagValue(nList[0], "end", "kml") ||
114                     // unbounded spans end at the present time
115                     formatDate(new Date());
116             }
117         }
118         // otherwise, use pubDate/updated elements
119         if (!data.start) {
120             if (feedType == 'rss') {
121                 // RSS needs date conversion
122                 var d = new Date(Date.parse(getTagValue(pm, "pubDate")));
123                 // reformat
124                 data.start = formatDate(d);
125             } else {
126                 // atom uses ISO 8601
127                 data.start = getTagValue(pm, "updated");
128             }
129         }
130         // find placemark - single geometry only for the moment
131         var done = false;
132         PLACEMARK: while (!done) {
133             var coords, geom;
134             // look for point, GeoRSS-Simple
135             coords = getTagValue(pm, "point", 'georss');
136             if (coords) {
137                 data.point = makePoint(coords); 
138                 break PLACEMARK;
139             }
140             // look for point, GML
141             nList = getNodeList(pm, "Point", 'gml');
142             if (nList.length > 0) {
143                 // GML <pos>
144                 coords = getTagValue(nList[0], "pos", 'gml');
145                 // GML <coordinates>
146                 if (!coords) {
147                     coords = getTagValue(nList[0], "coordinates", 'gml');
148                 }
149                 if (coords) {
150                     data.point = makePoint(coords); 
151                     break PLACEMARK;
152                 }
153             }
154             // look for point, W3C Geo
155             if (getTagValue(pm, "lat", 'geo')) {
156                 coords = [
157                     getTagValue(pm, "lat", 'geo'),
158                     getTagValue(pm, "long", 'geo')
159                 ];
160                 data.point = makePoint(coords); 
161                 break PLACEMARK;
162             }
163             // look for polyline, GeoRSS-Simple
164             coords = getTagValue(pm, "line", 'georss');
165             if (coords) {
166                 data.polyline = makePoly(coords); 
167                 break PLACEMARK;
168             }
169             // look for polygon, GeoRSS-Simple
170             coords = getTagValue(pm, "polygon", 'georss');
171             if (coords) {
172                 data.polygon = makePoly(coords); 
173                 break PLACEMARK;
174             }
175             // look for polyline, GML
176             nList = getNodeList(pm, "LineString", 'gml');
177             if (nList.length > 0) {
178                 geom = "polyline";
179             } else {
180                 nList = getNodeList(pm, "Polygon", 'gml');
181                 if (nList.length > 0) {
182                     geom = "polygon";
183                 }
184             }
185             if (nList.length > 0) {
186                 // GML <posList>
187                 coords = getTagValue(nList[0], "posList", 'gml');
188                 // GML <coordinates>
189                 if (!coords) {
190                     coords = getTagValue(nList[0], "coordinates", 'gml');
191                 }
192                 if (coords) {
193                     data[geom] = makePoly(coords);
194                     break PLACEMARK;
195                 }
196             }
197             
198             // XXX: deal with boxes
199             
200             done = true;
201         }
202         // look for any extra tags specified
203         this.parseExtra(data, pm);
204         items.push(data);
205     }
206     
207     // clean up
208     node = placemarks = pm = nList = null;
209     return items;
210 };
211