More trip planner testing with colors
[busui.git] / labs / openlayers / lib / OpenLayers / Layer / WFS.js
blob:a/labs/openlayers/lib/OpenLayers/Layer/WFS.js -> blob:b/labs/openlayers/lib/OpenLayers/Layer/WFS.js
  /* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for
  * full list of contributors). Published under the Clear BSD license.
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
   
   
  /**
  * @requires OpenLayers/Tile/WFS.js
  * @requires OpenLayers/Layer/Vector.js
  * @requires OpenLayers/Layer/Markers.js
  * @requires OpenLayers/Console.js
  */
   
  /**
  * Class: OpenLayers.Layer.WFS
  * *Deprecated*. To be removed in 3.0. Instead use OpenLayers.Layer.Vector
  * with a Protocol.WFS and one or more Strategies.
  *
  * Inherits from:
  * - <OpenLayers.Layer.Vector>
  * - <OpenLayers.Layer.Markers>
  */
  OpenLayers.Layer.WFS = OpenLayers.Class(
  OpenLayers.Layer.Vector, OpenLayers.Layer.Markers, {
   
  /**
  * APIProperty: isBaseLayer
  * {Boolean} WFS layer is not a base layer by default.
  */
  isBaseLayer: false,
   
  /**
  * Property: tile
  * {<OpenLayers.Tile.WFS>}
  */
  tile: null,
   
  /**
  * APIProperty: ratio
  * {Float} The ratio property determines the size of the serverside query
  * relative to the map viewport size. By default, we load an area twice
  * as big as the map, to allow for panning without immediately reload.
  * Setting this to 1 will cause the area of the WFS request to match
  * the map area exactly. It is recommended to set this to some number
  * at least slightly larger than 1, otherwise accidental clicks can
  * cause a data reload, by moving the map only 1 pixel.
  */
  ratio: 2,
   
  /**
  * Property: DEFAULT_PARAMS
  * {Object} Hashtable of default key/value parameters
  */
  DEFAULT_PARAMS: { service: "WFS",
  version: "1.0.0",
  request: "GetFeature"
  },
   
  /**
  * APIProperty: featureClass
  * {<OpenLayers.Feature>} If featureClass is defined, an old-style markers
  * based WFS layer is created instead of a new-style vector layer. If
  * sent, this should be a subclass of OpenLayers.Feature
  */
  featureClass: null,
   
  /**
  * APIProperty: format
  * {<OpenLayers.Format>} The format you want the data to be parsed with.
  * Must be passed in the constructor. Should be a class, not an instance.
  * This option can only be used if no featureClass is passed / vectorMode
  * is false: if a featureClass is passed, then this parameter is ignored.
  */
  format: null,
   
  /**
  * Property: formatObject
  * {<OpenLayers.Format>} Internally created/managed format object, used by
  * the Tile to parse data.
  */
  formatObject: null,
   
  /**
  * APIProperty: formatOptions
  * {Object} Hash of options which should be passed to the format when it is
  * created. Must be passed in the constructor.
  */
  formatOptions: null,
   
  /**
  * Property: vectorMode
  * {Boolean} Should be calculated automatically. Determines whether the
  * layer is in vector mode or marker mode.
  */
  vectorMode: true,
   
  /**
  * APIProperty: encodeBBOX
  * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no',
  * but some services want it that way. Default false.
  */
  encodeBBOX: false,
   
  /**
  * APIProperty: extractAttributes
  * {Boolean} Should the WFS layer parse attributes from the retrieved
  * GML? Defaults to false. If enabled, parsing is slower, but
  * attributes are available in the attributes property of
  * layer features.
  */
  extractAttributes: false,
   
  /**
  * Constructor: OpenLayers.Layer.WFS
  *
  * Parameters:
  * name - {String}
  * url - {String}
  * params - {Object}
  * options - {Object} Hashtable of extra options to tag onto the layer
  */
  initialize: function(name, url, params, options) {
  if (options == undefined) { options = {}; }
   
  if (options.featureClass ||
  !OpenLayers.Layer.Vector ||
  !OpenLayers.Feature.Vector) {
  this.vectorMode = false;
  }
   
  // Uppercase params
  params = OpenLayers.Util.upperCaseObject(params);
   
  // Turn off error reporting, browsers like Safari may work
  // depending on the setup, and we don't want an unneccesary alert.
  OpenLayers.Util.extend(options, {'reportError': false});
  var newArguments = [];
  newArguments.push(name, options);
  OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
  if (!this.renderer || !this.vectorMode) {
  this.vectorMode = false;
  if (!options.featureClass) {
  options.featureClass = OpenLayers.Feature.WFS;
  }
  OpenLayers.Layer.Markers.prototype.initialize.apply(this,
  newArguments);
  }
   
  if (this.params && this.params.typename && !this.options.typename) {
  this.options.typename = this.params.typename;
  }
   
  if (!this.options.geometry_column) {
  this.options.geometry_column = "the_geom";
  }
   
  this.params = OpenLayers.Util.applyDefaults(
  params,
  OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
  );
  this.url = url;
  },
   
   
  /**
  * APIMethod: destroy
  */
  destroy: function() {
  if (this.vectorMode) {
  OpenLayers.Layer.Vector.prototype.destroy.apply(this, arguments);
  } else {
  OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
  }
  if (this.tile) {
  this.tile.destroy();
  }
  this.tile = null;
   
  this.ratio = null;
  this.featureClass = null;
  this.format = null;
   
  if (this.formatObject && this.formatObject.destroy) {
  this.formatObject.destroy();
  }
  this.formatObject = null;
   
  this.formatOptions = null;
  this.vectorMode = null;
  this.encodeBBOX = null;
  this.extractAttributes = null;
  },
   
  /**
  * Method: setMap
  *
  * Parameters:
  * map - {<OpenLayers.Map>}
  */
  setMap: function(map) {
  if (this.vectorMode) {
  OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
   
  var options = {
  'extractAttributes': this.extractAttributes
  };
   
  OpenLayers.Util.extend(options, this.formatOptions);
  if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
  options.externalProjection = this.projection;
  options.internalProjection = this.map.getProjectionObject();
  }
   
  this.formatObject = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
  } else {
  OpenLayers.Layer.Markers.prototype.setMap.apply(this, arguments);
  }
  },
   
  /**
  * Method: moveTo
  *
  * Parameters:
  * bounds - {<OpenLayers.Bounds>}
  * zoomChanged - {Boolean}
  * dragging - {Boolean}
  */
  moveTo:function(bounds, zoomChanged, dragging) {
  if (this.vectorMode) {
  OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
  } else {
  OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
  }
   
  // don't load wfs features while dragging, wait for drag end
  if (dragging) {
  // TBD try to hide the vector layer while dragging
  // this.setVisibility(false);
  // this will probably help for panning performances
  return false;
  }
   
  if ( zoomChanged ) {
  if (this.vectorMode) {
  this.renderer.clear();
  }
  }
   
  //DEPRECATED - REMOVE IN 3.0
  // don't load data if current zoom level doesn't match
  if (this.options.minZoomLevel) {
  OpenLayers.Console.warn(OpenLayers.i18n('minZoomLevelError'));
   
  if (this.map.getZoom() < this.options.minZoomLevel) {
  return null;
  }
  }
   
  if (bounds == null) {
  bounds = this.map.getExtent();
  }
   
  var firstRendering = (this.tile == null);
   
  //does the new bounds to which we need to move fall outside of the
  // current tile's bounds?
  var outOfBounds = (!firstRendering &&
  !this.tile.bounds.containsBounds(bounds));
   
  if (zoomChanged || firstRendering || (!dragging && outOfBounds)) {
  //determine new tile bounds
  var center = bounds.getCenterLonLat();
  var tileWidth = bounds.getWidth() * this.ratio;
  var tileHeight = bounds.getHeight() * this.ratio;
  var tileBounds =
  new OpenLayers.Bounds(center.lon - (tileWidth / 2),
  center.lat - (tileHeight / 2),
  center.lon + (tileWidth / 2),
  center.lat + (tileHeight / 2));
   
  //determine new tile size
  var tileSize = this.map.getSize();
  tileSize.w = tileSize.w * this.ratio;
  tileSize.h = tileSize.h * this.ratio;
   
  //determine new position (upper left corner of new bounds)
  var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
  var pos = this.map.getLayerPxFromLonLat(ul);
   
  //formulate request url string
  var url = this.getFullRequestString();
   
  var params = null;
   
  // Cant combine "filter" and "BBOX". This is a cheap hack to help
  // people out who can't migrate to the WFS protocol immediately.
  var filter = this.params.filter || this.params.FILTER;
  if (filter) {
  params = {FILTER: filter};
  }
  else {
  params = {BBOX: this.encodeBBOX ? tileBounds.toBBOX()
  : tileBounds.toArray()};
  }
   
  if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
  var projectedBounds = tileBounds.clone();
  projectedBounds.transform(this.map.getProjectionObject(),
  this.projection);
  if (!filter){
  params.BBOX = this.encodeBBOX ? projectedBounds.toBBOX()
  : projectedBounds.toArray();
  }
  }
   
  url += "&" + OpenLayers.Util.getParameterString(params);
   
  if (!this.tile) {
  this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
  url, tileSize);
  this.addTileMonitoringHooks(this.tile);
  this.tile.draw();
  } else {
  if (this.vectorMode) {
  this.destroyFeatures();
  this.renderer.clear();
  } else {
  this.clearMarkers();
  }
  this.removeTileMonitoringHooks(this.tile);
  this.tile.destroy();
   
  this.tile = null;
  this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
  url, tileSize);
  this.addTileMonitoringHooks(this.tile);
  this.tile.draw();
  }
  }
  },
   
  /**
  * Method: addTileMonitoringHooks
  * This function takes a tile as input and adds the appropriate hooks to
  * the tile so that the layer can keep track of the loading tile
  * (making sure to check that the tile is always the layer's current
  * tile before taking any action).
  *
  * Parameters:
  * tile - {<OpenLayers.Tile>}
  */
  addTileMonitoringHooks: function(tile) {
  tile.onLoadStart = function() {
  //if this is the the layer's current tile, then trigger
  // a 'loadstart'
  if (this == this.layer.tile) {
  this.layer.events.triggerEvent("loadstart");
  }
  };
  tile.events.register("loadstart", tile, tile.onLoadStart);
   
  tile.onLoadEnd = function() {
  //if this is the the layer's current tile, then trigger
  // a 'tileloaded' and 'loadend'
  if (this == this.layer.tile) {
  this.layer.events.triggerEvent("tileloaded");
  this.layer.events.triggerEvent("loadend");
  }
  };
  tile.events.register("loadend", tile, tile.onLoadEnd);