From dd1b1433e44ca667dbbc6189afe12886bff79e3f Mon Sep 17 00:00:00 2001 From: crschmidt Date: Fri, 14 Sep 2007 22:50:30 +0000 Subject: [PATCH] Format.GeoRSS didn't use Format.XML serializer, nor did it support reading anything other than points. Both of these are resolved with the new GeoRSS support, which supports all of GeoRSS simple. Includes tests and significant review from Senor Schaub, and addition to the examples/vector-formats.html file. (Closes #973) git-svn-id: http://svn.openlayers.org/trunk/openlayers@4305 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- examples/vector-formats.html | 2 + lib/OpenLayers/Format/GeoRSS.js | 279 ++++++++++++++++++++++++++------ tests/Format/test_GeoRSS.html | 37 +++-- 3 files changed, 261 insertions(+), 57 deletions(-) diff --git a/examples/vector-formats.html b/examples/vector-formats.html index 7049a43e53..8890c81efd 100644 --- a/examples/vector-formats.html +++ b/examples/vector-formats.html @@ -77,6 +77,7 @@ formats = { wkt: new OpenLayers.Format.WKT(), geojson: new OpenLayers.Format.GeoJSON(), + georss: new OpenLayers.Format.GeoRSS(), gml: new OpenLayers.Format.GML(), kml: new OpenLayers.Format.KML() }; @@ -147,6 +148,7 @@ diff --git a/lib/OpenLayers/Format/GeoRSS.js b/lib/OpenLayers/Format/GeoRSS.js index 047d91f4f7..039d138c69 100644 --- a/lib/OpenLayers/Format/GeoRSS.js +++ b/lib/OpenLayers/Format/GeoRSS.js @@ -4,46 +4,216 @@ /** * @requires OpenLayers/Format.js + * @requires OpenLayers/Format/XML.js * * Class: OpenLayers.Format.GeoRSS - * Write-only GeoRSS. Create a new instance with the + * Read/write GeoRSS parser. Create a new instance with the * constructor. * * Inherits from: - * - + * - */ -OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format, { +OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, { /** * APIProperty: rssns - * RSS namespace to use. + * {String} RSS namespace to use. Defaults to + * "http://backend.userland.com/rss2" */ rssns: "http://backend.userland.com/rss2", /** * APIProperty: featurens - * Feature Attributes namespace + * {String} Feature Attributes namespace. Defaults to + * "http://mapserver.gis.umn.edu/mapserver" */ featureNS: "http://mapserver.gis.umn.edu/mapserver", /** * APIProperty: georssns - * GeoRSS namespace to use. + * {String} GeoRSS namespace to use. Defaults to + * "http://www.georss.org/georss" */ georssns: "http://www.georss.org/georss", + /** + * APIProperty: featureTitle + * {String} Default title for features. Defaults to "Untitled" + */ + featureTitle: "Untitled", + + /** + * APIProperty: featureDescription + * {String} Default description for features. Defaults to "No Description" + */ + featureDescription: "No Description", /** * Constructor: OpenLayers.Format.GeoRSS - * Create a new parser for GeoRSS + * Create a new parser for GeoRSS. * * Parameters: * options - {Object} An optional object whose properties will be set on - * this instance. + * this instance. */ initialize: function(options) { - OpenLayers.Format.prototype.initialize.apply(this, [options]); + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); }, + + /** + * Method: createGeometryFromItem + * Return a geometry from a GeoRSS Item. + * + * Parameters: + * item - {DOMElement} A GeoRSS item node. + * + * Returns: + * {} A geometry representing the node. + */ + createGeometryFromItem: function(item) { + var point = this.getElementsByTagNameNS(item, this.georssns, "point"); + var lat = this.getElementsByTagNameNS(item, this.geons, 'lat'); + var lon = this.getElementsByTagNameNS(item, this.geons, 'long'); + + var line = this.getElementsByTagNameNS(item, + this.georssns, + "line"); + var polygon = this.getElementsByTagNameNS(item, + this.georssns, + "polygon"); + + if (point.length > 0 || (lat.length > 0 && lon.length > 0)) { + if (point.length > 0) { + var location = OpenLayers.String.trim( + point[0].firstChild.nodeValue).split(/\s+/); + + if (location.length !=2) { + var location = OpenLayers.String.trim( + point[0].firstChild.nodeValue).split(/\s*,\s*/); + } + } else { + var location = [parseFloat(lat[0].firstChild.nodeValue), + parseFloat(lon[0].firstChild.nodeValue)]; + } + var geometry = new OpenLayers.Geometry.Point(parseFloat(location[1]), + parseFloat(location[0])); + } else if (line.length > 0) { + var coords = OpenLayers.String.trim(line[0].firstChild.nodeValue).split(/\s+/); + var components = []; + for (var i=0; i < coords.length; i+=2) { + var point = new OpenLayers.Geometry.Point(parseFloat(coords[i+1]), parseFloat(coords[i])); + components.push(point); + } + geometry = new OpenLayers.Geometry.LineString(components); + } else if (polygon.length > 0) { + var coords = OpenLayers.String.trim(polygon[0].firstChild.nodeValue).split(/\s+/); + var components = []; + for (var i=0; i < coords.length; i+=2) { + var point = new OpenLayers.Geometry.Point(parseFloat(coords[i+1]), parseFloat(coords[i])); + components.push(point); + } + geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]); + } + return geometry; + }, + + /** + * Method: createGeometryFromItem + * Return a feature from a GeoRSS Item. + * + * Parameters: + * item - {DOMElement} A GeoRSS item node. + * + * Returns: + * {} A feature representing the item. + */ + createFeatureFromItem: function(item) { + var geometry = this.createGeometryFromItem(item); + /* Provide defaults for title and description */ + var title = this.getChildValue(item, "*", "title", this.featureTitle); + + /* First try RSS descriptions, then Atom summaries */ + var description = this.getChildValue( + item, "*", "description", + this.getChildValue(item, "*", "content", this.featureDescription) + ); + + /* If no link URL is found in the first child node, try the + href attribute */ + var link = this.getChildValue(item, "*", "link"); + if(!link) { + try { + link = this.getElementsByTagNameNS(item, "*", "link")[0].getAttribute("href"); + } catch(e) { + link = null; + } + } + + var id = this.getChildValue(item, "*", "id", null); + + var data = { + "title": title, + "description": description, + "link": link + }; + var feature = new OpenLayers.Feature.Vector(geometry, data); + feature.fid = id; + return feature; + }, + + /** + * Method: getChildValue + * + * Parameters: + * node - {DOMElement} + * nsuri - {String} Child node namespace uri ("*" for any). + * name - {String} Child node name. + * def - {String} Optional string default to return if no child found. + * + * Returns: + * {String} The value of the first child with the given tag name. Returns + * default value or empty string if none found. + */ + getChildValue: function(node, nsuri, name, def) { + var value; + try { + value = this.getElementsByTagNameNS(node, nsuri, name)[0].firstChild.nodeValue; + } catch(e) { + value = (def == undefined) ? "" : def; + } + return value; + }, + + /** + * APIMethod: read + * Return a list of features from a GeoRSS doc + + * Parameters: + * data - {Element} + * + * Returns: + * An Array of s + */ + read: function(doc) { + if (typeof doc == "string") { + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); + } + + /* Try RSS items first, then Atom entries */ + var itemlist = null; + itemlist = this.getElementsByTagNameNS(doc, '*', 'item'); + if (itemlist.length == 0) { + itemlist = this.getElementsByTagNameNS(doc, '*', 'entry'); + } + + var numItems = itemlist.length; + var features = new Array(numItems); + for(var i=0; i}) List of features to serialize into a string. */ - write: function(features) { - var featureCollection = document.createElementNS(this.rssns, "rss"); - for (var i=0; i < features.length; i++) { - featureCollection.appendChild(this.createFeatureXML(features[i])); + write: function(features) { + var georss; + if(features instanceof Array) { + georss = this.createElementNS(this.rssns, "rss"); + for(var i=0; i < features.length; i++) { + georss.appendChild(this.createFeatureXML(features[i])); + } + } else { + georss = this.createFeatureXML(features); } - return featureCollection; - }, - + return OpenLayers.Format.XML.prototype.write.apply(this, [georss]); + }, + /** * Method: createFeatureXML * Accept an , and build a geometry for it. @@ -72,20 +247,26 @@ OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format, { */ createFeatureXML: function(feature) { var geometryNode = this.buildGeometryNode(feature.geometry); - var featureNode = document.createElementNS(this.rssns, "item"); - var titleNode = document.createElementNS(this.rssns, "title"); - titleNode.appendChild(document.createTextNode(feature.attributes.title ? feature.attributes.title : "")); - var descNode = document.createElementNS(this.rssns, "description"); - descNode.appendChild(document.createTextNode(feature.attributes.description ? feature.attributes.description : "")); + var featureNode = this.createElementNS(this.rssns, "item"); + var titleNode = this.createElementNS(this.rssns, "title"); + titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : "")); + var descNode = this.createElementNS(this.rssns, "description"); + descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : "")); featureNode.appendChild(titleNode); featureNode.appendChild(descNode); + if (feature.attributes.link) { + var linkNode = this.createElementNS(this.rssns, "link"); + linkNode.appendChild(this.createTextNode(feature.attributes.link)); + featureNode.appendChild(linkNode); + } for(var attr in feature.attributes) { - var attrText = document.createTextNode(feature.attributes[attr]); + if (attr == "link" || attr == "title" || attr == "description") { continue; } + var attrText = this.createTextNode(feature.attributes[attr]); var nodename = attr; if (attr.search(":") != -1) { nodename = attr.split(":")[1]; } - var attrContainer = document.createElementNS(this.featureNS, "feature:"+nodename); + var attrContainer = this.createElementNS(this.featureNS, "feature:"+nodename); attrContainer.appendChild(attrText); featureNode.appendChild(attrContainer); } @@ -98,30 +279,33 @@ OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format, { * builds a GeoRSS node with a given geometry * * Parameters: - * geometry - {} + * geometry - {} + * + * Returns: + * {DOMElement} A gml node. */ buildGeometryNode: function(geometry) { - var gml = ""; - // match MultiPolygon or Polygon + var node; + // match Polygon if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { - gml = document.createElementNS(this.georssns, 'georss:polygon'); - - gml.appendChild(this.buildCoordinatesNode(geometry.components[0])); - } - // match MultiLineString or LineString + node = this.createElementNS(this.georssns, 'georss:polygon'); + + node.appendChild(this.buildCoordinatesNode(geometry.components[0])); + } + // match LineString else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") { - gml = document.createElementNS(this.georssns, 'georss:line'); - - gml.appendChild(this.buildCoordinatesNode(geometry)); - } - // match MultiPoint or Point + node = this.createElementNS(this.georssns, 'georss:line'); + + node.appendChild(this.buildCoordinatesNode(geometry)); + } + // match Point else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { - gml = document.createElementNS(this.georssns, 'georss:point'); - gml.appendChild(this.buildCoordinatesNode(geometry)); - } else { - alert("Couldn't parse " + geometry.CLASS_NAME); + node = this.createElementNS(this.georssns, 'georss:point'); + node.appendChild(this.buildCoordinatesNode(geometry)); + } else { + throw "Couldn't parse " + geometry.CLASS_NAME; } - return gml; + return node; }, /** @@ -137,15 +321,18 @@ OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format, { points = geometry.components; } - var path = ""; + var path; if (points) { - for (var i = 0; i < points.length; i++) { - path += points[i].y + " " + points[i].x + " "; + var numPoints = points.length; + var parts = new Array(numPoints); + for (var i = 0; i < numPoints; i++) { + parts[i] = points[i].y + " " + points[i].x; } + path = parts.join(" "); } else { - path += geometry.y + " " + geometry.x + " "; + path = geometry.y + " " + geometry.x; } - return document.createTextNode(path); + return this.createTextNode(path); }, CLASS_NAME: "OpenLayers.Format.GeoRSS" diff --git a/tests/Format/test_GeoRSS.html b/tests/Format/test_GeoRSS.html index 53379a69e6..90c32c8c29 100644 --- a/tests/Format/test_GeoRSS.html +++ b/tests/Format/test_GeoRSS.html @@ -18,19 +18,34 @@ function test_Format_GeoRSS_serializeline(t) { t.plan(1); - if (OpenLayers.Util.getBrowserName() == "msie") { - //serialization currently not supported in IE - t.ok(true, "GeoRSS serialization not currently supported in IE"); - } else { - var parser = new OpenLayers.Format.GeoRSS(); - var point = new OpenLayers.Geometry.Point(-111.04, 45.68); - var point2 = new OpenLayers.Geometry.Point(-112.04, 45.68); - var l = new OpenLayers.Geometry.LineString([point, point2]); - var f = new OpenLayers.Feature.Vector(l); - var data = parser.write([f]); - t.eq(data.firstChild.childNodes[2].firstChild.nodeValue, '45.68 -111.04 45.68 -112.04 ', 'GeoRSS serializes a line correctly'); + var parser = new OpenLayers.Format.GeoRSS(); + var point = new OpenLayers.Geometry.Point(-111.04, 45.68); + var point2 = new OpenLayers.Geometry.Point(-112.04, 45.68); + var l = new OpenLayers.Geometry.LineString([point, point2]); + var f = new OpenLayers.Feature.Vector(l); + var data = parser.write([f]); + t.eq(data, '45.68 -111.04 45.68 -112.04', 'GeoRSS serializes a line correctly'); + } + function test_Format_GeoRSS_roundtrip(t) { + t.plan(input.length); + var parser = new OpenLayers.Format.GeoRSS(); + for(var i=0; i < input.length; i++) { + var feed = shell_start+input[i]+shell_end; + var data = parser.read(feed); + var out = parser.write(data); + t.eq(out, output[i], "Output gave expected value"); } } + + var shell_start = '\n scribble\n http://featureserver.org/featureserver.cgi/scribble?format=atom\n FeatureServer\n'; + var shell_end = ''; + var input = ['http://featureserver.org/featureserver.cgi/scribble/562.atomFeature 2<b>strokeColor</b>: red<br /><b>title</b>: Feature 2<br /><b>author</b>: Your Name Here-5.9765625 -131.484375 -58.0078125 -112.5 -50.2734375 -32.34375 52.3828125 -114.609375 -35.5078125 -167.34375 -57.3046875 -146.953125 -34.1015625 -139.921875 -5.9765625 -131.484375', + 'http://featureserver.org/featureserver.cgi/scribble/796.atomFeature 2<b>strokeColor</b>: 00ccff<br /><b>title</b>: Feature 2<br /><b>author</b>: Your Name Here75.5859375 15.46875', + 'http://featureserver.org/featureserver.cgi/scribble/794.atomFeature 5<b>strokeColor</b>: red<br /><b>title</b>: Feature 5<br /><b>author</b>: Your Name Here28.828125 32.6953125 49.921875 34.8046875 39.375 58.0078125 39.375 58.0078125 40.078125 58.0078125 41.484375 58.0078125 43.59375 58.0078125 45.703125 58.7109375 47.8125 58.7109375 49.21875 58.7109375 51.328125 59.4140625 52.03125 59.4140625 54.140625 60.8203125 56.25 61.5234375 56.25 62.2265625 57.65625 62.2265625 57.65625 62.9296875 58.359375 63.6328125 58.359375 65.0390625 58.359375 65.7421875 59.0625 66.4453125 59.0625 67.1484375 59.0625 68.5546875 59.765625 69.9609375 59.765625 72.0703125 59.765625 73.4765625 59.765625 76.2890625 59.765625 78.3984375 59.765625 79.8046875 59.765625 81.9140625 59.765625 83.3203125 59.0625 84.7265625 59.0625 86.8359375 58.359375 87.5390625 58.359375 88.2421875 56.953125 89.6484375 56.25 91.0546875 54.84375 93.8671875 52.03125 96.6796875 51.328125 98.7890625 50.625 100.1953125 49.21875 102.3046875 48.515625 103.7109375 47.8125 104.4140625 47.109375 105.8203125 46.40625 106.5234375 46.40625 107.9296875 45.703125 109.3359375 45 110.7421875 43.59375 112.8515625 43.59375 114.2578125 43.59375 114.9609375 42.890625 117.0703125 42.890625 117.7734375 42.1875 118.4765625 42.1875 119.1796875 42.1875 119.8828125' + ]; + var output= ['Feature 2<b>strokeColor</b>: red<br /><b>title</b>: Feature 2<br /><b>author</b>: Your Name Herehttp://featureserver.org/featureserver.cgi/scribble/562.atom-5.9765625 -131.484375 -58.0078125 -112.5 -50.2734375 -32.34375 52.3828125 -114.609375 -35.5078125 -167.34375 -57.3046875 -146.953125 -34.1015625 -139.921875 -5.9765625 -131.484375', + 'Feature 2<b>strokeColor</b>: 00ccff<br /><b>title</b>: Feature 2<br /><b>author</b>: Your Name Herehttp://featureserver.org/featureserver.cgi/scribble/796.atom75.5859375 15.46875', + 'Feature 5<b>strokeColor</b>: red<br /><b>title</b>: Feature 5<br /><b>author</b>: Your Name Herehttp://featureserver.org/featureserver.cgi/scribble/794.atom28.828125 32.6953125 49.921875 34.8046875 39.375 58.0078125 39.375 58.0078125 40.078125 58.0078125 41.484375 58.0078125 43.59375 58.0078125 45.703125 58.7109375 47.8125 58.7109375 49.21875 58.7109375 51.328125 59.4140625 52.03125 59.4140625 54.140625 60.8203125 56.25 61.5234375 56.25 62.2265625 57.65625 62.2265625 57.65625 62.9296875 58.359375 63.6328125 58.359375 65.0390625 58.359375 65.7421875 59.0625 66.4453125 59.0625 67.1484375 59.0625 68.5546875 59.765625 69.9609375 59.765625 72.0703125 59.765625 73.4765625 59.765625 76.2890625 59.765625 78.3984375 59.765625 79.8046875 59.765625 81.9140625 59.765625 83.3203125 59.0625 84.7265625 59.0625 86.8359375 58.359375 87.5390625 58.359375 88.2421875 56.953125 89.6484375 56.25 91.0546875 54.84375 93.8671875 52.03125 96.6796875 51.328125 98.7890625 50.625 100.1953125 49.21875 102.3046875 48.515625 103.7109375 47.8125 104.4140625 47.109375 105.8203125 46.40625 106.5234375 46.40625 107.9296875 45.703125 109.3359375 45 110.7421875 43.59375 112.8515625 43.59375 114.2578125 43.59375 114.9609375 42.890625 117.0703125 42.890625 117.7734375 42.1875 118.4765625 42.1875 119.1796875 42.1875 119.8828125'];