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