diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index 634f370853..b5d924a467 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -229,6 +229,7 @@ "OpenLayers/Rule.js", "OpenLayers/Format.js", "OpenLayers/Format/XML.js", + "OpenLayers/Format/Context.js", "OpenLayers/Format/ArcXML.js", "OpenLayers/Format/ArcXML/Features.js", "OpenLayers/Format/GML.js", @@ -256,6 +257,9 @@ "OpenLayers/Format/SLD.js", "OpenLayers/Format/SLD/v1.js", "OpenLayers/Format/SLD/v1_0_0.js", + "OpenLayers/Format/OWSCommon/v1.js", + "OpenLayers/Format/OWSCommon/v1_0_0.js", + "OpenLayers/Format/OWSCommon/v1_1_0.js", "OpenLayers/Format/CSWGetDomain.js", "OpenLayers/Format/CSWGetDomain/v2_0_2.js", "OpenLayers/Format/CSWGetRecords.js", @@ -279,11 +283,12 @@ "OpenLayers/Format/WMSCapabilities/v1_3.js", "OpenLayers/Format/WMSCapabilities/v1_3_0.js", "OpenLayers/Format/WMSGetFeatureInfo.js", - "OpenLayers/Format/OWSCommon/v1_1_0.js", "OpenLayers/Format/SOSCapabilities.js", "OpenLayers/Format/SOSCapabilities/v1_0_0.js", "OpenLayers/Format/SOSGetObservation.js", "OpenLayers/Format/SOSGetFeatureOfInterest.js", + "OpenLayers/Format/OWSContext.js", + "OpenLayers/Format/OWSContext/v0_3_1.js", "OpenLayers/Layer/WFS.js", "OpenLayers/Control/GetFeature.js", "OpenLayers/Control/MouseToolbar.js", diff --git a/lib/OpenLayers/Format/CSWGetRecords/v2_0_2.js b/lib/OpenLayers/Format/CSWGetRecords/v2_0_2.js index 2367c41637..aa0b8444c4 100644 --- a/lib/OpenLayers/Format/CSWGetRecords/v2_0_2.js +++ b/lib/OpenLayers/Format/CSWGetRecords/v2_0_2.js @@ -7,6 +7,7 @@ * @requires OpenLayers/Format/CSWGetRecords.js * @requires OpenLayers/Format/Filter/v1_0_0.js * @requires OpenLayers/Format/Filter/v1_1_0.js + * @requires OpenLayers/Format/OWSCommon/v1_0_0.js */ /** @@ -118,6 +119,17 @@ OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, */ Query: null, + /** + * Property: regExes + * Compiled regular expressions for manipulating strings. + */ + regExes: { + trimSpace: (/^\s*|\s*$/g), + removeSpace: (/\s*/g), + splitSpace: (/\s+/), + trimComma: (/\s*,\s*/g) + }, + /** * Constructor: OpenLayers.Format.CSWGetRecords.v2_0_2 * A class for parsing and generating CSWGetRecords v2.0.2 transactions. @@ -249,53 +261,24 @@ OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, obj[name].push(this.getChildValue(node)); } }, - "ows": { - "WGS84BoundingBox": function(node, obj) { - // LowerCorner = "min_x min_y" - // UpperCorner = "max_x max_y" - if (!(obj.BoundingBox instanceof Array)) { - obj.BoundingBox = new Array(); - } - //this.readChildNodes(node, bbox); - var lc = this.getChildValue( - this.getElementsByTagNameNS( - node, - this.namespaces["ows"], - "LowerCorner" - )[0] - ).split(' ', 2); - var uc = this.getChildValue( - this.getElementsByTagNameNS( - node, - this.namespaces["ows"], - "UpperCorner" - )[0] - ).split(' ', 2); - - var boundingBox = { - value: [ - parseFloat(lc[0]), - parseFloat(lc[1]), - parseFloat(uc[0]), - parseFloat(uc[1]) - ] - }; - // store boundingBox attributes - var attrs = node.attributes; - for(var i=0, len=attrs.length; i, the layers from the context document will be added + * to the map. + * + * Returns: + * {} A map based on the context. + */ + read: function(data, options) { + if(typeof data == "string") { + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); + } + var root = data.documentElement; + var version = this.version; + if(!version) { + version = root.getAttribute("version"); + } + var parser = this.getParser(version); + var context = parser.read(data, options); + var map; + if(options && options.map) { + this.context = context; + if(options.map instanceof OpenLayers.Map) { + map = this.mergeContextToMap(context, options.map); + } else { + var mapOptions = options.map; + if(OpenLayers.Util.isElement(mapOptions) || + typeof mapOptions == "string") { + // we assume mapOptions references a div + // element + mapOptions = {div: mapOptions}; + } + map = this.contextToMap(context, mapOptions); + } + } else { + // not documented as part of the API, provided as a non-API option + map = context; + } + return map; + }, + + /** + * Method: getLayerFromContext + * Create a WMS layer from a layerContext object. + * + * Parameters: + * layerContext - {Object} An object representing a WMS layer. + * + * Returns: + * {} A WMS layer. + */ + getLayerFromContext: function(layerContext) { + var i, len; + // fill initial options object from layerContext + var options = { + queryable: layerContext.queryable, //keep queryable for api compatibility + visibility: layerContext.visibility, + maxExtent: layerContext.maxExtent, + metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, + {styles: layerContext.styles}), + numZoomLevels: layerContext.numZoomLevels, + units: layerContext.units, + isBaseLayer: layerContext.isBaseLayer, + opacity: layerContext.opacity, + displayInLayerSwitcher: layerContext.displayInLayerSwitcher, + singleTile: layerContext.singleTile, + minScale: layerContext.minScale || layerContext.maxScaleDenominator, + maxScale: layerContext.maxScale || layerContext.minScaleDenominator + }; + if (this.layerOptions) { + OpenLayers.Util.applyDefaults(options, this.layerOptions); + } + + var params = { + layers: layerContext.name, + transparent: layerContext.transparent, + version: layerContext.version + }; + if (layerContext.formats && layerContext.formats.length>0) { + // set default value for params if current attribute is not positionned + params.format = layerContext.formats[0].value; + for (i=0, len=layerContext.formats.length; i0) { + for (i=0, len=layerContext.styles.length; i)} An array of layers. + */ + getLayersFromContext: function(layersContext) { + var layers = []; + for (var i=0, len=layersContext.length; i} A map based on the context object. + */ + contextToMap: function(context, options) { + options = OpenLayers.Util.applyDefaults({ + maxExtent: context.maxExtent, + projection: context.projection + }, options); + var map = new OpenLayers.Map(options); + map.addLayers(this.getLayersFromContext(context.layersContext)); + map.setCenter( + context.bounds.getCenterLonLat(), + map.getZoomForExtent(context.bounds, true) + ); + return map; + }, + + /** + * Method: mergeContextToMap + * Add layers from a context object to a map. + * + * Parameters: + * context - {Object} The context object. + * map - {} The map. + * + * Returns: + * {} The same map with layers added. + */ + mergeContextToMap: function(context, map) { + map.addLayers(this.getLayersFromContext(context.layersContext)); + return map; + }, + + /** + * APIMethod: write + * Write a context document given a map. + * + * Parameters: + * obj - { | Object} A map or context object. + * options - {Object} Optional configuration object. + * + * Returns: + * {String} A context document string. + */ + write: function(obj, options) { + obj = this.toContext(obj); + var version = options && options.version; + var parser = this.getParser(version); + var context = parser.write(obj, options); + return context; + }, + + CLASS_NAME: "OpenLayers.Format.Context" +}); + +/** + * Constant: OpenLayers.Format.Context.serviceTypes + * Enumeration for service types + */ +OpenLayers.Format.Context.serviceTypes = { + "WMS": "urn:ogc:serviceType:WMS", + "WFS": "urn:ogc:serviceType:WFS", + "WCS": "urn:ogc:serviceType:WCS", + "GML": "urn:ogc:serviceType:GML", + "SLD": "urn:ogc:serviceType:SLD", + "FES": "urn:ogc:serviceType:FES", + "KML": "urn:ogc:serviceType:KML" +}; diff --git a/lib/OpenLayers/Format/OWSCommon/v1.js b/lib/OpenLayers/Format/OWSCommon/v1.js new file mode 100644 index 0000000000..83e0e125d2 --- /dev/null +++ b/lib/OpenLayers/Format/OWSCommon/v1.js @@ -0,0 +1,252 @@ +/* Copyright (c) 2006-2010 MetaCarta, Inc., published under the Clear BSD + * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/XML.js + */ + +if (!OpenLayers.Format.OWSCommon) { + OpenLayers.Format.OWSCommon = {}; +} + +/** + * Class: OpenLayers.Format.OWSCommon.v1 + * Common readers and writers for OWSCommon v1.X formats + */ +OpenLayers.Format.OWSCommon.v1 = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * Property: namespaces + * {Object} Mapping of namespace aliases to namespace URIs. + */ + namespaces: { + xlink: "http://www.w3.org/1999/xlink" + }, + + /** + * Property: readers + * Contains public functions, grouped by namespace prefix, that will + * be applied when a namespaced node is found matching the function + * name. The function will be applied in the scope of this parser + * with two arguments: the node being read and a context object passed + * from the parent. + */ + readers: { + "ows": { + "ServiceIdentification": function(node, obj) { + obj.serviceIdentification = {}; + this.readChildNodes(node, obj.serviceIdentification); + }, + "Title": function(node, obj) { + obj.title = this.getChildValue(node); + }, + "Abstract": function(node, serviceIdentification) { + serviceIdentification["abstract"] = this.getChildValue(node); + }, + "Keywords": function(node, serviceIdentification) { + serviceIdentification.keywords = {}; + this.readChildNodes(node, serviceIdentification.keywords); + }, + "Keyword": function(node, keywords) { + keywords[this.getChildValue(node)] = true; + }, + "ServiceType": function(node, serviceIdentification) { + serviceIdentification.serviceType = { + codeSpace: node.getAttribute('codeSpace'), + value: this.getChildValue(node)}; + }, + "ServiceTypeVersion": function(node, serviceIdentification) { + serviceIdentification.serviceTypeVersion = this.getChildValue(node); + }, + "Fees": function(node, serviceIdentification) { + serviceIdentification.fees = this.getChildValue(node); + }, + "AccessConstraints": function(node, serviceIdentification) { + serviceIdentification.accessConstraints = + this.getChildValue(node); + }, + "ServiceProvider": function(node, obj) { + obj.serviceProvider = {}; + this.readChildNodes(node, obj.serviceProvider); + }, + "ProviderName": function(node, serviceProvider) { + serviceProvider.providerName = this.getChildValue(node); + }, + "ProviderSite": function(node, serviceProvider) { + serviceProvider.providerSite = this.getAttributeNS(node, + this.namespaces.xlink, "href"); + }, + "ServiceContact": function(node, serviceProvider) { + serviceProvider.serviceContact = {}; + this.readChildNodes(node, serviceProvider.serviceContact); + }, + "IndividualName": function(node, serviceContact) { + serviceContact.individualName = this.getChildValue(node); + }, + "PositionName": function(node, serviceContact) { + serviceContact.positionName = this.getChildValue(node); + }, + "ContactInfo": function(node, serviceContact) { + serviceContact.contactInfo = {}; + this.readChildNodes(node, serviceContact.contactInfo); + }, + "Phone": function(node, contactInfo) { + contactInfo.phone = {}; + this.readChildNodes(node, contactInfo.phone); + }, + "Voice": function(node, phone) { + phone.voice = this.getChildValue(node); + }, + "Address": function(node, contactInfo) { + contactInfo.address = {}; + this.readChildNodes(node, contactInfo.address); + }, + "DeliveryPoint": function(node, address) { + address.deliveryPoint = this.getChildValue(node); + }, + "City": function(node, address) { + address.city = this.getChildValue(node); + }, + "AdministrativeArea": function(node, address) { + address.administrativeArea = this.getChildValue(node); + }, + "PostalCode": function(node, address) { + address.postalCode = this.getChildValue(node); + }, + "Country": function(node, address) { + address.country = this.getChildValue(node); + }, + "ElectronicMailAddress": function(node, address) { + address.electronicMailAddress = this.getChildValue(node); + }, + "Role": function(node, serviceContact) { + serviceContact.role = this.getChildValue(node); + }, + "OperationsMetadata": function(node, obj) { + obj.operationsMetadata = {}; + this.readChildNodes(node, obj.operationsMetadata); + }, + "Operation": function(node, operationsMetadata) { + var name = node.getAttribute("name"); + operationsMetadata[name] = {}; + this.readChildNodes(node, operationsMetadata[name]); + }, + "DCP": function(node, operation) { + operation.dcp = {}; + this.readChildNodes(node, operation.dcp); + }, + "HTTP": function(node, dcp) { + dcp.http = {}; + this.readChildNodes(node, dcp.http); + }, + "Get": function(node, http) { + http.get = this.getAttributeNS(node, + this.namespaces.xlink, "href"); + }, + "Post": function(node, http) { + http.post = this.getAttributeNS(node, + this.namespaces.xlink, "href"); + }, + "Parameter": function(node, operation) { + if (!operation.parameters) { + operation.parameters = {}; + } + var name = node.getAttribute("name"); + operation.parameters[name] = {}; + this.readChildNodes(node, operation.parameters[name]); + }, + "Value": function(node, allowedValues) { + allowedValues[this.getChildValue(node)] = true; + }, + "OutputFormat": function(node, obj) { + obj.formats.push({value: this.getChildValue(node)}); + this.readChildNodes(node, obj); + }, + "WGS84BoundingBox": function(node, obj) { + var boundingBox = {}; + boundingBox.crs = node.getAttribute("crs"); + if (obj.BoundingBox) { + obj.BoundingBox.push(boundingBox); + } else { + obj.projection = boundingBox.crs; + boundingBox = obj; + } + this.readChildNodes(node, boundingBox); + }, + "BoundingBox": function(node, obj) { + // FIXME: We consider that BoundingBox is the same as WGS84BoundingBox + // LowerCorner = "min_x min_y" + // UpperCorner = "max_x max_y" + // It should normally depend on the projection + this.readers['ows']['WGS84BoundingBox'].apply(this, [node, obj]); + }, + "LowerCorner": function(node, obj) { + var str = this.getChildValue(node).replace( + this.regExes.trimSpace, ""); + str = str.replace(this.regExes.trimComma, ","); + var pointList = str.split(this.regExes.splitSpace); + obj.left = pointList[0]; + obj.bottom = pointList[1]; + }, + "UpperCorner": function(node, obj) { + var str = this.getChildValue(node).replace( + this.regExes.trimSpace, ""); + str = str.replace(this.regExes.trimComma, ","); + var pointList = str.split(this.regExes.splitSpace); + obj.right = pointList[0]; + obj.top = pointList[1]; + obj.bounds = new OpenLayers.Bounds(obj.left, obj.bottom, + obj.right, obj.top); + delete obj.left; + delete obj.bottom; + delete obj.right; + delete obj.top; + } + } + }, + + /** + * Property: writers + * As a compliment to the readers property, this structure contains public + * writing functions grouped by namespace alias and named like the + * node names they produce. + */ + writers: { + "ows": { + "BoundingBox": function(options) { + var node = this.createElementNSPlus("ows:BoundingBox", { + attributes: { + crs: options.projection + } + }); + this.writeNode("ows:LowerCorner", options, node); + this.writeNode("ows:UpperCorner", options, node); + return node; + }, + "LowerCorner": function(options) { + var node = this.createElementNSPlus("ows:LowerCorner", { + value: options.bounds.left + " " + options.bounds.bottom }); + return node; + }, + "UpperCorner": function(options) { + var node = this.createElementNSPlus("ows:UpperCorner", { + value: options.bounds.right + " " + options.bounds.top }); + return node; + }, + "Title": function(title) { + var node = this.createElementNSPlus("ows:Title", { + value: title }); + return node; + }, + "OutputFormat": function(format) { + var node = this.createElementNSPlus("ows:OutputFormat", { + value: format }); + return node; + } + } + }, + + CLASS_NAME: "OpenLayers.Format.OWSCommon.v1" + +}); diff --git a/lib/OpenLayers/Format/OWSCommon/v1_0_0.js b/lib/OpenLayers/Format/OWSCommon/v1_0_0.js new file mode 100644 index 0000000000..b767340031 --- /dev/null +++ b/lib/OpenLayers/Format/OWSCommon/v1_0_0.js @@ -0,0 +1,40 @@ +/* Copyright (c) 2006-2009 MetaCarta, Inc., published under the Clear BSD + * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/OWSCommon/v1.js + */ + +/** + * Class: OpenLayers.Format.OWSCommon.v1_0_0 + * Parser for OWS Common version 1.0.0 which can be used by other parsers. + * It is not intended to be used on its own. + */ +OpenLayers.Format.OWSCommon.v1_0_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, { + + /** + * Property: readers + * Contains public functions, grouped by namespace prefix, that will + * be applied when a namespaced node is found matching the function + * name. The function will be applied in the scope of this parser + * with two arguments: the node being read and a context object passed + * from the parent. + */ + readers: { + "ows": OpenLayers.Format.OWSCommon.v1.prototype.readers["ows"] + }, + + /** + * Property: writers + * As a compliment to the readers property, this structure contains public + * writing functions grouped by namespace alias and named like the + * node names they produce. + */ + writers: { + "ows": OpenLayers.Format.OWSCommon.v1.prototype.writers["ows"] + }, + + CLASS_NAME: "OpenLayers.Format.OWSCommon.v1_1_0" + +}); diff --git a/lib/OpenLayers/Format/OWSCommon/v1_1_0.js b/lib/OpenLayers/Format/OWSCommon/v1_1_0.js index cf1c2708bb..c8423633d5 100644 --- a/lib/OpenLayers/Format/OWSCommon/v1_1_0.js +++ b/lib/OpenLayers/Format/OWSCommon/v1_1_0.js @@ -3,30 +3,16 @@ * full text of the license. */ /** - * @requires OpenLayers/Format/XML.js + * @requires OpenLayers/Format/OWSCommon/v1.js */ -OpenLayers.Format.OWSCommon = {}; - /** * Class: OpenLayers.Format.OWSCommon.v1_1_0 * Parser for OWS Common version 1.1.0 which can be used by other parsers. * It is not intended to be used on its own. - * - * Inherits from: - * - */ -OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.XML, { +OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, { - /** - * Property: namespaces - * {Object} Mapping of namespace aliases to namespace URIs. - */ - namespaces: { - ows: "http://www.opengis.net/ows/1.1", - xlink: "http://www.w3.org/1999/xlink" - }, - /** * Property: readers * Contains public functions, grouped by namespace prefix, that will @@ -36,129 +22,7 @@ OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.XML, { * from the parent. */ readers: { - "ows": { - "ServiceIdentification": function(node, obj) { - obj.serviceIdentification = {}; - this.readChildNodes(node, obj.serviceIdentification); - }, - "Title": function(node, serviceIdentification) { - serviceIdentification.title = this.getChildValue(node); - }, - "Abstract": function(node, serviceIdentification) { - serviceIdentification["abstract"] = this.getChildValue(node); - }, - "Keywords": function(node, serviceIdentification) { - serviceIdentification.keywords = {}; - this.readChildNodes(node, serviceIdentification.keywords); - }, - "Keyword": function(node, keywords) { - keywords[this.getChildValue(node)] = true; - }, - "ServiceType": function(node, serviceIdentification) { - serviceIdentification.serviceType = { - codeSpace: node.getAttribute('codeSpace'), - value: this.getChildValue(node)}; - }, - "ServiceTypeVersion": function(node, serviceIdentification) { - serviceIdentification.serviceTypeVersion = this.getChildValue(node); - }, - "Fees": function(node, serviceIdentification) { - serviceIdentification.fees = this.getChildValue(node); - }, - "AccessConstraints": function(node, serviceIdentification) { - serviceIdentification.accessConstraints = - this.getChildValue(node); - }, - "ServiceProvider": function(node, obj) { - obj.serviceProvider = {}; - this.readChildNodes(node, obj.serviceProvider); - }, - "ProviderName": function(node, serviceProvider) { - serviceProvider.providerName = this.getChildValue(node); - }, - "ProviderSite": function(node, serviceProvider) { - serviceProvider.providerSite = this.getAttributeNS(node, - this.namespaces.xlink, "href"); - }, - "ServiceContact": function(node, serviceProvider) { - serviceProvider.serviceContact = {}; - this.readChildNodes(node, serviceProvider.serviceContact); - }, - "IndividualName": function(node, serviceContact) { - serviceContact.individualName = this.getChildValue(node); - }, - "PositionName": function(node, serviceContact) { - serviceContact.positionName = this.getChildValue(node); - }, - "ContactInfo": function(node, serviceContact) { - serviceContact.contactInfo = {}; - this.readChildNodes(node, serviceContact.contactInfo); - }, - "Phone": function(node, contactInfo) { - contactInfo.phone = {}; - this.readChildNodes(node, contactInfo.phone); - }, - "Voice": function(node, phone) { - phone.voice = this.getChildValue(node); - }, - "Address": function(node, contactInfo) { - contactInfo.address = {}; - this.readChildNodes(node, contactInfo.address); - }, - "DeliveryPoint": function(node, address) { - address.deliveryPoint = this.getChildValue(node); - }, - "City": function(node, address) { - address.city = this.getChildValue(node); - }, - "AdministrativeArea": function(node, address) { - address.administrativeArea = this.getChildValue(node); - }, - "PostalCode": function(node, address) { - address.postalCode = this.getChildValue(node); - }, - "Country": function(node, address) { - address.country = this.getChildValue(node); - }, - "ElectronicMailAddress": function(node, address) { - address.electronicMailAddress = this.getChildValue(node); - }, - "Role": function(node, serviceContact) { - serviceContact.role = this.getChildValue(node); - }, - "OperationsMetadata": function(node, obj) { - obj.operationsMetadata = {}; - this.readChildNodes(node, obj.operationsMetadata); - }, - "Operation": function(node, operationsMetadata) { - var name = node.getAttribute("name"); - operationsMetadata[name] = {}; - this.readChildNodes(node, operationsMetadata[name]); - }, - "DCP": function(node, operation) { - operation.dcp = {}; - this.readChildNodes(node, operation.dcp); - }, - "HTTP": function(node, dcp) { - dcp.http = {}; - this.readChildNodes(node, dcp.http); - }, - "Get": function(node, http) { - http.get = this.getAttributeNS(node, - this.namespaces.xlink, "href"); - }, - "Post": function(node, http) { - http.post = this.getAttributeNS(node, - this.namespaces.xlink, "href"); - }, - "Parameter": function(node, operation) { - if (!operation.parameters) { - operation.parameters = {}; - } - var name = node.getAttribute("name"); - operation.parameters[name] = {}; - this.readChildNodes(node, operation.parameters[name]); - }, + "ows": OpenLayers.Util.applyDefaults({ "AllowedValues": function(node, parameter) { parameter.allowedValues = {}; this.readChildNodes(node, parameter.allowedValues); @@ -166,9 +30,6 @@ OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.XML, { "AnyValue": function(node, parameter) { parameter.anyValue = true; }, - "Value": function(node, allowedValues) { - allowedValues[this.getChildValue(node)] = true; - }, "Range": function(node, allowedValues) { allowedValues.range = {}; this.readChildNodes(node, allowedValues.range); @@ -179,7 +40,7 @@ OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.XML, { "MaximumValue": function(node, range) { range.maxValue = this.getChildValue(node); } - } + }, OpenLayers.Format.OWSCommon.v1.prototype.readers["ows"]) }, CLASS_NAME: "OpenLayers.Format.OWSCommon.v1_1_0" diff --git a/lib/OpenLayers/Format/OWSContext.js b/lib/OpenLayers/Format/OWSContext.js new file mode 100644 index 0000000000..53b4ab8b5d --- /dev/null +++ b/lib/OpenLayers/Format/OWSContext.js @@ -0,0 +1,80 @@ +/* Copyright (c) 2006-2010 MetaCarta, Inc., published under the Clear BSD + * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/Context.js + */ + +/** + * Class: OpenLayers.Format.OWSContext + * Read and write OWS Context documents. OWS Context documents are a + * preliminary OGC (Open Geospatial Consortium) standard for storing the + * state of a web mapping application. In a way it is the successor to + * Web Map Context (WMC), since it is more generic and more types of layers + * can be stored. Also, nesting of layers is supported since version 0.3.1. + * For more information see: http://www.ogcnetwork.net/context + */ +OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context,{ + + /** + * APIProperty: defaultVersion + * {String} Version number to assume if none found. Default is "0.3.1". + */ + defaultVersion: "0.3.1", + + /** + * Method: getParser + * Get the OWSContext parser given a version. Create a new parser if it does not + * already exist. + * + * Parameters: + * version - {String} The version of the parser. + * + * Returns: + * {} An OWSContext parser. + */ + getParser: function(version) { + var v = version || this.version || this.defaultVersion; + // 0.3.1 is backwards compatible with 0.3.0 + if (v === "0.3.0") { + v = this.defaultVersion; + } + if(!this.parser || this.parser.VERSION != v) { + var format = OpenLayers.Format.OWSContext[ + "v" + v.replace(/\./g, "_") + ]; + if(!format) { + throw "Can't find a OWSContext parser for version " + v; + } + this.parser = new format(this.options); + } + return this.parser; + }, + + /** + * Method: toContext + * Create a context object free from layer given a map or a + * context object. + * + * Parameters: + * obj - { | Object} The map or context. + * + * Returns: + * {Object} A context object. + */ + toContext: function(obj) { + var context = {}; + if(obj.CLASS_NAME == "OpenLayers.Map") { + context.bounds = obj.getExtent(); + context.maxExtent = obj.maxExtent; + context.projection = obj.projection; + context.size = obj.getSize(); + context.layers = obj.layers; + } + return context; + }, + + CLASS_NAME: "OpenLayers.Format.OWSContext" + +}); diff --git a/lib/OpenLayers/Format/OWSContext/v0_3_1.js b/lib/OpenLayers/Format/OWSContext/v0_3_1.js new file mode 100644 index 0000000000..3f7e7a797b --- /dev/null +++ b/lib/OpenLayers/Format/OWSContext/v0_3_1.js @@ -0,0 +1,588 @@ +/* Copyright (c) 2006-2010 MetaCarta, Inc., published under the Clear BSD + * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/XML.js + * @requires OpenLayers/Format/KML.js + * @requires OpenLayers/Format/GML.js + * @requires OpenLayers/Format/GML/v2.js + * @requires OpenLayers/Format/SLD/v1_0_0.js + * @requires OpenLayers/Format/OWSContext.js + * @requires OpenLayers/Format/OWSCommon/v1_0_0.js + */ + +/** + * Class: OpenLayers.Format.OWSContext.v0_3_1 + * Read and write OWSContext version 0.3.1. + * + * Inherits from: + * - + */ +OpenLayers.Format.OWSContext.v0_3_1 = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * Property: namespaces + * {Object} Mapping of namespace aliases to namespace URIs. + */ + namespaces: { + owc: "http://www.opengis.net/ows-context", + gml: "http://www.opengis.net/gml", + kml: "http://www.opengis.net/kml/2.2", + ogc: "http://www.opengis.net/ogc", + ows: "http://www.opengis.net/ows", + sld: "http://www.opengis.net/sld", + xlink: "http://www.w3.org/1999/xlink", + xsi: "http://www.w3.org/2001/XMLSchema-instance" + }, + + /** + * Constant: VERSION + * {String} 0.3.1 + */ + VERSION: "0.3.1", + + /** + * Property: schemaLocation + * {String} Schema location + */ + schemaLocation: "http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd", + + /** + * Property: defaultPrefix + * {String} Default namespace prefix to use. + */ + defaultPrefix: "owc", + + /** + * APIProperty: extractAttributes + * {Boolean} Extract attributes from GML. Default is true. + */ + extractAttributes: true, + + /** + * APIProperty: xy + * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x) + * Changing is not recommended, a new Format should be instantiated. + */ + xy: true, + + /** + * Property: regExes + * Compiled regular expressions for manipulating strings. + */ + regExes: { + trimSpace: (/^\s*|\s*$/g), + removeSpace: (/\s*/g), + splitSpace: (/\s+/), + trimComma: (/\s*,\s*/g) + }, + + /** + * Property: featureNS + * {String} The namespace uri to use for writing InlineGeometry + */ + featureNS: "http://mapserver.gis.umn.edu/mapserver", + + /** + * Property: featureType + * {String} The name to use as the feature type when writing out + * InlineGeometry + */ + featureType: 'vector', + + /** + * Property: geometryName + * {String} The name to use for the geometry attribute when writing out + * InlineGeometry + */ + geometryName: 'geometry', + + /** + * Property: nestingLayerLookup + * {Object} Hashtable lookup for nesting layer nodes. Used while writing + * the OWS context document. It is necessary to keep track of the + * nestingPaths for which nesting layer nodes have already been + * created, so (nesting) layer nodes are added to those nodes. + * + * For example: + * + * If there are three layers with nestingPaths: + * layer1.metadata.nestingPath = "a/b/" + * layer2.metadata.nestingPath = "a/b/" + * layer2.metadata.nestingPath = "a/c" + * + * then a nesting layer node "a" should be created once and added + * to the resource list, a nesting layer node "b" should be created + * once and added under "a", and a nesting layer node "c" should be + * created and added under "a". The lookup paths for these nodes + * will be "a", "a/b", and "a/c" respectively. + */ + nestingLayerLookup: null, + + /** + * Constructor: OpenLayers.Format.OWSContext.v0_3_1 + * Instances of this class are not created directly. Use the + * constructor instead. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + initialize: function(options) { + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); + OpenLayers.Format.GML.v2.prototype.setGeometryTypes.call(this); + }, + + /** + * Method: setNestingPath + * Set the nestingPath property of the layer depending on the position + * of the layer in hierarchy of layers. + * + * Parameters: + * l - {Object} An object that may have a layersContext array property. + * + */ + setNestingPath : function(l){ + if(l.layersContext){ + for (var i = 0, len = l.layersContext.length; i < len; i++) { + var layerContext = l.layersContext[i]; + var nPath = []; + var nTitle = l.title || ""; + if(l.metadata && l.metadata.nestingPath){ + nPath = l.metadata.nestingPath.slice(); + } + if (nTitle != "") { + nPath.push(nTitle); + } + layerContext.metadata.nestingPath = nPath; + if(layerContext.layersContext){ + this.setNestingPath(layerContext); + } + } + } + }, + + /** + * Function: decomposeNestingPath + * Takes a nestingPath like "a/b/c" and decomposes it into subpaths: + * "a", "a/b", "a/b/c" + * + * Parameters: + * nPath - {Array} the nesting path + * + * Returns: + * Array({String}) Array with subpaths, or empty array if there is nothing + * to decompose + */ + decomposeNestingPath: function(nPath){ + var a = []; + if (nPath instanceof Array) { + while (nPath.length > 0) { + a.push(nPath.slice()); + nPath.pop(); + } + a.reverse(); + } + return a; + }, + + /** + * APIMethod: read + * Read OWS context data from a string or DOMElement, and return a list + * of layers. + * + * Parameters: + * data - {String} or {DOMElement} data to read/parse. + * + * Returns: + * {Object} The context object with a flat layer list as a property named + * layersContext. + */ + read: function(data) { + if(typeof data == "string") { + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); + } + if(data && data.nodeType == 9) { + data = data.documentElement; + } + var context = {}; + this.readNode(data, context); + // since an OWSContext can be nested we need to go through this + // structure recursively + this.setNestingPath({layersContext : context.layersContext}); + // after nesting path has been set, create a flat list of layers + var layers = []; + this.processLayer(layers, context); + delete context.layersContext; + context.layersContext = layers; + return context; + }, + + /** + * Method: processLayer + * Recursive function to get back a flat list of layers from the hierarchic + * layer structure. + * + * Parameters: + * layerArray - {Array({Object})} Array of layerContext objects + * layerContext - {Object} layerContext object + */ + processLayer: function(layerArray, layer) { + if (layer.layersContext) { + for (var i=0, len = layer.layersContext.length; i= 1) { + el = elements[0]; + } + if (el && el.firstChild) { + var featurenode = (el.firstChild.nextSibling) ? + el.firstChild.nextSibling : el.firstChild; + this.setNamespace("feature", featurenode.namespaceURI); + this.featureType = featurenode.localName || + featurenode.nodeName.split(":").pop(); + this.readChildNodes(node, obj); + } + }, + "Server": function(node, obj) { + // when having multiple Server types, we prefer WMS + if ((!obj.service && !obj.version) || + (obj.service != + OpenLayers.Format.Context.serviceTypes.WMS)) { + obj.service = node.getAttribute("service"); + obj.version = node.getAttribute("version"); + this.readChildNodes(node, obj); + } + }, + "Name": function(node, obj) { + obj.name = this.getChildValue(node); + this.readChildNodes(node, obj); + }, + "Title": function(node, obj) { + obj.title = this.getChildValue(node); + this.readChildNodes(node, obj); + }, + "StyleList": function(node, obj) { + this.readChildNodes(node, obj.styles); + }, + "Style": function(node, obj) { + var style = {}; + obj.push(style); + this.readChildNodes(node, style); + }, + "LegendURL": function(node, obj) { + var legend = {}; + obj.legend = legend; + this.readChildNodes(node, legend); + }, + "OnlineResource": function(node, obj) { + obj.url = this.getAttributeNS(node, this.namespaces.xlink, + "href"); + this.readChildNodes(node, obj); + } + }, + "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers.ows, + "gml": OpenLayers.Format.GML.v2.prototype.readers.gml, + "sld": OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld, + "feature": OpenLayers.Format.GML.v2.prototype.readers.feature + }, + + /** + * Property: writers + * As a compliment to the readers property, this structure contains public + * writing functions grouped by namespace alias and named like the + * node names they produce. + */ + writers: { + "owc": { + "OWSContext": function(options) { + var node = this.createElementNSPlus("OWSContext", { + attributes: { + version: this.VERSION, + id: options.id || OpenLayers.Util.createUniqueID("OpenLayers_OWSContext_") + } + }); + this.writeNode("General", options, node); + this.writeNode("ResourceList", options, node); + return node; + }, + "General": function(options) { + var node = this.createElementNSPlus("General"); + this.writeNode("ows:BoundingBox", options, node); + this.writeNode("ows:Title", options.title || 'OpenLayers OWSContext', node); + return node; + }, + "ResourceList": function(options) { + var node = this.createElementNSPlus("ResourceList"); + for (var i=0, len=options.layers.length; i 0) { + this.writeNode("StyleList", layer.metadata.styles, node); + } + return node; + }, + "_Layer": function(options) { + var layer, subPaths, node, title; + layer = options.layer; + subPaths = options.subPaths; + node = null; + title = null; + // subPaths is an array of an array + // recursively calling _Layer writer eats up subPaths, until a + // real writer is called and nodes are returned. + if(subPaths.length > 0){ + var path = subPaths[0].join("/"); + var index = path.lastIndexOf("/"); + node = this.nestingLayerLookup[path]; + title = (index > 0)?path.substring(index + 1, path.length):path; + if(!node){ + // category layer + node = this.createElementNSPlus("Layer"); + this.writeNode("ows:Title", title, node); + this.nestingLayerLookup[path] = node; + } + options.subPaths.shift();//remove a path after each call + this.writeNode("_Layer", options, node); + return node; + } else { + // write out the actual layer + if (layer instanceof OpenLayers.Layer.WMS) { + node = this.writeNode("_WMS", layer); + } else if (layer instanceof OpenLayers.Layer.Vector) { + if (layer.protocol instanceof OpenLayers.Protocol.WFS.v1) { + node = this.writeNode("_WFS", layer); + } else if (layer.protocol instanceof OpenLayers.Protocol.HTTP) { + if (layer.protocol.format instanceof OpenLayers.Format.GML) { + layer.protocol.format.version = "2.1.2"; + node = this.writeNode("_GML", layer); + } else if (layer.protocol.format instanceof OpenLayers.Format.KML) { + layer.protocol.format.version = "2.2"; + node = this.writeNode("_KML", layer); + } + } else { + // write out as inline GML since we have no idea + // about the original Format + this.setNamespace("feature", this.featureNS); + node = this.writeNode("_InlineGeometry", layer); + } + } + if (layer.options.maxScale) { + this.writeNode("sld:MinScaleDenominator", + layer.options.maxScale, node); + } + if (layer.options.minScale) { + this.writeNode("sld:MaxScaleDenominator", + layer.options.minScale, node); + } + this.nestingLayerLookup[layer.name] = node; + return node; + } + }, + "_WFS": function(layer) { + var node = this.createElementNSPlus("Layer", {attributes: { + name: layer.protocol.featurePrefix + ":" + layer.protocol.featureType, + hidden: layer.visibility ? "0" : "1" } + }); + this.writeNode("ows:Title", layer.name, node); + this.writeNode("Server", {service: + OpenLayers.Format.Context.serviceTypes.WFS, + version: layer.protocol.version, + url: layer.protocol.url}, node); + return node; + }, + "_InlineGeometry": function(layer) { + var node = this.createElementNSPlus("Layer", {attributes: { + name: this.featureType, + hidden: layer.visibility ? "0" : "1" } + }); + this.writeNode("ows:Title", layer.name, node); + this.writeNode("InlineGeometry", layer, node); + return node; + }, + "_GML": function(layer) { + var node = this.createElementNSPlus("Layer"); + this.writeNode("ows:Title", layer.name, node); + this.writeNode("Server", {service: + OpenLayers.Format.Context.serviceTypes.GML, + url: layer.protocol.url, version: + layer.protocol.format.version}, node); + return node; + }, + "_KML": function(layer) { + var node = this.createElementNSPlus("Layer"); + this.writeNode("ows:Title", layer.name, node); + this.writeNode("Server", {service: + OpenLayers.Format.Context.serviceTypes.KML, + version: layer.protocol.format.version, url: + layer.protocol.url}, node); + return node; + } + }, + "gml": OpenLayers.Util.applyDefaults({ + "boundedBy": function(bounds) { + var node = this.createElementNSPlus("gml:boundedBy"); + this.writeNode("gml:Box", bounds, node); + return node; + } + }, OpenLayers.Format.GML.v2.prototype.writers.gml), + "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.writers.ows, + "sld": OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld, + "feature": OpenLayers.Format.GML.v2.prototype.writers.feature + }, + + CLASS_NAME: "OpenLayers.Format.OWSContext.v0_3_1" + +}); diff --git a/lib/OpenLayers/Format/WMC.js b/lib/OpenLayers/Format/WMC.js index af1b1b48d9..40722843b1 100644 --- a/lib/OpenLayers/Format/WMC.js +++ b/lib/OpenLayers/Format/WMC.js @@ -4,6 +4,7 @@ /** * @requires OpenLayers/Format/XML.js + * @requires OpenLayers/Format/Context.js */ /** @@ -13,7 +14,7 @@ * Inherits from: * - */ -OpenLayers.Format.WMC = OpenLayers.Class({ +OpenLayers.Format.WMC = OpenLayers.Class(OpenLayers.Format.Context, { /** * APIProperty: defaultVersion @@ -21,98 +22,6 @@ OpenLayers.Format.WMC = OpenLayers.Class({ */ defaultVersion: "1.1.0", - /** - * APIProperty: version - * {String} Specify a version string if one is known. - */ - version: null, - - /** - * Property: layerOptions - * {Object} Default options for layers created by the parser. These - * options are overridden by the options which are read from the - * capabilities document. - */ - layerOptions: null, - - /** - * Property: layerParams - * {Object} Default parameters for layers created by the parser. This - * can be used to override DEFAULT_PARAMS for OpenLayers.Layer.WMS. - */ - layerParams: null, - - /** - * Property: parser - * {Object} Instance of the versioned parser. Cached for multiple read and - * write calls of the same version. - */ - parser: null, - - /** - * Constructor: OpenLayers.Format.WMC - * Create a new parser for WMC docs. - * - * Parameters: - * options - {Object} An optional object whose properties will be set on - * this instance. - */ - initialize: function(options) { - OpenLayers.Util.extend(this, options); - this.options = options; - }, - - /** - * APIMethod: read - * Read WMC data from a string, and return an object with map properties - * and a list of layers. - * - * Parameters: - * data - {String} or {DOMElement} data to read/parse. - * options - {Object} The options object must contain a map property. If - * the map property is a string, it must be the id of a dom element - * where the new map will be placed. If the map property is an - * , the layers from the context document will be added - * to the map. If the map property is an object, this will be - * considered as options to create the map with, in most cases, it would - * have a div property. - * - * Returns: - * {} A map based on the context. - */ - read: function(data, options) { - if(typeof data == "string") { - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); - } - var root = data.documentElement; - var version = this.version; - if(!version) { - version = root.getAttribute("version"); - } - var parser = this.getParser(version); - var context = parser.read(data, options); - var map; - if(options && options.map) { - this.context = context; - if(options.map instanceof OpenLayers.Map) { - map = this.mergeContextToMap(context, options.map); - } else { - var mapOptions = options.map; - if(OpenLayers.Util.isElement(mapOptions) || - typeof mapOptions == "string") { - // we assume mapOptions references a div - // element - mapOptions = {div: mapOptions}; - } - map = this.contextToMap(context, mapOptions); - } - } else { - // not documented as part of the API, provided as a non-API option - map = context; - } - return map; - }, - /** * Method: getParser * Get the WMC parser given a version. Create a new parser if it does not @@ -138,162 +47,6 @@ OpenLayers.Format.WMC = OpenLayers.Class({ return this.parser; }, - /** - * Method: getLayerFromContext - * Create a WMS layer from a layerContext object. - * - * Parameters: - * layerContext - {Object} An object representing a WMS layer. - * - * Returns: - * {} A WMS layer. - */ - getLayerFromContext: function(layerContext) { - var i, len; - // fill initial options object from layerContext - var options = { - queryable: layerContext.queryable, //keep queryable for api compatibility - visibility: layerContext.visibility, - maxExtent: layerContext.maxExtent, - numZoomLevels: layerContext.numZoomLevels, - units: layerContext.units, - isBaseLayer: layerContext.isBaseLayer, - opacity: layerContext.opacity, - displayInLayerSwitcher: layerContext.displayInLayerSwitcher, - singleTile: layerContext.singleTile, - minScale: layerContext.minScale, - maxScale: layerContext.maxScale - }; - if (this.layerOptions) { - OpenLayers.Util.applyDefaults(options, this.layerOptions); - } - - var params = { - layers: layerContext.name, - transparent: layerContext.transparent, - version: layerContext.version - }; - if (layerContext.formats && layerContext.formats.length>0) { - // set default value for params if current attribute is not positionned - params.format = layerContext.formats[0].value; - for (i=0, len=layerContext.formats.length; i0) { - for (i=0, len=layerContext.styles.length; i)} An array of WMS layers. - */ - getLayersFromContext: function(layersContext) { - var layers = []; - for (var i=0, len=layersContext.length; i} A map based on the context object. - */ - contextToMap: function(context, options) { - options = OpenLayers.Util.applyDefaults({ - maxExtent: context.maxExtent, - projection: context.projection - }, options); - var map = new OpenLayers.Map(options); - map.addLayers(this.getLayersFromContext(context.layersContext)); - map.setCenter( - context.bounds.getCenterLonLat(), - map.getZoomForExtent(context.bounds, true) - ); - return map; - }, - - /** - * Method: mergeContextToMap - * Add layers from a context object to a map. - * - * Parameters: - * context - {Object} The context object. - * map - {} The map. - * - * Returns: - * {} The same map with layers added. - */ - mergeContextToMap: function(context, map) { - map.addLayers(this.getLayersFromContext(context.layersContext)); - return map; - }, - - /** - * APIMethod: write - * Write a WMC document given a map. - * - * Parameters: - * obj - { | Object} A map or context object. - * options - {Object} Optional configuration object. - * - * Returns: - * {String} A WMC document string. - */ - write: function(obj, options) { - obj = this.toContext(obj); - var version = options && options.version; - var parser = this.getParser(version); - var wmc = parser.write(obj, options); - return wmc; - }, - /** * Method: layerToContext * Create a layer context object given a wms layer object. diff --git a/lib/OpenLayers/Layer.js b/lib/OpenLayers/Layer.js index 7b61f19054..de33a663ce 100644 --- a/lib/OpenLayers/Layer.js +++ b/lib/OpenLayers/Layer.js @@ -301,6 +301,13 @@ OpenLayers.Layer = OpenLayers.Class({ */ SUPPORTED_TRANSITIONS: ['resize'], + /** + * Property: metadata + * {Object} This object can be used to store additional information on a + * layer object. + */ + metadata: {}, + /** * Constructor: OpenLayers.Layer * diff --git a/tests/Format/CSWGetRecords/v2_0_2.html b/tests/Format/CSWGetRecords/v2_0_2.html index 6ad4504eb2..e32383e254 100644 --- a/tests/Format/CSWGetRecords/v2_0_2.html +++ b/tests/Format/CSWGetRecords/v2_0_2.html @@ -66,7 +66,7 @@ t.eq(testRecord.title, [{value:"Sample title"}], "check value for record.title"); //test bbox - t.eq(testRecord.BoundingBox.length, 1, "object contains 1 BoundingBox"); + t.eq(testRecord.BoundingBox.length, 2, "object contains 2 BoundingBoxes"); var bbox = testRecord.BoundingBox[0]; t.ok(bbox, "object contains BoundingBox properties"); t.eq(bbox.crs, "::Lambert Azimuthal Projection", "check value for BoundingBox.crs"); diff --git a/tests/Format/CSWGetRecords/v2_0_2.js b/tests/Format/CSWGetRecords/v2_0_2.js index 0d4fa2cd3d..c5fe31b9bd 100644 --- a/tests/Format/CSWGetRecords/v2_0_2.js +++ b/tests/Format/CSWGetRecords/v2_0_2.js @@ -25,6 +25,10 @@ var csw_response = '156 -3' + '37 83' + '' + + '' + + '51.1 -34.6' + + '-17.3 38.2' + + '' + '' + '' + '8a7245c3-8546-42de-8e6f-8fb8b5fd1bc3' + diff --git a/tests/Format/OWSContext/v0_3_1.html b/tests/Format/OWSContext/v0_3_1.html new file mode 100644 index 0000000000..cc7d1dccba --- /dev/null +++ b/tests/Format/OWSContext/v0_3_1.html @@ -0,0 +1,240 @@ + + + + + + +
+ + diff --git a/tests/list-tests.html b/tests/list-tests.html index 0ce2b796b4..0f9bad4768 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -92,6 +92,7 @@
  • Format/SOSCapabilities/v1_0_0.html
  • Format/SOSGetObservation.html
  • Format/SOSGetFeatureOfInterest.html
  • +
  • Format/OWSContext/v0_3_1.html
  • Format/XML.html
  • Geometry.html
  • Geometry/Collection.html