WFS.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. /* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the Clear BSD license.
  3. * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Tile/WFS.js
  7. * @requires OpenLayers/Layer/Vector.js
  8. * @requires OpenLayers/Layer/Markers.js
  9. * @requires OpenLayers/Console.js
  10. * @requires OpenLayers/Lang.js
  11. */
  12. /**
  13. * Class: OpenLayers.Layer.WFS
  14. * *Deprecated*. To be removed in 3.0. Instead use OpenLayers.Layer.Vector
  15. * with a Protocol.WFS and one or more Strategies.
  16. *
  17. * Inherits from:
  18. * - <OpenLayers.Layer.Vector>
  19. * - <OpenLayers.Layer.Markers>
  20. */
  21. OpenLayers.Layer.WFS = OpenLayers.Class(
  22. OpenLayers.Layer.Vector, OpenLayers.Layer.Markers, {
  23. /**
  24. * APIProperty: isBaseLayer
  25. * {Boolean} WFS layer is not a base layer by default.
  26. */
  27. isBaseLayer: false,
  28. /**
  29. * Property: tile
  30. * {<OpenLayers.Tile.WFS>}
  31. */
  32. tile: null,
  33. /**
  34. * APIProperty: ratio
  35. * {Float} The ratio property determines the size of the serverside query
  36. * relative to the map viewport size. By default, we load an area twice
  37. * as big as the map, to allow for panning without immediately reload.
  38. * Setting this to 1 will cause the area of the WFS request to match
  39. * the map area exactly. It is recommended to set this to some number
  40. * at least slightly larger than 1, otherwise accidental clicks can
  41. * cause a data reload, by moving the map only 1 pixel.
  42. */
  43. ratio: 2,
  44. /**
  45. * Property: DEFAULT_PARAMS
  46. * {Object} Hashtable of default key/value parameters
  47. */
  48. DEFAULT_PARAMS: { service: "WFS",
  49. version: "1.0.0",
  50. request: "GetFeature"
  51. },
  52. /**
  53. * APIProperty: featureClass
  54. * {<OpenLayers.Feature>} If featureClass is defined, an old-style markers
  55. * based WFS layer is created instead of a new-style vector layer. If
  56. * sent, this should be a subclass of OpenLayers.Feature
  57. */
  58. featureClass: null,
  59. /**
  60. * APIProperty: format
  61. * {<OpenLayers.Format>} The format you want the data to be parsed with.
  62. * Must be passed in the constructor. Should be a class, not an instance.
  63. * This option can only be used if no featureClass is passed / vectorMode
  64. * is false: if a featureClass is passed, then this parameter is ignored.
  65. */
  66. format: null,
  67. /**
  68. * Property: formatObject
  69. * {<OpenLayers.Format>} Internally created/managed format object, used by
  70. * the Tile to parse data.
  71. */
  72. formatObject: null,
  73. /**
  74. * APIProperty: formatOptions
  75. * {Object} Hash of options which should be passed to the format when it is
  76. * created. Must be passed in the constructor.
  77. */
  78. formatOptions: null,
  79. /**
  80. * Property: vectorMode
  81. * {Boolean} Should be calculated automatically. Determines whether the
  82. * layer is in vector mode or marker mode.
  83. */
  84. vectorMode: true,
  85. /**
  86. * APIProperty: encodeBBOX
  87. * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no',
  88. * but some services want it that way. Default false.
  89. */
  90. encodeBBOX: false,
  91. /**
  92. * APIProperty: extractAttributes
  93. * {Boolean} Should the WFS layer parse attributes from the retrieved
  94. * GML? Defaults to false. If enabled, parsing is slower, but
  95. * attributes are available in the attributes property of
  96. * layer features.
  97. */
  98. extractAttributes: false,
  99. /**
  100. * Constructor: OpenLayers.Layer.WFS
  101. *
  102. * Parameters:
  103. * name - {String}
  104. * url - {String}
  105. * params - {Object}
  106. * options - {Object} Hashtable of extra options to tag onto the layer
  107. */
  108. initialize: function(name, url, params, options) {
  109. if (options == undefined) { options = {}; }
  110. if (options.featureClass ||
  111. !OpenLayers.Layer.Vector ||
  112. !OpenLayers.Feature.Vector) {
  113. this.vectorMode = false;
  114. }
  115. // Uppercase params
  116. params = OpenLayers.Util.upperCaseObject(params);
  117. // Turn off error reporting, browsers like Safari may work
  118. // depending on the setup, and we don't want an unneccesary alert.
  119. OpenLayers.Util.extend(options, {'reportError': false});
  120. var newArguments = [];
  121. newArguments.push(name, options);
  122. OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
  123. if (!this.renderer || !this.vectorMode) {
  124. this.vectorMode = false;
  125. if (!options.featureClass) {
  126. options.featureClass = OpenLayers.Feature.WFS;
  127. }
  128. OpenLayers.Layer.Markers.prototype.initialize.apply(this,
  129. newArguments);
  130. }
  131. if (this.params && this.params.typename && !this.options.typename) {
  132. this.options.typename = this.params.typename;
  133. }
  134. if (!this.options.geometry_column) {
  135. this.options.geometry_column = "the_geom";
  136. }
  137. this.params = OpenLayers.Util.applyDefaults(
  138. params,
  139. OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
  140. );
  141. this.url = url;
  142. },
  143. /**
  144. * APIMethod: destroy
  145. */
  146. destroy: function() {
  147. if (this.vectorMode) {
  148. OpenLayers.Layer.Vector.prototype.destroy.apply(this, arguments);
  149. } else {
  150. OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
  151. }
  152. if (this.tile) {
  153. this.tile.destroy();
  154. }
  155. this.tile = null;
  156. this.ratio = null;
  157. this.featureClass = null;
  158. this.format = null;
  159. if (this.formatObject && this.formatObject.destroy) {
  160. this.formatObject.destroy();
  161. }
  162. this.formatObject = null;
  163. this.formatOptions = null;
  164. this.vectorMode = null;
  165. this.encodeBBOX = null;
  166. this.extractAttributes = null;
  167. },
  168. /**
  169. * Method: setMap
  170. *
  171. * Parameters:
  172. * map - {<OpenLayers.Map>}
  173. */
  174. setMap: function(map) {
  175. if (this.vectorMode) {
  176. OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
  177. var options = {
  178. 'extractAttributes': this.extractAttributes
  179. };
  180. OpenLayers.Util.extend(options, this.formatOptions);
  181. if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
  182. options.externalProjection = this.projection;
  183. options.internalProjection = this.map.getProjectionObject();
  184. }
  185. this.formatObject = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
  186. } else {
  187. OpenLayers.Layer.Markers.prototype.setMap.apply(this, arguments);
  188. }
  189. },
  190. /**
  191. * Method: moveTo
  192. *
  193. * Parameters:
  194. * bounds - {<OpenLayers.Bounds>}
  195. * zoomChanged - {Boolean}
  196. * dragging - {Boolean}
  197. */
  198. moveTo:function(bounds, zoomChanged, dragging) {
  199. if (this.vectorMode) {
  200. OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
  201. } else {
  202. OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
  203. }
  204. // don't load wfs features while dragging, wait for drag end
  205. if (dragging) {
  206. // TBD try to hide the vector layer while dragging
  207. // this.setVisibility(false);
  208. // this will probably help for panning performances
  209. return false;
  210. }
  211. if ( zoomChanged ) {
  212. if (this.vectorMode) {
  213. this.renderer.clear();
  214. }
  215. }
  216. //DEPRECATED - REMOVE IN 3.0
  217. // don't load data if current zoom level doesn't match
  218. if (this.options.minZoomLevel) {
  219. OpenLayers.Console.warn(OpenLayers.i18n('minZoomLevelError'));
  220. if (this.map.getZoom() < this.options.minZoomLevel) {
  221. return null;
  222. }
  223. }
  224. if (bounds == null) {
  225. bounds = this.map.getExtent();
  226. }
  227. var firstRendering = (this.tile == null);
  228. //does the new bounds to which we need to move fall outside of the
  229. // current tile's bounds?
  230. var outOfBounds = (!firstRendering &&
  231. !this.tile.bounds.containsBounds(bounds));
  232. if (zoomChanged || firstRendering || (!dragging && outOfBounds)) {
  233. //determine new tile bounds
  234. var center = bounds.getCenterLonLat();
  235. var tileWidth = bounds.getWidth() * this.ratio;
  236. var tileHeight = bounds.getHeight() * this.ratio;
  237. var tileBounds =
  238. new OpenLayers.Bounds(center.lon - (tileWidth / 2),
  239. center.lat - (tileHeight / 2),
  240. center.lon + (tileWidth / 2),
  241. center.lat + (tileHeight / 2));
  242. //determine new tile size
  243. var tileSize = this.map.getSize();
  244. tileSize.w = tileSize.w * this.ratio;
  245. tileSize.h = tileSize.h * this.ratio;
  246. //determine new position (upper left corner of new bounds)
  247. var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
  248. var pos = this.map.getLayerPxFromLonLat(ul);
  249. //formulate request url string
  250. var url = this.getFullRequestString();
  251. var params = null;
  252. // Cant combine "filter" and "BBOX". This is a cheap hack to help
  253. // people out who can't migrate to the WFS protocol immediately.
  254. var filter = this.params.filter || this.params.FILTER;
  255. if (filter) {
  256. params = {FILTER: filter};
  257. }
  258. else {
  259. params = {BBOX: this.encodeBBOX ? tileBounds.toBBOX()
  260. : tileBounds.toArray()};
  261. }
  262. if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
  263. var projectedBounds = tileBounds.clone();
  264. projectedBounds.transform(this.map.getProjectionObject(),
  265. this.projection);
  266. if (!filter){
  267. params.BBOX = this.encodeBBOX ? projectedBounds.toBBOX()
  268. : projectedBounds.toArray();
  269. }
  270. }
  271. url += "&" + OpenLayers.Util.getParameterString(params);
  272. if (!this.tile) {
  273. this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
  274. url, tileSize);
  275. this.addTileMonitoringHooks(this.tile);
  276. this.tile.draw();
  277. } else {
  278. if (this.vectorMode) {
  279. this.destroyFeatures();
  280. this.renderer.clear();
  281. } else {
  282. this.clearMarkers();
  283. }
  284. this.removeTileMonitoringHooks(this.tile);
  285. this.tile.destroy();
  286. this.tile = null;
  287. this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
  288. url, tileSize);
  289. this.addTileMonitoringHooks(this.tile);
  290. this.tile.draw();
  291. }
  292. }
  293. },
  294. /**
  295. * Method: addTileMonitoringHooks
  296. * This function takes a tile as input and adds the appropriate hooks to
  297. * the tile so that the layer can keep track of the loading tile
  298. * (making sure to check that the tile is always the layer's current
  299. * tile before taking any action).
  300. *
  301. * Parameters:
  302. * tile - {<OpenLayers.Tile>}
  303. */
  304. addTileMonitoringHooks: function(tile) {
  305. tile.onLoadStart = function() {
  306. //if this is the the layer's current tile, then trigger
  307. // a 'loadstart'
  308. if (this == this.layer.tile) {
  309. this.layer.events.triggerEvent("loadstart");
  310. }
  311. };
  312. tile.events.register("loadstart", tile, tile.onLoadStart);
  313. tile.onLoadEnd = function() {
  314. //if this is the the layer's current tile, then trigger
  315. // a 'tileloaded' and 'loadend'
  316. if (this == this.layer.tile) {
  317. this.layer.events.triggerEvent("tileloaded");
  318. this.layer.events.triggerEvent("loadend");
  319. }
  320. };
  321. tile.events.register("loadend", tile, tile.onLoadEnd);
  322. tile.events.register("unload", tile, tile.onLoadEnd);
  323. },
  324. /**
  325. * Method: removeTileMonitoringHooks
  326. * This function takes a tile as input and removes the tile hooks
  327. * that were added in addTileMonitoringHooks()
  328. *
  329. * Parameters:
  330. * tile - {<OpenLayers.Tile>}
  331. */
  332. removeTileMonitoringHooks: function(tile) {
  333. tile.unload();
  334. tile.events.un({
  335. "loadstart": tile.onLoadStart,
  336. "loadend": tile.onLoadEnd,
  337. "unload": tile.onLoadEnd,
  338. scope: tile
  339. });
  340. },
  341. /**
  342. * Method: onMapResize
  343. * Call the onMapResize method of the appropriate parent class.
  344. */
  345. onMapResize: function() {
  346. if(this.vectorMode) {
  347. OpenLayers.Layer.Vector.prototype.onMapResize.apply(this,
  348. arguments);
  349. } else {
  350. OpenLayers.Layer.Markers.prototype.onMapResize.apply(this,
  351. arguments);
  352. }
  353. },
  354. /**
  355. * Method: display
  356. * Call the display method of the appropriate parent class.
  357. */
  358. display: function() {
  359. if(this.vectorMode) {
  360. OpenLayers.Layer.Vector.prototype.display.apply(this,
  361. arguments);
  362. } else {
  363. OpenLayers.Layer.Markers.prototype.display.apply(this,
  364. arguments);
  365. }
  366. },
  367. /**
  368. * APIMethod: mergeNewParams
  369. * Modify parameters for the layer and redraw.
  370. *
  371. * Parameters:
  372. * newParams - {Object}
  373. */
  374. mergeNewParams:function(newParams) {
  375. var upperParams = OpenLayers.Util.upperCaseObject(newParams);
  376. var newArguments = [upperParams];
  377. return OpenLayers.Layer.HTTPRequest.prototype.mergeNewParams.apply(this,
  378. newArguments);
  379. },
  380. /**
  381. * APIMethod: clone
  382. *
  383. * Parameters:
  384. * obj - {Object}
  385. *
  386. * Returns:
  387. * {<OpenLayers.Layer.WFS>} An exact clone of this OpenLayers.Layer.WFS
  388. */
  389. clone: function (obj) {
  390. if (obj == null) {
  391. obj = new OpenLayers.Layer.WFS(this.name,
  392. this.url,
  393. this.params,
  394. this.getOptions());
  395. }
  396. //get all additions from superclasses
  397. if (this.vectorMode) {
  398. obj = OpenLayers.Layer.Vector.prototype.clone.apply(this, [obj]);
  399. } else {
  400. obj = OpenLayers.Layer.Markers.prototype.clone.apply(this, [obj]);
  401. }
  402. // copy/set any non-init, non-simple values here
  403. return obj;
  404. },
  405. /**
  406. * APIMethod: getFullRequestString
  407. * combine the layer's url with its params and these newParams.
  408. *
  409. * Add the SRS parameter from 'projection' -- this is probably
  410. * more eloquently done via a setProjection() method, but this
  411. * works for now and always.
  412. *
  413. * Parameters:
  414. * newParams - {Object}
  415. * altUrl - {String} Use this as the url instead of the layer's url
  416. */
  417. getFullRequestString:function(newParams, altUrl) {
  418. var projectionCode = this.projection.getCode() || this.map.getProjection();
  419. this.params.SRS = (projectionCode == "none") ? null : projectionCode;
  420. return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
  421. this, arguments);
  422. },
  423. /**
  424. * APIMethod: commit
  425. * Write out the data to a WFS server.
  426. */
  427. commit: function() {
  428. if (!this.writer) {
  429. var options = {};
  430. if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
  431. options.externalProjection = this.projection;
  432. options.internalProjection = this.map.getProjectionObject();
  433. }
  434. this.writer = new OpenLayers.Format.WFS(options,this);
  435. }
  436. var data = this.writer.write(this.features);
  437. OpenLayers.Request.POST({
  438. url: this.url,
  439. data: data,
  440. success: this.commitSuccess,
  441. failure: this.commitFailure,
  442. scope: this
  443. });
  444. },
  445. /**
  446. * Method: commitSuccess
  447. * Called when the Ajax request returns a response
  448. *
  449. * Parameters:
  450. * response - {XmlNode} from server
  451. */
  452. commitSuccess: function(request) {
  453. var response = request.responseText;
  454. if (response.indexOf('SUCCESS') != -1) {
  455. this.commitReport(OpenLayers.i18n("commitSuccess", {'response':response}));
  456. for(var i = 0; i < this.features.length; i++) {
  457. this.features[i].state = null;
  458. }
  459. // TBD redraw the layer or reset the state of features
  460. // foreach features: set state to null
  461. } else if (response.indexOf('FAILED') != -1 ||
  462. response.indexOf('Exception') != -1) {
  463. this.commitReport(OpenLayers.i18n("commitFailed", {'response':response}));
  464. }
  465. },
  466. /**
  467. * Method: commitFailure
  468. * Called when the Ajax request fails
  469. *
  470. * Parameters:
  471. * response - {XmlNode} from server
  472. */
  473. commitFailure: function(request) {},
  474. /**
  475. * APIMethod: commitReport
  476. * Called with a 'success' message if the commit succeeded, otherwise
  477. * a failure message, and the full request text as a second parameter.
  478. * Override this function to provide custom transaction reporting.
  479. *
  480. * string - {String} reporting string
  481. * response - {String} full XML response
  482. */
  483. commitReport: function(string, response) {
  484. OpenLayers.Console.userError(string);
  485. },
  486. /**
  487. * APIMethod: refresh
  488. * Refreshes all the features of the layer
  489. */
  490. refresh: function() {
  491. if (this.tile) {
  492. if (this.vectorMode) {
  493. this.renderer.clear();
  494. this.features.length = 0;
  495. } else {
  496. this.clearMarkers();
  497. this.markers.length = 0;
  498. }
  499. this.tile.draw();
  500. }
  501. },
  502. /**
  503. * APIMethod: getDataExtent
  504. * Calculates the max extent which includes all of the layer data.
  505. *
  506. * Returns:
  507. * {<OpenLayers.Bounds>}
  508. */
  509. getDataExtent: function () {
  510. var extent;
  511. //get all additions from superclasses
  512. if (this.vectorMode) {
  513. extent = OpenLayers.Layer.Vector.prototype.getDataExtent.apply(this);
  514. } else {
  515. extent = OpenLayers.Layer.Markers.prototype.getDataExtent.apply(this);
  516. }
  517. return extent;
  518. },
  519. /**
  520. * APIMethod: setOpacity
  521. * Call the setOpacity method of the appropriate parent class to set the
  522. * opacity.
  523. *
  524. * Parameter:
  525. * opacity - {Float}
  526. */
  527. setOpacity: function (opacity) {
  528. if (this.vectorMode) {
  529. OpenLayers.Layer.Vector.prototype.setOpacity.apply(this, [opacity]);
  530. } else {
  531. OpenLayers.Layer.Markers.prototype.setOpacity.apply(this, [opacity]);
  532. }
  533. },
  534. CLASS_NAME: "OpenLayers.Layer.WFS"
  535. });