ArcIMS.js 14 KB

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