]> dev.renevier.net Git - syp.git/blob - openlayers/lib/OpenLayers/Layer/MapGuide.js
initial commit
[syp.git] / openlayers / lib / OpenLayers / Layer / MapGuide.js
1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2  * licence.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3  * full text of the license. */
4
5 /**
6  * @requires OpenLayers/Request/XMLHttpRequest.js
7  * @requires OpenLayers/Layer/Grid.js
8  */
9
10 /**
11  * Class: OpenLayers.Layer.MapGuide
12  * Instances of OpenLayers.Layer.MapGuide are used to display
13  * data from a MapGuide OS instance.
14  *
15  * Inherits from:
16  *  - <OpenLayers.Layer.Grid>
17  */
18 OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, {
19
20     /** 
21      * APIProperty: isBaseLayer
22      * {Boolean} Treat this layer as a base layer.  Default is true.
23      **/
24     isBaseLayer: true,
25     
26     /**
27      * APIProperty: useHttpTile
28      * {Boolean} use a tile cache exposed directly via a webserver rather than the 
29            *    via mapguide server. This does require extra configuration on the Mapguide Server,
30            *    and will only work when singleTile is false. The url for the layer must be set to the
31            *    webserver path rather than the Mapguide mapagent.         
32            *    See http://trac.osgeo.org/mapguide/wiki/CodeSamples/Tiles/ServingTilesViaHttp 
33      **/
34     useHttpTile: false,
35     
36     /** 
37      * APIProperty: singleTile
38      * {Boolean} use tile server or request single tile image. 
39      **/
40     singleTile: false,
41     
42     /** 
43      * APIProperty: useOverlay
44      * {Boolean} flag to indicate if the layer should be retrieved using
45      * GETMAPIMAGE (default) or using GETDYNAMICOVERLAY requests.
46      **/
47     useOverlay: false,
48     
49     /** 
50      * APIProperty: useAsyncOverlay
51      * {Boolean} indicates if the MapGuide site supports the asynchronous 
52      * GETDYNAMICOVERLAY requests which is available in MapGuide Enterprise 2010
53      * and MapGuide Open Source v2.0.3 or higher. The newer versions of MG 
54      * is called asynchronously, allows selections to be drawn separately from 
55      * the map and offers styling options.
56      * 
57      * With older versions of MapGuide, set useAsyncOverlay=false.  Note that in
58      * this case a synchronous AJAX call is issued and the mapname and session
59      * parameters must be used to initialize the layer, not the mapdefinition
60      * parameter. Also note that this will issue a synchronous AJAX request 
61      * before the image request can be issued so the users browser may lock
62      * up if the MG Web tier does not respond in a timely fashion.
63      **/
64     useAsyncOverlay: true,
65     
66     /**
67      * Constant: TILE_PARAMS
68      * {Object} Hashtable of default parameter key/value pairs for tiled layer
69      */
70     TILE_PARAMS: {
71          operation: 'GETTILEIMAGE',
72          version: '1.2.0'
73     },
74
75     /**
76      * Constant: SINGLE_TILE_PARAMS
77      * {Object} Hashtable of default parameter key/value pairs for untiled layer
78      */
79     SINGLE_TILE_PARAMS: {
80         operation: 'GETMAPIMAGE',
81         format: 'PNG',
82         locale: 'en',
83         clip: '1',
84         version: '1.0.0'
85     },
86     
87     /**
88      * Constant: OVERLAY_PARAMS
89      * {Object} Hashtable of default parameter key/value pairs for untiled layer
90      */
91     OVERLAY_PARAMS: {
92         operation: 'GETDYNAMICMAPOVERLAYIMAGE',
93         format: 'PNG',
94         locale: 'en',
95         clip: '1',
96         version: '2.0.0'
97     },
98     
99     /** 
100      * Constant: FOLDER_PARAMS
101      * {Object} Hashtable of parameter key/value pairs which describe 
102      * the folder structure for tiles as configured in the mapguide 
103      * serverconfig.ini section [TileServiceProperties]
104      */
105     FOLDER_PARAMS: {
106         tileColumnsPerFolder: 30,
107         tileRowsPerFolder: 30,
108         format: 'png',
109         querystring: null
110     },  
111
112     /** 
113      * Property: defaultSize
114      * {<OpenLayers.Size>} Tile size as produced by MapGuide server
115      **/
116     defaultSize: new OpenLayers.Size(300,300),
117
118     /**
119      * Constructor: OpenLayers.Layer.MapGuide
120      * Create a new Mapguide layer, either tiled or untiled.  
121      *
122      * For tiled layers, the 'groupName' and 'mapDefinition' values 
123      * must be specified as parameters in the constructor.
124      *
125      * For untiled base layers, specify either combination of 'mapName' and
126      * 'session', or 'mapDefinition' and 'locale'.  
127      *
128      * For older versions of MapGuide and overlay layers, set useAsyncOverlay 
129      * to false and in this case mapName and session are required parameters 
130      * for the constructor.
131      *
132      * NOTE: MapGuide OS uses a DPI value and degrees to meters conversion 
133      * factor that are different than the defaults used in OpenLayers, 
134      * so these must be adjusted accordingly in your application.  
135      * See the MapGuide example for how to set these values for MGOS.
136      *
137      * Parameters:
138      * name - {String} Name of the layer displayed in the interface
139      * url - {String} Location of the MapGuide mapagent executable
140      *            (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi)
141      * params - {Object} hashtable of additional parameters to use. Some
142      *     parameters may require additional code on the server. The ones that
143      *     you may want to use are: 
144      *   - mapDefinition - {String} The MapGuide resource definition
145      *            (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition)
146      *   - locale - Locale setting 
147      *            (for untiled overlays layers only)
148      *   - mapName - {String} Name of the map as stored in the MapGuide session.
149      *          (for untiled layers with a session parameter only)
150      *   - session - { String} MapGuide session ID 
151      *            (for untiled overlays layers only)
152      *   - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only
153      *   - format - Image format to be returned (for untiled overlay layers only)
154      *   - showLayers - {String} A comma separated list of GUID's for the
155      *       layers to display eg: 'cvc-xcv34,453-345-345sdf'.
156      *   - hideLayers - {String} A comma separated list of GUID's for the
157      *       layers to hide eg: 'cvc-xcv34,453-345-345sdf'.
158      *   - showGroups - {String} A comma separated list of GUID's for the
159      *       groups to display eg: 'cvc-xcv34,453-345-345sdf'.
160      *   - hideGroups - {String} A comma separated list of GUID's for the
161      *       groups to hide eg: 'cvc-xcv34,453-345-345sdf'
162      *   - selectionXml - {String} A selection xml string Some server plumbing
163      *       is required to read such a value.
164      * options - {Ojbect} Hashtable of extra options to tag onto the layer; 
165      *          will vary depending if tiled or untiled maps are being requested
166      */
167     initialize: function(name, url, params, options) {
168         
169         OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);
170         
171         // unless explicitly set in options, if the layer is transparent, 
172         // it will be an overlay
173         if (options == null || options.isBaseLayer == null) {
174             this.isBaseLayer = ((this.transparent != "true") && 
175                                 (this.transparent != true));
176         }
177
178         if (options && options.useOverlay!=null) {
179           this.useOverlay = options.useOverlay;
180         }
181         
182         //initialize for untiled layers
183         if (this.singleTile) {
184           if (this.useOverlay) {
185             OpenLayers.Util.applyDefaults(
186                            this.params,
187                            this.OVERLAY_PARAMS
188                            );
189             if (!this.useAsyncOverlay) {
190               this.params.version = "1.0.0";
191             }
192           } else {
193             OpenLayers.Util.applyDefaults(
194                            this.params,
195                            this.SINGLE_TILE_PARAMS
196                            );
197           }         
198         } else {
199             //initialize for tiled layers
200             if (this.useHttpTile) {
201                 OpenLayers.Util.applyDefaults(
202                                this.params,
203                                this.FOLDER_PARAMS
204                                );
205             } else {
206                 OpenLayers.Util.applyDefaults(
207                                this.params,
208                                this.TILE_PARAMS
209                                );
210             }
211             this.setTileSize(this.defaultSize); 
212         }
213     },
214
215     /**
216      * Method: clone
217      * Create a clone of this layer
218      *
219      * Returns:
220      * {<OpenLayers.Layer.MapGuide>} An exact clone of this layer
221      */
222     clone: function (obj) {
223       if (obj == null) {
224             obj = new OpenLayers.Layer.MapGuide(this.name,
225                                            this.url,
226                                            this.params,
227                                            this.options);
228       }
229       //get all additions from superclasses
230       obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
231
232       return obj;
233     },
234
235     /**
236      * Method: addTile
237      * Creates a tile, initializes it, and adds it to the layer div. 
238      *
239      * Parameters:
240      * bounds - {<OpenLayers.Bounds>}
241      * position - {<OpenLayers.Pixel>}
242      * 
243      * Returns:
244      * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
245      */
246     addTile:function(bounds,position) {
247         return new OpenLayers.Tile.Image(this, position, bounds, 
248                                          null, this.tileSize);
249     },
250
251     /**
252      * Method: getURL
253      * Return a query string for this layer
254      *
255      * Parameters:
256      * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox 
257      *                                for the request
258      *
259      * Returns:
260      * {String} A string with the layer's url and parameters and also 
261      *          the passed-in bounds and appropriate tile size specified 
262      *          as parameters.
263      */
264     getURL: function (bounds) {
265         var url;
266         var center = bounds.getCenterLonLat();
267         var mapSize = this.map.getCurrentSize();
268
269         if (this.singleTile) {
270           //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY with
271           //dynamic map parameters
272           var params = {
273             setdisplaydpi: OpenLayers.DOTS_PER_INCH,
274             setdisplayheight: mapSize.h*this.ratio,
275             setdisplaywidth: mapSize.w*this.ratio,
276             setviewcenterx: center.lon,
277             setviewcentery: center.lat,
278             setviewscale: this.map.getScale()
279           };
280           
281           if (this.useOverlay && !this.useAsyncOverlay) {
282             //first we need to call GETVISIBLEMAPEXTENT to set the extent
283             var getVisParams = {};
284             getVisParams = OpenLayers.Util.extend(getVisParams, params);
285             getVisParams.operation = "GETVISIBLEMAPEXTENT";
286             getVisParams.version = "1.0.0";
287             getVisParams.session = this.params.session;
288             getVisParams.mapName = this.params.mapName;
289             getVisParams.format = 'text/xml';
290             url = this.getFullRequestString( getVisParams );
291             
292             OpenLayers.Request.GET({url: url, async: false});
293           }
294           //construct the full URL
295           url = this.getFullRequestString( params );
296         } else {
297
298           //tiled version
299           var currentRes = this.map.getResolution();
300           var colidx = Math.floor((bounds.left-this.maxExtent.left)/currentRes);
301           colidx = Math.round(colidx/this.tileSize.w);
302           var rowidx = Math.floor((this.maxExtent.top-bounds.top)/currentRes);
303           rowidx = Math.round(rowidx/this.tileSize.h);
304
305           if (this.useHttpTile){
306                   url = this.getImageFilePath(
307                    {
308                        tilecol: colidx,
309                        tilerow: rowidx,
310                        scaleindex: this.resolutions.length - this.map.zoom - 1
311                     });
312                   
313           } else {
314             url = this.getFullRequestString(
315                    {
316                        tilecol: colidx,
317                        tilerow: rowidx,
318                        scaleindex: this.resolutions.length - this.map.zoom - 1
319                     });
320           }
321        }
322        return url;
323     },
324
325     /**
326      * Method: getFullRequestString
327      * getFullRequestString on MapGuide layers is special, because we 
328      * do a regular expression replace on ',' in parameters to '+'.
329      * This is why it is subclassed here.
330      *
331      * Parameters:
332      * altUrl - {String} Alternative base URL to use.
333      *
334      * Returns:
335      * {String} A string with the layer's url appropriately encoded for MapGuide
336      */
337     getFullRequestString:function(newParams, altUrl) {
338         // use layer's url unless altUrl passed in
339         var url = (altUrl == null) ? this.url : altUrl;
340         
341         // if url is not a string, it should be an array of strings, 
342         //  in which case we will randomly select one of them in order
343         //  to evenly distribute requests to different urls.
344         if (typeof url == "object") {
345             url = url[Math.floor(Math.random()*url.length)];
346         }   
347         // requestString always starts with url
348         var requestString = url;        
349
350         // create a new params hashtable with all the layer params and the 
351         // new params together. then convert to string
352         var allParams = OpenLayers.Util.extend({}, this.params);
353         allParams = OpenLayers.Util.extend(allParams, newParams);
354         // ignore parameters that are already in the url search string
355         var urlParams = OpenLayers.Util.upperCaseObject(
356                             OpenLayers.Util.getArgs(url));
357         for(var key in allParams) {
358             if(key.toUpperCase() in urlParams) {
359                 delete allParams[key];
360             }
361         }
362         var paramsString = OpenLayers.Util.getParameterString(allParams);
363         
364         /* MapGuide needs '+' seperating things like bounds/height/width.
365            Since typically this is URL encoded, we use a slight hack: we
366            depend on the list-like functionality of getParameterString to
367            leave ',' only in the case of list items (since otherwise it is
368            encoded) then do a regular expression replace on the , characters
369            to '+' */
370         paramsString = paramsString.replace(/,/g, "+");
371         
372         if (paramsString != "") {
373             var lastServerChar = url.charAt(url.length - 1);
374             if ((lastServerChar == "&") || (lastServerChar == "?")) {
375                 requestString += paramsString;
376             } else {
377                 if (url.indexOf('?') == -1) {
378                     //serverPath has no ? -- add one
379                     requestString += '?' + paramsString;
380                 } else {
381                     //serverPath contains ?, so must already have paramsString at the end
382                     requestString += '&' + paramsString;
383                 }
384             }
385         }
386         return requestString;
387     },
388
389      /** 
390      * Method: getImageFilePath
391      * special handler to request mapguide tiles from an http exposed tilecache 
392      *
393      * Parameters:
394      * altUrl - {String} Alternative base URL to use.
395      *
396      * Returns:
397      * {String} A string with the url for the tile image
398      */
399     getImageFilePath:function(newParams, altUrl) {
400         // use layer's url unless altUrl passed in
401         var url = (altUrl == null) ? this.url : altUrl;
402         
403         // if url is not a string, it should be an array of strings, 
404         //  in which case we will randomly select one of them in order
405         //  to evenly distribute requests to different urls.
406         if (typeof url == "object") {
407             url = url[Math.floor(Math.random()*url.length)];
408         }   
409         // requestString always starts with url
410         var requestString = url;        
411
412         var tileRowGroup = "";
413         var tileColGroup = "";
414         
415         if (newParams.tilerow < 0) {
416           tileRowGroup =  '-';
417         }
418           
419         if (newParams.tilerow == 0 ) {
420           tileRowGroup += '0';
421         } else {
422           tileRowGroup += Math.floor(Math.abs(newParams.tilerow/this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder;
423         }
424           
425         if (newParams.tilecol < 0) {
426           tileColGroup =  '-';
427         }
428         
429         if (newParams.tilecol == 0) {
430           tileColGroup += '0';
431         } else {
432           tileColGroup += Math.floor(Math.abs(newParams.tilecol/this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder;
433         }                                       
434         
435         var tilePath = '/S' + Math.floor(newParams.scaleindex)
436                 + '/' + this.params.basemaplayergroupname
437                 + '/R' + tileRowGroup
438                 + '/C' + tileColGroup
439                 + '/' + (newParams.tilerow % this.params.tileRowsPerFolder) 
440                 + '_' + (newParams.tilecol % this.params.tileColumnsPerFolder) 
441                 + '.' + this.params.format;
442     
443         if (this.params.querystring) {
444                tilePath += "?" + this.params.querystring;
445         }
446         
447         requestString += tilePath;
448         return requestString;
449     },
450     
451     /** 
452      * Method: calculateGridLayout
453      * Generate parameters for the grid layout. This  
454      *
455      * Parameters:
456      * bounds - {<OpenLayers.Bound>}
457      * extent - {<OpenLayers.Bounds>}
458      * resolution - {Number}
459      *
460      * Returns:
461      * Object containing properties tilelon, tilelat, tileoffsetlat,
462      * tileoffsetlat, tileoffsetx, tileoffsety
463      */
464     calculateGridLayout: function(bounds, extent, resolution) {
465         var tilelon = resolution * this.tileSize.w;
466         var tilelat = resolution * this.tileSize.h;
467         
468         var offsetlon = bounds.left - extent.left;
469         var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
470         var tilecolremain = offsetlon/tilelon - tilecol;
471         var tileoffsetx = -tilecolremain * this.tileSize.w;
472         var tileoffsetlon = extent.left + tilecol * tilelon;
473         
474         var offsetlat = extent.top - bounds.top + tilelat; 
475         var tilerow = Math.floor(offsetlat/tilelat) - this.buffer;
476         var tilerowremain = tilerow - offsetlat/tilelat;
477         var tileoffsety = tilerowremain * this.tileSize.h;
478         var tileoffsetlat = extent.top - tilelat*tilerow;
479         
480         return { 
481           tilelon: tilelon, tilelat: tilelat,
482           tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat,
483           tileoffsetx: tileoffsetx, tileoffsety: tileoffsety
484         };
485     },
486     
487     CLASS_NAME: "OpenLayers.Layer.MapGuide"
488 });