1 /** 2 * @fileOverview 3 * Copyright (c) 2013 Regione Autonoma della Sardegna 4 * Published under the GPL license.<br> 5 * See <a href="https://sardegnageoportale.it/webgis/license.txt">https://sardegnageoportale.it/webgis/license.txt</a> 6 * for the full text of the license. 7 * @version 0.1 8 */ 9 10 /** 11 * @namespace framework.widgets.search 12 */ 13 Ext.namespace("framework.widgets.search"); 14 15 /** api: (define) 16 * module = framework.widgets.search 17 * class = FeatureInfoPanel 18 * base_link = `Ext.Panel <http://dev.sencha.com/deploy/ext-3.3.1/docs/?class=Ext.Panel>`_ 19 */ 20 21 /** api: example 22 * Sample code showing how to configure a FeatureInfoPanel. 23 * All regular ExtJS `Ext.Panel <http://dev.sencha.com/deploy/ext-3.3.1/docs/?class=Ext.Panel>`_ 24 * config params also apply. 25 * The ``infoFormat`` config parameter is the default ``INFO_FORMAT`` to be used for WMS GetFeatureInfo (GFI). 26 * This value can be overruled by an optional per-Layer ``infoFormat`` WMS Layer config parameter. 27 * GetFeatureInfo-response data may be displayed as a Grid, a Tree or formatted XML. The ``displayPanels`` 28 * config option can be used to trigger a menu with display options. Note also the use of "GridCellRenderers". 29 * These allow you to render specific formatting of cell content within the feature grid. For example 30 * URL substitution to render external links in a new tab or browser window. You can even supply your own formatting 31 * function. This function is according to the ExtJS ColumnModel renderers (see e.g. http://snipplr.com/view/40942). 32 * 33 * .. code-block:: javascript 34 * 35 * xtype: 'framework_featureinfopanel', 36 * id: 'framework-feature-info', 37 * region: "south", 38 * border: true, 39 * collapsible: true, 40 * collapsed: true, 41 * height: 205, 42 * split: true, 43 * infoFormat: 'application/vnd.ogc.gml', 44 * displayPanels: ['Grid', 'XML'], 45 * exportFormats: ['CSV', 'XLS'], 46 * maxFeatures: 10, 47 * discardStylesForDups: true, 48 * gridCellRenderers: [ 49 * { 50 * featureType: 'cities', 51 * attrName: 'City', 52 * renderer: { 53 * fn : framework.widgets.GridCellRenderer.directLink, 54 * options : { 55 * url: 'http://en.wikipedia.org/wiki/{City}', 56 * target: '_new' 57 * } 58 * } 59 * }, 60 * { 61 * featureType: 'cities', 62 * attrName : 'Country', 63 * renderer : { 64 * fn : framework.widgets.GridCellRenderer.browserPopupLink, 65 * options : { 66 * url: 'http://en.wikipedia.org/wiki/{Country}', 67 * winName: 'demoWin', 68 * width: 400, 69 * height: 800, 70 * scrollbars: 'yes' 71 * } 72 * } 73 * }, 74 * { // Example for custom HTML, could use also with e.g. links 75 * featureType: 'cities', 76 * attrName : 'longitude', 77 * renderer : { 78 * fn : framework.widgets.GridCellRenderer.valueSubstitutor, 79 * options : { 80 * template: '<i>ll={latitude},{longitude}{empty}</i>' 81 * } 82 * } 83 * }, 84 * { 85 * // Example: supply your own function, parms as in ExtJS ColumnModel 86 * featureType: 'cities', 87 * attrName : 'population', 88 * renderer : { 89 * fn : function(value, metaData, record, rowIndex, colIndex, store) { 90 * // Custom formatting, may also use this.options if needed 91 * return '<b>' + value + ' inh.</b>'; 92 * }, 93 * options : { 94 * 95 * } 96 * } 97 * } 98 * 99 * } 100 * 101 */ 102 103 104 /** 105 * A Panel designed to hold WMS GetFeatureInfo (GFI) data for one or more WMS layers. 106 * @name_ FeatureInfoPanel 107 * @class A Panel designed to hold WMS GetFeatureInfo (GFI) data for one or more WMS layers. 108 * @constructor 109 @extends <a target="_blank" href="http://docs.sencha.com/extjs/3.4.0/#!/api/Ext.form.ComboBox">Ext.form.ComboBox</a> 110 */ 111 framework.widgets.search.FeatureInfoPanel = Ext.extend(Ext.Panel, 112 /** 113 * @lends framework.widgets.search.FeatureInfoPanel.prototype 114 */ 115 { 116 /** api: title 117 * 118 * Default title of the panel. If not set 119 * the value ``Feature Info`` will be used. 120 * @public 121 * @type String 122 */ 123 title: 'Feature Info', 124 /** api: config[maxFeatures] 125 * 126 * Default GFI MAX_FEATURES parameter. Will be ``5`` if not set. 127 * @public 128 * @type int 129 */ 130 maxFeatures: 5, 131 /** api: config[displayPanels] 132 * 133 * String array of types of Panels to display GFI info in, default value is ['Grid'], a grid table. Other values are 'XML' and 'Tree'. 134 * If multiple display values are given a menu will be shown to switch display types. 135 * @public 136 * @type String Array 137 */ 138 displayPanels: ['Grid'], 139 /** api: config[exportFormats] 140 * 141 * Array of document formats to be used when exporting the content of a GFI response. This requires the server-side CGI script 142 * ``framework.cgi`` to be installed. Exporting results in a download of a document with the contents of the (Grid) Panel. 143 * For example when 'XLS' is configured, exporting will result in the Excel (or compatible) program to be 144 * started with the GFI data in an Excel worksheet. 145 * Option values are 'CSV' and/or 'XLS', , 'GMLv2', 'GeoJSON', 'WellKnownText' default is, ``null``, meaning no export (results in no export menu). 146 * The value ['CSV', 'XLS'] configures a menu to choose from a ``.csv`` or ``.xls`` export document format. 147 * @public 148 * @type String Array 149 */ 150 exportFormats: ['CSV', 'XLS', 'GMLv2', 'GeoJSON', 'WellKnownText'], 151 /** api: config[infoFormat] 152 * 153 * Default GFI INFO_FORMAT parameter, may be overruled per Layer object infoFormat WMS param. If not set 154 * the value ``application/vnd.ogc.gml`` will be used. 155 * @public 156 * @type String 157 */ 158 infoFormat: 'application/vnd.ogc.gml', 159 /** api: config[hover] 160 * 161 * Show features on hovering. 162 * @public 163 * @type Boolean 164 */ 165 hover: false, 166 /** api: config[drillDown] 167 * 168 * Show features from all visible layers that are queryable. 169 * @public 170 * @type Boolean 171 */ 172 drillDown: true, 173 /** api: config[layer] 174 * 175 * The layer to get feature information from. Parameter value will be ``""`` if not set. 176 * If not set, all visible layers of the map will be searched. In case the drillDown 177 * parameter is ``false``, the topmost visible layer will searched. 178 * @public 179 * @type String 180 */ 181 layer: "", 182 /** api: config[discardStylesForDups] 183 * 184 * In case the same Layer is present multiple times, request only once without any STYLES= parameter. 185 * Default is ``false``. 186 * @public 187 * @type Boolean 188 */ 189 discardStylesForDups: false, 190 /** api: config[showTopToolbar] 191 * 192 * Show the toolbar with object count, clear and export buttons. 193 * Default is ``false``. 194 * @public 195 * @type Boolean 196 */ 197 showTopToolbar: true, 198 /** api: config[showGeometries] 199 * 200 * Should the feature geometries be shown? Default ``true``. 201 * @public 202 * @type Boolean 203 */ 204 showGeometries: true, 205 /** api: config[featureSelection] 206 * 207 * Should the feature geometries that are shown be selectable in grid and map? Default ``true``. 208 * @public 209 * @type Boolean 210 */ 211 featureSelection: true, 212 /** Internal vars */ 213 /** api: config[pop] 214 * 215 * @public 216 * @type 217 */ 218 pop: null, 219 /** Internal vars */ 220 /** api: config[map] 221 * 222 * A configured map or a configuration object 223 * @public 224 * @type OpenLayers.Map or Object 225 */ 226 map: null, 227 /** api: config[displayPanel] 228 * 229 * A configured map or a configuration object 230 * @public 231 * @type Boolean 232 * 233 */ 234 displayPanel: null, 235 /** api: config[lastEvt] 236 * 237 * A configured map or a configuration object 238 * @public 239 * @type Object 240 * 241 */ 242 lastEvt: null, 243 /** api: config[olControl] 244 * 245 * A configured map or a configuration object 246 * @public 247 * @type Object Openlayers.Control 248 * 249 */ 250 olControl: null, 251 /** api: config[tb] 252 * 253 * A configured map or a configuration object 254 * @public 255 * @type Object 256 * 257 */ 258 tb: null, 259 /** api: config[zoomToDataExtent] 260 * 261 * Zoom to layer data extent when loaded ?. 262 * @public 263 * @type Boolean 264 */ 265 zoomToDataExtent: true, 266 /** api: config[hiddenLayers] 267 * 268 * show feature for hidden layers. 269 * @public 270 * @type Boolean 271 */ 272 hiddenLayers: false, 273 mapVoli: null, 274 panelsContainer: null, 275 /** 276 * Image icon button. 277 * @public 278 * @type String 279 */ 280 iconButton: 'theme/app/img/camera16x16.png', 281 /** Init the component creating a store and object'events. 282 * @private 283 */ 284 initComponent: function() { 285 286 // For closures ("this" is not valid in callbacks) 287 var self = this; 288 Ext.apply(this, { 289 layout: "fit" 290 }); 291 this.display = this.displayGrid; 292 framework.widgets.search.FeatureInfoPanel.superclass.initComponent.call(this); 293 if (!this.map) 294 this.map = this.target.mapPanel.map; 295 /*** 296 * Add a WMSGetFeatureInfo control to the map if it is not yet present 297 * The control could already have been set. If not try and find 298 * the control in the map. 299 */ 300 if (!this.olControl) { 301 var controls = this.map.getControlsByClass("OpenLayers.Control.WMSGetFeatureInfo"); 302 if (controls && controls.length > 0) { 303 for (var index = 0; index < controls.length; index++) { 304 //Control should not be the one used for tooltips. 305 if (controls[index].id !== "framework-feature-info-hover") { 306 this.olControl = controls[index]; 307 // Overrule with our own info format and max features 308 this.olControl.infoFormat = this.infoFormat; 309 this.olControl.maxFeatures = this.maxFeatures; 310 this.olControl.hover = this.hover; 311 this.olControl.drillDown = this.drillDown; 312 break; 313 } 314 } 315 } 316 317 // No GFI control present: create new and add to Map 318 if (!this.olControl) { 319 this.olControl = new OpenLayers.Control.WMSGetFeatureInfo({ 320 maxFeatures: this.maxFeatures, 321 queryVisible: true, 322 infoFormat: this.infoFormat, 323 hover: this.hover, 324 drillDown: this.drillDown 325 }); 326 this.map.addControl(this.olControl); 327 } 328 } 329 // Register WMSGetFeatureInfo control event handlers 330 this.olControl.events.register("getfeatureinfo", this, this.handleGetFeatureInfo); 331 this.olControl.events.register("beforegetfeatureinfo", this, this.handleBeforeGetFeatureInfo); 332 this.olControl.events.register("nogetfeatureinfo", this, this.handleNoGetFeatureInfo); 333 this.addListener("afterrender", this.onPanelRendered, this); 334 this.addListener("render", this.onPanelRender, this); 335 this.addListener("show", this.onPanelShow, this); 336 this.addListener("hide", this.onPanelHide, this); 337 }, 338 /** api: method[onPanelRender] 339 * Called when Panel has been rendered. 340 * @private 341 */ 342 onPanelRender: function() { 343 this.mask = new Ext.LoadMask(this.body, {msg: 'Loading...'}); 344 }, 345 /** api: method[onPanelRendered] 346 * Called when Panel has been rendered. 347 * @private 348 */ 349 onPanelRendered: function() { 350 if (this.ownerCt) { 351 this.ownerCt.addListener("hide", this.onPanelHide, this); 352 this.ownerCt.addListener("show", this.onPanelShow, this); 353 } 354 }, 355 /** private: method[onPanelShow] 356 * Called after our panel is shown. 357 * @private 358 */ 359 onPanelShow: function() { 360 if(this.cartPanel) 361 this.olControl.deactivate(); 362 if (this.tabPanel) { 363 this.tabPanel.items.each(function(item) { 364 return item.showLayer ? item.showLayer() : true; 365 }, this); 366 } 367 }, 368 /** private: method[onPanelHide] 369 * Called before our panel is hidden. 370 * @private 371 */ 372 onPanelHide: function() { 373 if(this.cartPanel && this.map.layersSearchOnMap && Object.keys(this.map.layersSearchOnMap).length > 0) 374 this.olControl.activate(); 375 if (this.tabPanel) { 376 this.tabPanel.items.each(function(item) { 377 return item.hideLayer ? item.hideLayer() : true; 378 }, this); 379 } 380 }, 381 /** private: method[initPanel] 382 * 383 * @private 384 */ 385 initPanel: function() { 386 this.lastEvt = null; 387 this.expand(); 388 if (this.tabPanel) { 389 this.tabPanel.items.each(function(item) { 390 this.tabPanel.remove(item); 391 return item.cleanup ? item.cleanup() : true; 392 }, this); 393 } 394 395 if (this.displayPanel) { 396 this.remove(this.displayPanel); 397 this.displayPanel = null; 398 } 399 400 this.displayOn = false; 401 }, 402 /** private: method[handleBeforeGetFeatureInfo] 403 * 404 * @private 405 */ 406 handleBeforeGetFeatureInfo: function(evt) { 407 //If the event was not triggered from this.olControl, do nothing 408 if (evt.object !== this.olControl) { 409 return; 410 } 411 412 this.olControl.layers = []; 413 // Needed to force accessing multiple WMS-es when multiple layers are visible 414 this.olControl.url = null; 415 this.olControl.drillDown = this.drillDown; 416 // Select WMS layers that are visible and enabled (via featureInfoFormat or Layer info_format (capitalized by OL) prop) 417 var layer; 418 //If a layer is specified, try and find the layer in the map. 419 if (this.layer) { 420 var layers = this.map.getLayersByName(this.layer); 421 if (layers) { 422 //Add the first layer found with name layer 423 layer = layers[0]; 424 this.olControl.layers.push(layer); 425 } 426 } 427 428 //If no layer was specified or the specified layer was not found, 429 //assign the visible WMS-layers to the olControl. 430 if (this.olControl.layers.length == 0) { 431 this.layerDups = {}; 432 for (var index = 0; index < this.map.layers.length; index++) { 433 layer = this.map.layers[index]; 434 // Skip non-WMS layers 435 if (!layer instanceof OpenLayers.Layer.WMS || !layer.params) { 436 continue; 437 } 438 439 440 // Enable layers for GFI that have a GFI mime param specified 441 if (layer.visibility && (layer.featureInfoFormat || layer.params.INFO_FORMAT)) { 442 // Backward compatible with old configs that have only featureInfoFormat 443 // set to a mime type like "text/xml". layer.params.INFO_FORMAT determines the mime 444 // requested from WMS server. 445 if (!layer.params.INFO_FORMAT && layer.featureInfoFormat) { 446 layer.params.INFO_FORMAT = layer.featureInfoFormat; 447 } 448 449 if (this.layerDups[layer.params.LAYERS]) { 450 // https://code.google.com/p/geoext-viewer/issues/detail?id=215 451 // what to do when we have duplicate layers, at least we may replace if 452 // one of them is without any STYLES or FILTER or CQL. 453 if (this.discardStylesForDups) { 454 // Make the STYLES empty for the request only (restore after the request) 455 var dupLayer = this.layerDups[layer.params.LAYERS]; 456 dupLayer.savedStyles = dupLayer.params.STYLES; 457 dupLayer.params.STYLES = ""; 458 } 459 continue; 460 } 461 462 463 this.olControl.layers.push(layer); 464 this.layerDups[layer.params.LAYERS] = layer; 465 } 466 } 467 } 468 469 this.initPanel(); 470 if (this.mask) { 471 // Show loading mask 472 this.mask.show(); 473 } 474 475 this.fireEvent('beforefeatureinfo', evt); 476 // Try to fetch features from WFS/Vector layers 477 this.handleVectorFeatureInfo(evt.object.handler.evt); 478 // No layers with GFI and no features from Vector layers available: display message 479 if (this.olControl.layers.length == 0 && this.features == null) { 480 this.handleNoGetFeatureInfo(); 481 } 482 }, 483 /** private: method[handleGetFeatureInfo] 484 * 485 * @private 486 */ 487 handleGetFeatureInfo: function(evt) { 488 489 // Always restore possible Layer duplicate STYLES 490 if (this.discardStylesForDups) { 491 // https://code.google.com/p/geoext-viewer/issues/detail?id=215 492 for (var layerName in this.layerDups) { 493 var layerDup = this.layerDups[layerName]; 494 if (layerDup.savedStyles) { 495 layerDup.params.STYLES = layerDup.savedStyles; 496 layerDup.savedStyles = null; 497 } 498 } 499 } 500 501 // If the event was not triggered from this.olControl, and not a Vector layer, do nothing 502 if (evt && evt.object !== this.olControl) { 503 return; 504 } 505 506 // Hide the loading mask 507 if (this.mask) { 508 this.mask.hide(); 509 } 510 511 // Save result e.g. when changing views 512 if (evt) { 513 this.lastEvt = evt; 514 } 515 516 if (!this.lastEvt) { 517 return; 518 } 519 520 this.displayFeatures(this.lastEvt); 521 }, 522 /** private: method[handleGetFeatureInfo] 523 * 524 * Determine if Vector features are touched. 525 * @private 526 */ 527 handleVectorFeatureInfo: function(evt) { 528 this.vectorFeaturesFound = false; 529 // Nasty hack but IE refuses to play nice and provide screen X,Y as all others!! 530 531 var screenX = Ext.isIE ? Ext.EventObject.xy[0] : evt.clientX; 532 var screenY = Ext.isIE ? Ext.EventObject.xy[1] : evt.clientY; 533 this.features = this.getFeaturesByXY(screenX, screenY); 534 if (this.mask) { 535 this.mask.hide(); 536 } 537 538 evt.features = this.features; 539 if (evt.features && evt.features.length > 0) { 540 this.vectorFeaturesFound = true; 541 this.displayFeatures(evt); 542 } 543 }, 544 /** private: method[handleNoGetFeatureInfo] 545 * 546 * @private 547 */ 548 handleNoGetFeatureInfo: function() { 549 // When also no visible Vector layers give warning 550 if (!this.visibleVectorLayers) { 551 Ext.Msg.alert('Warning', 'Feature Info unavailable (you may need to make some layers visible)'); 552 } 553 }, 554 /** 555 * private: method[getFeaturesByXY] 556 * Get all features at the given screen location. 557 * 558 * Parameters: 559 * x - {Number} Screen x coordinate. 560 * y - {Number} Screen y coordinate. 561 * 562 * Returns: 563 * {Array(<OpenLayers.Feature.Vector>)} List of features at the given point. 564 * 565 * From: 566 * http://trac.osgeo.org/openlayers/browser/sandbox/tschaub/select/lib/OpenLayers/FeatureAgent.js 567 * @private 568 */ 569 getFeaturesByXY: function(x, y) { 570 this.visibleVectorLayers = false; 571 var features = [], targets = [], layers = []; 572 var layer, target, feature, i, len; 573 // go through all layers looking for targets 574 for (i = this.map.layers.length - 1; i >= 0; --i) { 575 layer = this.map.layers[i]; 576 if (layer.div.style.display !== "none") { 577 if (layer instanceof OpenLayers.Layer.Vector) { 578 target = document.elementFromPoint(x, y); 579 while (target && target._featureId) { 580 feature = layer.getFeatureById(target._featureId); 581 if (feature) { 582 var featureClone = feature.clone(); 583 featureClone.type = layer.name; 584 featureClone.layer = layer; 585 features.push(featureClone); 586 target.style.display = "none"; 587 targets.push(target); 588 target = document.elementFromPoint(x, y); 589 this.visibleVectorLayers = true; 590 } else { 591 // sketch, all bets off 592 target = false; 593 } 594 } 595 } 596 layers.push(layer); 597 layer.div.style.display = "none"; 598 } 599 } 600 // restore feature visibility 601 for (i = 0, len = targets.length; i < len; ++i) { 602 targets[i].style.display = ""; 603 } 604 // restore layer visibility 605 for (i = layers.length - 1; i >= 0; --i) { 606 layers[i].div.style.display = "block"; 607 } 608 return features; 609 }, 610 /*** 611 * private: method[getFeatureType] 612 * Try to get feature type name from a feature, this is often WMS-specific and a bit of black art. 613 * TODO: determine for ESRI WMS. 614 * @private 615 */ 616 getFeatureType: function(feature) { 617 618 // If GFI returned GML, OL has may have parsed out the featureType 619 // http://code.google.com/p/geoext-viewer/issues/detail?id=92 620 if (feature.gml && feature.gml.featureType) { 621 return feature.gml.featureType; 622 } 623 624 // GeoServer-specific 625 if (feature.fid && feature.fid.indexOf('undefined') < 0) { 626 // TODO: this is nasty and GeoServer specific ? 627 // We may check the FT e.g. from the GML tag(s) available in the evt 628 // More specific, we need to. Because now with multiple layers, all are assigned to 629 // unknown and you get strange column results when the featuretypes are mixed... 630 var featureType = /[^\.]*/.exec(feature.fid); 631 return (featureType[0] != "null") ? featureType[0] : null; 632 } 633 634 // Mapserver-specific 635 if (feature.type) { 636 return feature.type; 637 } 638 639 // ESRI-specific 640 if (feature.attributes['_LAYERID_']) { 641 // Try ESRI WMS GFI returns layername/featureType as attribute '_LAYERID_' ! 642 // See http://webhelp.esri.com/arcims/9.3/general/mergedprojects/wms_connect/wms_connector/get_featureinfo.htm 643 // See e.g. http://svn.flamingo-mc.org/trac/changeset/648/flamingo/trunk/fmc/OGWMSConnector.as 644 return feature.attributes['_LAYERID_']; 645 } 646 647 // TNO/DINO-specific 648 if (feature.attributes['DINO_DBA.MAP_SDE_GWS_WELL_W_HEADS_VW.DINO_NR']) { 649 // TODO find better way to determine and fix for DINO services 650 // var nodes = featureNode.childNodes; 651 // var _featureType = ""; 652 // for (j = 0,jlen = nodes.length; j < jlen; ++j) { 653 // var node = nodes[j]; 654 // if (node.nodeType !== 3) { 655 // //Dirty fix for dino name needs to be stripped as it consists of 3 parts 656 // var dino_name = node.getAttribute("name"); 657 // var _feat = dino_name.split("."); 658 // if(_feat[0] === "DINO_DBA"){ 659 // attributes[_feat[2]] = node.getAttribute("value"); 660 // _featureType = _feat[1]; 661 // } else { 662 // attributes[node.getAttribute("name")] = node.getAttribute("value"); 663 // } 664 // } 665 // } 666 // } 667 // _feature = new OpenLayers.Feature.Vector(geom, attributes, null); 668 // 669 // if(_featureType !== ""){ 670 // // Dirty fix for dino to maintain reference to layer 671 // _feature.gml = {}; 672 // _feature.gml.featureType = _featureType; 673 // _feature.fid = _featureType + "." + len; 674 // _feature.layer = _featureType; 675 // } 676 // var _feat = dino_name.split("."); 677 // if(_feat[0] === "DINO_DBA"){ 678 // attributes[_feat[2]] = node.getAttribute("value"); 679 // _featureType = _feat[1]; 680 // } else { 681 // attributes[node.getAttribute("name")] = node.getAttribute("value"); 682 // } 683 // rec.attributes[0] 684 return 'TNO_DINO_WELLS'; 685 } 686 687 // TNO/DINO-specific (see above) 688 if (feature.attributes['DINO_DBA.MAP_SDE_BRH_BOREHOLE_RD_VW.DINO_NR']) { 689 return 'TNO_DINO_BOREHOLES'; 690 } 691 692 // Nothing found :-( 693 return 'Unknown'; 694 }, 695 /*** 696 * private: method[getFeatureTitle] 697 * Try to get feature title (user friendly name) for a feature type name. 698 * @private 699 */ 700 getFeatureTitle: function(featureType) { 701 // Fall back to featureType if we can't find the name 702 var featureTitle = featureType; 703 // WMS/WFS GetFeature results don't return the Human Friendly name. 704 // So we get it from the layer declaration here and use this for the tab titles. 705 // Resolves Enhancement #164 - JM, 2013.01.30 706 var layers = this.map.layers; 707 for (var l = 0; l < layers.length; l++) { 708 var nextLayer = layers[l]; 709 // Skip non-WMS layers and non-visible layers 710 if (!nextLayer.params || (!this.hiddenLayers && !nextLayer.visibility)) { 711 continue; 712 } 713 714 // Ensure cases match by making all lowerCase. May not otherwise. 715 if (featureType.toLowerCase() == /([^:]*$)/.exec(nextLayer.params.LAYERS)[0].toLowerCase()) { 716 featureTitle = nextLayer.name; 717 break; 718 } 719 } 720 721 return featureTitle; 722 }, 723 /*** 724 * private: method[displayFeatures] 725 * 726 * @private 727 */ 728 displayFeatures: function(evt) { 729 730 // Were any features returned ? 731 if (evt.features && evt.features.length > 0) { 732 if(!evt.features[0].gml && this.cartPanel) 733 return; 734 if (!this.vectorFeaturesFound && this.displayPanel) { 735 this.remove(this.displayPanel); 736 this.displayPanel = null; 737 this.displayOn = false; 738 } 739 // Delegate to current display panel (Grid, Tree, XML) 740 this.displayPanel = this.display(evt); 741 } else if (!this.vectorFeaturesFound) { 742 // No features found in WMS and Vector Layers: show message 743 if(this.cartPanel){ 744 this.displayPanel = this.displayInfo('Nessun elemento trovato nel punto selezionato.'); 745 }else{ 746 this.displayPanel = this.displayInfo('No features found'); 747 } 748 749 } 750 751 if (this.displayPanel && !this.displayOn) { 752 this.add(this.displayPanel); 753 this.displayPanel.doLayout(); 754 } 755 756 if (this.getLayout() instanceof Object && !this.displayOn) { 757 this.getLayout().runLayout(); 758 } 759 this.displayOn = true; 760 this.fireEvent('featureinfo', evt); 761 }, 762 /*** 763 * private: method[displayFeatures] 764 * Callback function for handling the result of an OpenLayers GetFeatureInfo request (display as grid) 765 * @private 766 */ 767 displayGrid: function(evt) { 768 var featureSets = {}, featureSet, featureType, featureTitle, featureSetKey, featureKeysOnCart; 769 this.searchExtent = null; 770 // if(this.map.activeSearchPanel){ 771 // this.map.activeSearchPanel.remove(this.map.activeSearchPanel.tabPanel); 772 // } 773 // this.map.activeSearchPanel = this; 774 // 775 // if(this.map.layersWithFeature && this.map.layersWithFeature.length > 0){ 776 // for(var i=0;i<this.map.layersWithFeature.length;i++){ 777 // this.map.layersWithFeature[i].destroyFeatures(); 778 // } 779 // if (window.popupAddr) { 780 // window.popupAddr.close(); 781 // window.popupAddr.destroy(); 782 // } 783 // } 784 // this.map.layersWithFeature = []; 785 // 786 //se esiste un carrello carico le chiavi delle feature presenti nel carrello 787 if (this.cartPanel) { 788 var cart = Ext.getCmp(this.cartPanel); 789 featureKeysOnCart = cart.getKeysStore(); 790 } 791 792 // Extract feature set per feature type from total array of features 793 for (var index = 0; index < evt.features.length; index++) { 794 var feature = evt.features[index]; 795 // Get feature type name 796 featureType = this.getFeatureType(feature); 797 featureTitle = this.getFeatureTitle(featureType); 798 featureSetKey = featureType + featureTitle; 799 //se esiste un carrello allora selezionare le feature presenti sul carrello 800 if (this.cartPanel) { 801 var key = featureType + '_' + feature.attributes.IMAGE; 802 if (featureKeysOnCart.indexOf(key) > -1) 803 feature.attributes.onCart = true; 804 else 805 feature.attributes.onCart = false; 806 } 807 808 if (!featureSets[featureSetKey]) { 809 featureSet = { 810 featureType: featureType, 811 title: featureTitle, 812 features: [] 813 }; 814 featureSets[featureSetKey] = featureSet; 815 } 816 817 818 /*** 819 * Go through attributes and modify where needed: 820 * - hyperlinks clickable 821 * - illegal field names (with dots) 822 * - custom hyperlinks 823 */ 824 for (var attrName in feature.attributes) { 825 826 // Check for hyperlinks 827 // Simple fix for issue 23 828 // http://code.google.com/p/geoext-viewer/issues/detail?id=23 829 var attrValue = feature.attributes[attrName]; 830 if (attrValue && typeof attrValue == 'string' && attrValue.indexOf("http://") >= 0) { 831 // Display value as HTML hyperlink 832 feature.attributes[attrName] = '<a href="' + attrValue + '" target="_new">' + attrValue + '</a>'; 833 } 834 835 // GetFeatureInfo response may contain dots in the fieldnames, these are not allowed in ExtJS store fieldnames. 836 // Use a regex to replace the dots /w underscores. 837 if (attrName.indexOf(".") >= 0) { 838 var newAttrName = attrName.replace(/\./g, "_"); 839 feature.attributes[newAttrName] = feature.attributes[attrName]; 840 if (attrName != newAttrName) { 841 delete feature.attributes[attrName]; 842 } 843 } 844 } 845 846 featureSet.features.push(feature); 847 } 848 849 // Remove any existing panel 850 if (this.tabPanel != null && !this.displayOn) { 851 this.remove(this.tabPanel); 852 this.tabPanel = null; 853 } 854 855 // Run through feature sets, creating a feature grid for each 856 // and adding to the tab panel 857 for (featureSetKey in featureSets) { 858 featureSet = featureSets[featureSetKey]; 859 if (featureSet.features.length == 0) { 860 // Do not display empty feature set 861 continue; 862 } 863 864 var grid = {}; 865 if (this.cartPanel) { 866 grid = new framework.widgets.search.FeatureSearchGridPanel({ 867 title: featureSet.title, 868 cartPanel: this.cartPanel, 869 basePathResources: this.basePathResources, 870 managerProductPanel: this.managerProductPanel, 871 urlGeoNetwork: this.urlGeoNetwork, 872 featureType: featureSet.featureType, 873 header: false, 874 features: featureSet.features, 875 autoConfig: true, 876 showGeometries: this.showGeometries, 877 featureSelection: this.featureSelection, 878 gridCellRenderers: this.gridCellRenderers, 879 showTopToolbar: this.showTopToolbar, 880 exportFormats: this.exportFormats, 881 father: this, 882 map: this.map, 883 enableColumnHide: false, 884 viewConfig: { 885 forceFit: true, 886 sortAscText: 'Ordine crescente', 887 sortDescText: 'Ordine decrescente', 888 columnsText: 'Colonne', 889 lockText: "Colonna fissa", 890 unlockText: "Colonna libera" 891 }, 892 hropts: { 893 zoomOnRowDoubleClick: true, 894 zoomOnFeatureSelect: false, 895 zoomLevelPointSelect: 8, 896 zoomToDataExtent: this.zoomToDataExtent 897 } 898 }); 899 } else { 900 grid = new framework.widgets.search.FeatureGridPanel({ 901 title: featureSet.title, 902 featureType: featureSet.featureType, 903 header: false, 904 features: featureSet.features, 905 autoConfig: true, 906 showGeometries: this.showGeometries, 907 featureSelection: this.featureSelection, 908 gridCellRenderers: this.gridCellRenderers, 909 showTopToolbar: this.showTopToolbar, 910 exportFormats: this.exportFormats, 911 map: this.map, 912 iconButton: this.iconButton, 913 hropts: { 914 zoomOnRowDoubleClick: true, 915 zoomOnFeatureSelect: false, 916 zoomLevelPointSelect: 8, 917 zoomToDataExtent: this.zoomToDataExtent 918 }, 919 listeners: { 920 showAerialPhoto: function(record) { 921 this.fireEvent('showAerialPhoto', record); 922 }, 923 scope: this 924 }}); 925 } 926 927 // Create tab panel for the first FT and add additional tabs for each FT 928 if (!this.tabPanel) { 929 this.tabPanel = new Ext.TabPanel({ 930 border: false, 931 autoDestroy: true, 932 enableTabScroll: true, 933 //height: this.getHeight(), 934 items: [grid], 935 activeTab: 0 936 }); 937 } else { 938 // Add to existing tab panel 939 this.tabPanel.add(grid); 940 this.tabPanel.setActiveTab(0); 941 } 942 943 grid.loadFeatures(featureSet.features, featureSet.featureType); 944 } 945 return this.tabPanel; 946 }, 947 /*** 948 * private: method[displayTree] 949 * Callback function for handling the result of an OpenLayers GetFeatureInfo request (display as Tree) 950 * @private 951 */ 952 displayTree: function(evt) { 953 var panel = new framework.widgets.XMLTreePanel(); 954 panel.xmlTreeFromText(panel, evt.text); 955 return panel; 956 }, 957 /*** 958 * private: method[displayFeatures] 959 * Callback function for handling the result of an OpenLayers GetFeatureInfo request (display as XML) 960 * @private 961 */ 962 displayXML: function(evt) { 963 var opts = { 964 // DA VERIFICARE 965 // html: '<div class="hr-html-panel-body"><pre>' + Heron.Utils.formatXml(evt.text, true) + '</pre></div>', 966 html: '<div class="hr-html-panel-body"><pre>' + evt.text + '</pre></div>', 967 preventBodyReset: true, 968 autoScroll: true 969 }; 970 return new Ext.Panel(opts); 971 }, 972 /*** 973 * private: method[displayInfo] 974 * Display info panel. 975 * @private 976 */ 977 displayInfo: function(infoStr) { 978 var opts = { 979 html: '<div class="hr-html-panel-body"><pre>' + infoStr + '</pre></div>', 980 preventBodyReset: true, 981 autoScroll: true 982 }; 983 return new Ext.Panel(opts); 984 } 985 }); 986 /** api: xtype = framework_featureinfopanel */ 987 Ext.reg('framework_featureinfopanel', framework.widgets.search.FeatureInfoPanel); 988