import nl.idgis.giclient.gis.Layer; import nl.idgis.giclient.webserviceconnector.MapConnector; import nl.idgis.giclient.webserviceconnector.wmsconnector.TransformAction; import nl.idgis.giclient.webserviceconnector.TransformConnector; import nl.idgis.giclient.webserviceconnector.wmsconnector.WMSConnectorListener; import nl.idgis.giclient.gis.CentreScale; import nl.idgis.giclient.geoma.Envelope; import nl.idgis.giclient.geoma.MultiPoint; import nl.idgis.giclient.util.XMLTools; import nl.idgis.giclient.gis.GIS; import nl.idgis.giclient.gis.WMLayer; import nl.idgis.giclient.geoma.GMLFactory; import nl.idgis.giclient.geoma.Point; import nl.idgis.giclient.webserviceconnector.AuthenticationConnector; class nl.idgis.giclient.webserviceconnector.wmsconnector.WMSConnector implements MapConnector, AuthenticationConnector { static private var instances:Array = new Array(); static private var formats:Array = new Array("image/png", "image/gif", "image/jpeg", "image/jpg"); static private var timeOut:Number = 20000; static function getInstance(url:String, title:String):WMSConnector { var instance:WMSConnector = null; for (var i:String in instances) { instance = WMSConnector(instances[i]); if (instance.getURL() == url) { return instance; } } instance = new WMSConnector(url, instances.length, title); instances.push(instance); return instance; } static function loadCapabilities(listener:WMSConnectorListener, transformConnector:TransformConnector):Void { if(instances.length > 0) { var xml:XML = new XML(); xml.ignoreWhite = true; xml["listener"] = listener; xml["connector"] = instances.length - 1; xml["instances"] = instances; xml["transformConnector"] = transformConnector; xml["timeOut"] = timeOut; xml["onTimeOut"] = function() { clearInterval(this["intervalId"]); this["timedOut"] = true; this.onLoad(false); }; xml.onData = function(src) { if(this["timedOut"]) { return; } clearInterval(this["intervalId"]); if (src == undefined) { this.onLoad(false); } else { this.parseXML(src); this.loaded = true; this.onLoad(true); } }; xml.onLoad = function(success:Boolean) { if(success) { var wmConnector:WMSConnector = this["instances"][this["connector"]]; if(wmConnector.capabilities == null) { if(XMLNode(this.firstChild).nodeName == "WMT_MS_Capabilities") { wmConnector.capabilities = this.firstChild; if(transformConnector != null) wmConnector.preProcessCapabilities(); } else { listener.onWMSError(this["instances"][this["connector"]]); } } this["connector"]--; if(this["connector"] >= 0) { var url = this["instances"][this["connector"]].getURL() + "service=WMS&request=GetCapabilities&version=1.1.1"; url = this["instances"][this["connector"]].addOwsSecurity(url); this.load(url); } else { var ta:TransformAction = new TransformAction(this["instances"], this["transformConnector"], this["listener"]); ta.transform(); } } else { listener.onWMSError(this["instances"][this["connector"]]); this["connector"]--; if(this["connector"] >= 0) { xml["timedOut"] = false; xml["intervalId"] = setInterval(xml, "onTimeOut", this["timeOut"]); var url = this["instances"][this["connector"]].getURL() + "service=WMS&request=GetCapabilities&version=1.1.1"; url = this["instances"][this["connector"]].addOwsSecurity(url); this.load(url); } else { var ta:TransformAction = new TransformAction(this["instances"], this["transformConnector"], this["listener"]); ta.transform(); } } }; if(instances[xml["connector"]].capabilities == null) { xml["timedOut"] = false; xml["intervalId"] = setInterval(xml, "onTimeOut", timeOut); var url = instances[xml["connector"]].getURL() + "service=WMS&request=GetCapabilities&version=1.1.1"; url = instances[xml["connector"]].addOwsSecurity(url); xml.load(url); } else { xml.onLoad(true); } } else listener.onWMSReady(); } private var url:String; private var title:String; private var id:Number; private var capabilities:XMLNode = null; private var layerNodes:Object = null; var envelopes:Object = null; var srs:Array = null; var toTransformNames:Array = null; var toTransformPoints:Array = null; private var username:String; private var password:String; private var securityMethod:String; function WMSConnector(url:String, id:Number, title:String) { this.url = url; this.id = id; this.title = title; layerNodes = new Object(); envelopes = new Object(); srs = new Array(); toTransformNames = new Array(); toTransformPoints = new Array(); } private function preProcessLayer(parent:XMLNode):Void { var layers:Array = XMLTools.getChildNodes("Layer", parent); for(var i:Number = 0; i < layers.length; i++) { //trace(XMLTools.getStringValue("Name", layers[i])); var name:String = XMLTools.getStringValue("Name", layers[i]); if(name != null) { //trace("Layername = " + name); for(var j:Number = 0; j < srs.length; j++) { toTransformPoints.push(new Array()); toTransformNames.push(new Array()); var envelope:Envelope = getBoundingBox(layers[i], srs[j]); if(envelope != null) { //trace(srs[j] + name); envelopes[srs[j] + name] = envelope; } else { var latlon:Envelope = getLatLonBoundingBox(layers[i]); if(latlon != null) { toTransformNames[j].push(XMLTools.getStringValue("Name", layers[i])); Array(toTransformPoints[j]).push(new Point("EPSG:4326", latlon.getMinX(), latlon.getMinY())); Array(toTransformPoints[j]).push(new Point("EPSG:4326", latlon.getMaxX(), latlon.getMaxY())); } } } } preProcessLayer(layers[i]); } } function preProcessCapabilities():Void { preProcessLayer(XMLTools.getChild("Capability", capabilities)); } function addSRS(srs:String):Void { for(var i:Number = 0; i < this.srs.length; i++) { if(this.srs[i] == srs) return; } this.srs.push(srs); } function getURL():String { return url; } private function getLayer(layerName:String, layerArray:Array):XMLNode { for(var i:Number = 0; i < layerArray.length; i++) { var name:String = XMLTools.getStringValue("Name", layerArray[i]); if(name == layerName) return layerArray[i]; var subLayerArray:Array = XMLTools.getChildNodes("Layer", layerArray[i]); if(subLayerArray != null && subLayerArray.length != 0) { var sub:XMLNode = getLayer(layerName, subLayerArray); if(sub != null) return sub; } } } private function getLayerNode(layerName:String):XMLNode { var storedLayerNode:XMLNode = XMLNode(layerNodes[layerName]); if(storedLayerNode != null) { return storedLayerNode; } var layerArray:Array = XMLTools.getChildNodes("Layer", XMLTools.getChild("Capability", capabilities)); var node:XMLNode = getLayer(layerName, layerArray); if(node != null) { layerNodes[layerName] = layerName; return node; } return null; } private function getScalesFromNode(layer:XMLNode):Array { var scales:Array = new Array(); var scaleHint:XMLNode = XMLTools.getChild("ScaleHint", layer); if(scaleHint != null) { var min:Number = XMLTools.getNumberValue("min", scaleHint); var max:Number = XMLTools.getNumberValue("max", scaleHint); min = Math.sqrt(min * min / 2); max = Math.sqrt(max * max / 2); scales.push(min / 0.00028); scales.push(max / 0.00028); } else { if(layer.parentNode.localName == "Layer") { scales = getScalesFromNode(layer.parentNode); } } return scales; } function getScales(layerName:String):Array { var scales:Array = new Array(); var layer:XMLNode = getLayerNode(layerName); if(layer != null) { scales = getScalesFromNode(layer); } if(scales.length < 2) { scales.push(0); scales.push(Number.MAX_VALUE); } return scales; } private function getOnlineResource(requestNode:XMLNode): String { return XMLTools.getStringValue("xlink:href", XMLTools.getChild("OnlineResource", XMLTools.getChild("Get", XMLTools.getChild("HTTP", XMLTools.getChild("DCPType", requestNode))))); } private function getRequestNode(request:String):XMLNode { return XMLTools.getChild(request, XMLTools.getChild("Request", XMLTools.getChild("Capability", capabilities))); } private function fixURL(url:String):String { var lastChar:String = url.charAt(url.length - 1); if(lastChar == "?" || lastChar == "&") { return url; } else { if(url.indexOf("?", 0) == -1) { return url + "?"; } else { return url + "&"; } } } public function getMapURL():String { return getOnlineResource(getRequestNode("GetMap")); } public function getFeatureInfoURL():String { return getOnlineResource(getRequestNode("GetFeatureInfo")); } public function getInfoFormats():Array { return XMLTools.getStringValues("Format", getRequestNode("GetFeatureInfo")); } public function getMapFormats():Array { return XMLTools.getStringValues("Format", getRequestNode("GetMap")); } private function getSRSFromNode(node:XMLNode):Array { var srs:Array = XMLTools.getStringValues("SRS", node); if(node.parentNode.nodeName == "Layer") { var superSrs:Array = getSRSFromNode(node.parentNode); for(var i:Number = 0; i < superSrs.length; i++) srs.push(superSrs[i]); } return srs; } public function getSRS(layerName:String):Array { var layerNode:XMLNode = getLayerNode(layerName); return getSRSFromNode(layerNode); } public function getEnvelope(layerName:String, srs:String):Envelope { return envelopes[srs + layerName]; } private function getLatLonBoundingBox(layerNode:XMLNode):Envelope { var bbox:XMLNode = XMLTools.getChild("LatLonBoundingBox", layerNode); if(bbox == null) return null; var envelope:Envelope = new Envelope("EPSG:4326", XMLTools.getNumberValue("minx", bbox), XMLTools.getNumberValue("miny", bbox), XMLTools.getNumberValue("maxx", bbox), XMLTools.getNumberValue("maxy", bbox)); return envelope; } private function getBoundingBox(layerNode:XMLNode, srs:String):Envelope { if(srs == "EPSG:4326") return getLatLonBoundingBox(layerNode); var bboxNodes:Array = XMLTools.getChildNodes("BoundingBox", layerNode); if(bboxNodes != null && bboxNodes.length != 0) { for(var i:Number = 0; i < bboxNodes.length; i++) { if(XMLTools.getStringValue("SRS", bboxNodes[i]) == srs) { var envelope:Envelope = new Envelope(srs, XMLTools.getNumberValue("minx", bboxNodes[i]), XMLTools.getNumberValue("miny", bboxNodes[i]), XMLTools.getNumberValue("maxx", bboxNodes[i]), XMLTools.getNumberValue("maxy", bboxNodes[i])); return envelope; } } } return null; } function isQueryable(layerName:String):Boolean { return XMLTools.getStringValue("queryable", getLayerNode(layerName)) == "1"; } function getMaptipPath(layerName:String):Boolean { return XMLTools.getStringValue("queryable", getLayerNode(layerName)) == "1"; } private function chooseFromArray(choices:Array, preferences:Array):String { for(var i:Number = 0; i < preferences.length; i++) { for(var j:Number = 0; j < choices.length; j++) { if(String(choices[j]).indexOf(preferences[i]) != -1) return choices[j]; } } return null; } function getFeatureInfo(layer:Layer, centreScale:CentreScale, width:Number, height:Number, gis:GIS, x:Number, y:Number):String { if(isQueryable(layer.getName())) { var url:String = this.getMap(layer, centreScale, width, height, gis); if(url != null) { var location:Number = url.indexOf("GetMap"); var str1:String = url.slice(0, location); var str2:String = url.slice(location + 6, -1); url = str1 + "GetFeatureInfo" + str2; url += "&query_layers=" + layer.getName(); var infoFormats:Array = getInfoFormats(); var infoFormat:String = "application/vnd.ogc.gml"; for (var i:Number = 0; i < infoFormats.length; i++) { var iFormat:String = String(infoFormats[i]).toLowerCase(); if (iFormat == infoFormat) { infoFormat = iFormat; break; } else if (iFormat == "text/xml") { infoFormat = iFormat; } } url += "&info_format=" + infoFormat; url += "&x=" + x; url += "&y=" + y; url = addOwsSecurity(url); return url; } } return null; } function getMap(layer:Layer, centreScale:CentreScale, width:Number, height:Number, gis:GIS):String { var wmLayer:WMLayer = WMLayer(layer); var formats:Array = wmLayer.getFormats(); if(formats == null || formats.length == 0) formats = WMSConnector.formats; var mapFormats:Array = getMapFormats(); var format:String = chooseFromArray(mapFormats, formats); if(format == null) format = mapFormats[0]; var url:String = fixURL(getMapURL()); url += "service=WMS"; url += "&version=1.1.1"; url += "&request=GetMap"; url += "&layers=" + layer.getName(); url += "&styles="; var envelope:Envelope = centreScale.toEnvelope(width, height, gis.getCoordPixFactorForSrs(centreScale.getSRS())); url += "&bbox=" + envelope.getMinX() + "," + envelope.getMinY() + "," + envelope.getMaxX() + "," + envelope.getMaxY(); url += "&width=" + width; url += "&height=" + height; url += "&srs=" + centreScale.getSRS(); url += "&format=" + format; url += "&transparent=true"; if (wmLayer.getSldUrl() != null && wmLayer.getSldUrl().length > 0) { var sldUrl:String = gis.getGIClient().getAbosulteUrl(wmLayer.getSldUrl()); url += "&sld=" + sldUrl; } url = addOwsSecurity(url); return url; } function addOwsSecurity(url:String):String { var retval:String = url; if(this.securityMethod == "ows") { retval += "&user=" + username; retval += "&password=" + password; } return retval; } function getLegendGraphic(layer:Layer, scale:Number):String { var url:String = null; var layerNode:XMLNode = getLayerNode(layer.getName()); if(layerNode != null) { var styles:Array = XMLTools.getChildNodes("Style", layerNode); if(styles != null && styles.length > 0) { var styleNo:Number = 0; var style:XMLNode = null; for (var i:Number = 0; i < styles.length; i++) { style = XMLNode(styles[i]); var stylename:String = XMLTools.getStringValue("Name", style); if (stylename.toLowerCase().indexOf("_leg") > -1) { //this is a hack to use the style meant as legend (RO-Online) styleNo = i; break; } } style = XMLNode(styles[styleNo]); var legendurl:XMLNode = XMLTools.getChild("LegendURL", style); if(legendurl != null) { var onlineResource:XMLNode = XMLTools.getChild("OnlineResource", legendurl); var format:String = XMLTools.getStringValue("Format", legendurl); if(onlineResource != null && format != null && format.length != 0) { url = XMLTools.getStringValue("xlink:href", onlineResource); } } } } url = addOwsSecurity(url); return url; } function getID():String { return "OGCWMS" + id; } function isSRSSupported(layer : Layer, srs : String) : Boolean { var supported:Array = getSRS(layer.getName()); for(var i:Number = 0; i < supported.length; i++) if(supported[i] == srs) return true; return false; } function getTitle():String { return title; } public function setCredentials(username : String, password : String) : Void { this.username = username; this.password = password; } public function setMethod(name : String) : Void { this.securityMethod = name; } public function getLoginTitle():String { return "WMS: " + url; } function getMetaUrl(layer:Layer):String { var layerNode:XMLNode = getLayerNode(layer.getName()); var metadataURLNode:XMLNode = XMLTools.getChild("MetadataURL", layerNode); if(metadataURLNode != null) { var onlineResourceNode:XMLNode = XMLTools.getChild("OnlineResource", metadataURLNode); var attr:Object = onlineResourceNode.attributes; for(var s:String in attr) { if(s.indexOf("href") != -1) { return attr[s]; } } } return null; } private function subLayers(superLayer:XMLNode, depth:Number):Array { var layers:Array = new Array(); var subLayerNodes:Array = XMLTools.getChildNodes("Layer", superLayer); for(var i:Number = 0; i < subLayerNodes.length; i++) { var layer:XMLNode = subLayerNodes[i]; var name:String = XMLTools.getStringValue("Name", layer); if(name == null) { var sub:Array = subLayers(layer, depth); for(var j:Number = 0; j < sub.length; j++) { layers.push(sub[j]); } } else { var title:String = XMLTools.getStringValue("Title", layer); if(title == null) { title = name; } layers.push({title: title, name: name, depth: depth}); var sub:Array = subLayers(layer, depth + 1); for(var j:Number = 0; j < sub.length; j++) { layers.push(sub[j]); } } } return layers; } public function getLayers():Array { var layers:Array = new Array(); var capability:XMLNode = XMLTools.getChild("Capability", capabilities); var rootLayer:XMLNode = XMLTools.getChild("Layer", capability); var name:String = XMLTools.getStringValue("Name", rootLayer); if(name == null) { var sub:Array = subLayers(rootLayer, 0); for(var i:Number = 0; i < sub.length; i++) { layers.push(sub[i]); } } else { var title:String = XMLTools.getStringValue("Title", rootLayer); if(title == null) { title = name; } layers.push({title: title, name: name, depth: 0}); var sub:Array = subLayers(rootLayer, 1); for(var i:Number = 0; i < sub.length; i++) { layers.push(sub[i]); } } return layers; } }