ArcIMS.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  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/Layer/Grid.js
  7. * @requires OpenLayers/Tile/Image.js
  8. * @requires OpenLayers/Format/ArcXML.js
  9. * @requires OpenLayers/Request.js
  10. */
  11. /**
  12. * Class: OpenLayers.Layer.ArcIMS
  13. * Instances of OpenLayers.Layer.ArcIMS are used to display data from ESRI ArcIMS
  14. * Mapping Services. Create a new ArcIMS layer with the <OpenLayers.Layer.ArcIMS>
  15. * constructor.
  16. *
  17. * Inherits from:
  18. * - <OpenLayers.Layer.Grid>
  19. */
  20. OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
  21. /**
  22. * Constant: DEFAULT_PARAMS
  23. * {Object} Default query string parameters.
  24. */
  25. DEFAULT_PARAMS: {
  26. ClientVersion: "9.2",
  27. ServiceName: ''
  28. },
  29. /**
  30. * APIProperty: tileSize
  31. * {<OpenLayers.Size>} Size for tiles. Default is 512x512.
  32. */
  33. tileSize: null,
  34. /**
  35. * APIProperty: featureCoordSys
  36. * {String} Code for feature coordinate system. Default is "4326".
  37. */
  38. featureCoordSys: "4326",
  39. /**
  40. * APIProperty: filterCoordSys
  41. * {String} Code for filter coordinate system. Default is "4326".
  42. */
  43. filterCoordSys: "4326",
  44. /**
  45. * APIProperty: layers
  46. * {Array} An array of objects with layer properties.
  47. */
  48. layers: null,
  49. /**
  50. * APIProperty: async
  51. * {Boolean} Request images asynchronously. Default is true.
  52. */
  53. async: true,
  54. /**
  55. * APIProperty: name
  56. * {String} Layer name. Default is "ArcIMS".
  57. */
  58. name: "ArcIMS",
  59. /**
  60. * APIProperty: isBaseLayer
  61. * {Boolean} The layer is a base layer. Default is true.
  62. */
  63. isBaseLayer: true,
  64. /**
  65. * Constant: DEFAULT_OPTIONS
  66. * {Object} Default layers properties.
  67. */
  68. DEFAULT_OPTIONS: {
  69. tileSize: new OpenLayers.Size(512, 512),
  70. featureCoordSys: "4326",
  71. filterCoordSys: "4326",
  72. layers: null,
  73. isBaseLayer: true,
  74. async: true,
  75. name: "ArcIMS"
  76. },
  77. /**
  78. * Constructor: OpenLayers.Layer.ArcIMS
  79. * Create a new ArcIMS layer object.
  80. *
  81. * Example:
  82. * (code)
  83. * var arcims = new OpenLayers.Layer.ArcIMS(
  84. * "Global Sample",
  85. * "http://sample.avencia.com/servlet/com.esri.esrimap.Esrimap",
  86. * {
  87. * service: "OpenLayers_Sample",
  88. * layers: [
  89. * // layers to manipulate
  90. * {id: "1", visible: true}
  91. * ]
  92. * }
  93. * );
  94. * (end)
  95. *
  96. * Parameters:
  97. * name - {String} A name for the layer
  98. * url - {String} Base url for the ArcIMS server
  99. * options - {Object} Optional object with properties to be set on the
  100. * layer.
  101. */
  102. initialize: function(name, url, options) {
  103. this.tileSize = new OpenLayers.Size(512, 512);
  104. // parameters
  105. this.params = OpenLayers.Util.applyDefaults(
  106. {ServiceName: options.serviceName},
  107. this.DEFAULT_PARAMS
  108. );
  109. this.options = OpenLayers.Util.applyDefaults(
  110. options, this.DEFAULT_OPTIONS
  111. );
  112. OpenLayers.Layer.Grid.prototype.initialize.apply(
  113. this, [name, url, this.params, options]
  114. );
  115. //layer is transparent
  116. if (this.transparent) {
  117. // unless explicitly set in options, make layer an overlay
  118. if (!this.isBaseLayer) {
  119. this.isBaseLayer = false;
  120. }
  121. // jpegs can never be transparent, so intelligently switch the
  122. // format, depending on the browser's capabilities
  123. if (this.format == "image/jpeg") {
  124. this.format = OpenLayers.Util.alphaHack() ? "image/gif" : "image/png";
  125. }
  126. }
  127. // create an empty layer list if no layers specified in the options
  128. if (this.options.layers === null) {
  129. this.options.layers = [];
  130. }
  131. },
  132. /**
  133. * Method: destroy
  134. * Destroy this layer
  135. */
  136. destroy: function() {
  137. // for now, nothing special to do here.
  138. OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);
  139. },
  140. /**
  141. * Method: getURL
  142. * Return an image url this layer.
  143. *
  144. * Parameters:
  145. * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the
  146. * request.
  147. *
  148. * Returns:
  149. * {String} A string with the map image's url.
  150. */
  151. getURL: function(bounds) {
  152. var url = "";
  153. bounds = this.adjustBounds(bounds);
  154. // create an arcxml request to generate the image
  155. var axlReq = new OpenLayers.Format.ArcXML(
  156. OpenLayers.Util.extend(this.options, {
  157. requesttype: "image",
  158. envelope: bounds.toArray(),
  159. tileSize: this.tileSize
  160. })
  161. );
  162. // create a synchronous ajax request to get an arcims image
  163. var req = new OpenLayers.Request.POST({
  164. url: this.getFullRequestString(),
  165. data: axlReq.write(),
  166. async: false
  167. });
  168. // if the response exists
  169. if (req != null) {
  170. var doc = req.responseXML;
  171. if (!doc || !doc.documentElement) {
  172. doc = req.responseText;
  173. }
  174. // create a new arcxml format to read the response
  175. var axlResp = new OpenLayers.Format.ArcXML();
  176. var arcxml = axlResp.read(doc);
  177. url = this.getUrlOrImage(arcxml.image.output);
  178. }
  179. return url;
  180. },
  181. /**
  182. * Method: getURLasync
  183. * Get an image url this layer asynchronously, and execute a callback
  184. * when the image url is generated.
  185. *
  186. * Parameters:
  187. * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the
  188. * request.
  189. * scope - {Object} The scope of the callback method.
  190. * prop - {String} The name of the property in the scoped object to
  191. * recieve the image url.
  192. * callback - {Function} Function to call when image url is retrieved.
  193. */
  194. getURLasync: function(bounds, scope, prop, callback) {
  195. bounds = this.adjustBounds(bounds);
  196. // create an arcxml request to generate the image
  197. var axlReq = new OpenLayers.Format.ArcXML(
  198. OpenLayers.Util.extend(this.options, {
  199. requesttype: "image",
  200. envelope: bounds.toArray(),
  201. tileSize: this.tileSize
  202. })
  203. );
  204. // create an asynchronous ajax request to get an arcims image
  205. OpenLayers.Request.POST({
  206. url: this.getFullRequestString(),
  207. async: true,
  208. data: axlReq.write(),
  209. callback: function(req) {
  210. // process the response from ArcIMS, and call the callback function
  211. // to set the image URL
  212. var doc = req.responseXML;
  213. if (!doc || !doc.documentElement) {
  214. doc = req.responseText;
  215. }
  216. // create a new arcxml format to read the response
  217. var axlResp = new OpenLayers.Format.ArcXML();
  218. var arcxml = axlResp.read(doc);
  219. scope[prop] = this.getUrlOrImage(arcxml.image.output);
  220. // call the callback function to recieve the updated property on the
  221. // scoped object
  222. callback.apply(scope);
  223. },
  224. scope: this
  225. });
  226. },
  227. /**
  228. * Method: getUrlOrImage
  229. * Extract a url or image from the ArcXML image output.
  230. *
  231. * Parameters:
  232. * output - {Object} The image.output property of the object returned from
  233. * the ArcXML format read method.
  234. *
  235. * Returns:
  236. * {String} A URL for an image (potentially with the data protocol).
  237. */
  238. getUrlOrImage: function(output) {
  239. var ret = "";
  240. if(output.url) {
  241. // If the image response output url is a string, then the image
  242. // data is not inline.
  243. ret = output.url;
  244. } else if(output.data) {
  245. // The image data is inline and base64 encoded, create a data
  246. // url for the image. This will only work for small images,
  247. // due to browser url length limits.
  248. ret = "data:image/" + output.type +
  249. ";base64," + output.data;
  250. }
  251. return ret;
  252. },
  253. /**
  254. * Method: setLayerQuery
  255. * Set the query definition on this layer. Query definitions are used to
  256. * render parts of the spatial data in an image, and can be used to
  257. * filter features or layers in the ArcIMS service.
  258. *
  259. * Parameters:
  260. * id - {String} The ArcIMS layer ID.
  261. * queryDef - {Object} The query definition to apply to this layer.
  262. */
  263. setLayerQuery: function(id, querydef) {
  264. // find the matching layer, if it exists
  265. for (var lyr = 0; lyr < this.options.layers.length; lyr++) {
  266. if (id == this.options.layers[lyr].id) {
  267. // replace this layer definition
  268. this.options.layers[lyr].query = querydef;
  269. return;
  270. }
  271. }
  272. // no layer found, create a new definition
  273. this.options.layers.push({id: id, visible: true, query: querydef});
  274. },
  275. /**
  276. * Method: getFeatureInfo
  277. * Get feature information from ArcIMS. Using the applied geometry, apply
  278. * the options to the query (buffer, area/envelope intersection), and
  279. * query the ArcIMS service.
  280. *
  281. * A note about accuracy:
  282. * ArcIMS interprets the accuracy attribute in feature requests to be
  283. * something like the 'modulus' operator on feature coordinates,
  284. * applied to the database geometry of the feature. It doesn't round,
  285. * so your feature coordinates may be up to (1 x accuracy) offset from
  286. * the actual feature coordinates. If the accuracy of the layer is not
  287. * specified, the accuracy will be computed to be approximately 1
  288. * feature coordinate per screen pixel.
  289. *
  290. * Parameters:
  291. * geometry - {<OpenLayers.LonLat>} or {<OpenLayers.Geometry.Polygon>} The
  292. * geometry to use when making the query. This should be a closed
  293. * polygon for behavior approximating a free selection.
  294. * layer - {Object} The ArcIMS layer definition. This is an anonymous object
  295. * that looks like:
  296. * (code)
  297. * {
  298. * id: "ArcXML layer ID", // the ArcXML layer ID
  299. * query: {
  300. * where: "STATE = 'PA'", // the where clause of the query
  301. * accuracy: 100 // the accuracy of the returned feature
  302. * }
  303. * }
  304. * (end)
  305. * options - {Object} Object with non-default properties to set on the layer.
  306. * Supported properties are buffer, callback, scope, and any other
  307. * properties applicable to the ArcXML format. Set the 'callback' and
  308. * 'scope' for an object and function to recieve the parsed features
  309. * from ArcIMS.
  310. */
  311. getFeatureInfo: function(geometry, layer, options) {
  312. // set the buffer to 1 unit (dd/m/ft?) by default
  313. var buffer = options.buffer || 1;
  314. // empty callback by default
  315. var callback = options.callback || function() {};
  316. // default scope is window (global)
  317. var scope = options.scope || window;
  318. // apply these option to the request options
  319. var requestOptions = {};
  320. OpenLayers.Util.extend(requestOptions, this.options);
  321. // this is a feature request
  322. requestOptions.requesttype = "feature";
  323. if (geometry instanceof OpenLayers.LonLat) {
  324. // create an envelope if the geometry is really a lon/lat
  325. requestOptions.polygon = null;
  326. requestOptions.envelope = [
  327. geometry.lon - buffer,
  328. geometry.lat - buffer,
  329. geometry.lon + buffer,
  330. geometry.lat + buffer
  331. ];
  332. } else if (geometry instanceof OpenLayers.Geometry.Polygon) {
  333. // use the polygon assigned, and empty the envelope
  334. requestOptions.envelope = null;
  335. requestOptions.polygon = geometry;
  336. }
  337. // create an arcxml request to get feature requests
  338. var arcxml = new OpenLayers.Format.ArcXML(requestOptions);
  339. // apply any get feature options to the arcxml request
  340. OpenLayers.Util.extend(arcxml.request.get_feature, options);
  341. arcxml.request.get_feature.layer = layer.id;
  342. if (typeof layer.query.accuracy == "number") {
  343. // set the accuracy if it was specified
  344. arcxml.request.get_feature.query.accuracy = layer.query.accuracy;
  345. } else {
  346. // guess that the accuracy is 1 per screen pixel
  347. var mapCenter = this.map.getCenter();
  348. var viewPx = this.map.getViewPortPxFromLonLat(mapCenter);
  349. viewPx.x++;
  350. var mapOffCenter = this.map.getLonLatFromPixel(viewPx);
  351. arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon;
  352. }
  353. // set the get_feature query to be the same as the layer passed in
  354. arcxml.request.get_feature.query.where = layer.query.where;
  355. // use area_intersection
  356. arcxml.request.get_feature.query.spatialfilter.relation = "area_intersection";
  357. // create a new asynchronous request to get the feature info
  358. OpenLayers.Request.POST({
  359. url: this.getFullRequestString({'CustomService': 'Query'}),
  360. data: arcxml.write(),
  361. callback: function(request) {
  362. // parse the arcxml response
  363. var response = arcxml.parseResponse(request.responseText);
  364. if (!arcxml.iserror()) {
  365. // if the arcxml is not an error, call the callback with the features parsed
  366. callback.call(scope, response.features);
  367. } else {
  368. // if the arcxml is an error, return null features selected
  369. callback.call(scope, null);
  370. }
  371. }
  372. });
  373. },
  374. /**
  375. * Method: clone
  376. * Create a clone of this layer
  377. *
  378. * Returns:
  379. * {<OpenLayers.Layer.ArcIMS>} An exact clone of this layer
  380. */
  381. clone: function (obj) {
  382. if (obj == null) {
  383. obj = new OpenLayers.Layer.ArcIMS(this.name,
  384. this.url,
  385. this.getOptions());
  386. }
  387. //get all additions from superclasses
  388. obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
  389. // copy/set any non-init, non-simple values here
  390. return obj;
  391. },
  392. CLASS_NAME: "OpenLayers.Layer.ArcIMS"
  393. });