~mortenoh/+junk/dhis2-detailed-import-export

« back to all changes in this revision

Viewing changes to gis/dhis-gis-geostat/mfbase/mapfish/core/PrintProtocol.js

  • Committer: larshelge at gmail
  • Date: 2009-03-03 16:46:36 UTC
  • Revision ID: larshelge@gmail.com-20090303164636-2sjlrquo7ib1gf7r
Initial check-in

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2008 Camptocamp
 
3
 *
 
4
 * This file is part of MapFish Client
 
5
 *
 
6
 * MapFish Client is free software: you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation, either version 3 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * MapFish Client is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with MapFish Client.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
/*
 
21
 * @requires OpenLayers/Console.js
 
22
 * @requires OpenLayers/Format/JSON.js
 
23
 * @requires OpenLayers/Request/XMLHttpRequest.js
 
24
 */
 
25
 
 
26
/**
 
27
 * Class: mapfish.PrintProtocol
 
28
 * Class to communicate with the print module.
 
29
 *
 
30
 * This class will automatically pick the layers from the OL map and create the
 
31
 * configuration structure accordingly.
 
32
 *
 
33
 * As we often want a sligtly different styling or minScale/maxScale, an
 
34
 * override functionallity is provided that allows to override the OL layers'
 
35
 * configuration, this can be used as well if a layer's URL points to a
 
36
 * TileCache service to allow the print module to access the WMS service
 
37
 * directly.
 
38
 */
 
39
mapfish.PrintProtocol = OpenLayers.Class({
 
40
    /**
 
41
     * APIProperty: config
 
42
     * {String} the configuration as returned by the MapPrinterServlet.
 
43
     */
 
44
    config: null,
 
45
 
 
46
    /**
 
47
     * APIProperty: spec
 
48
     * {Object} The complete spec to send to the servlet. You can use it to add
 
49
     * custom parameters.
 
50
     */
 
51
    spec: null,
 
52
 
 
53
    /**
 
54
     * APIProperty: params
 
55
     * {Object} Additional params to send in the print service Ajax calls. Can
 
56
     *          be used to set the "locale" parameter.
 
57
     */
 
58
    params: null,
 
59
 
 
60
    /**
 
61
     * Constructor: OpenLayers.Layer
 
62
     *
 
63
     * Parameters:
 
64
     * map - {<OpenLayers.Map>} The OL MAP.
 
65
     * config - {Object} the configuration as returned by the MapPrinterServlet.
 
66
     * overrides - {Object} the map that specify the print module overrides for
 
67
     *                      each layers.
 
68
     * dpi - {Integer} the DPI resolution
 
69
     * params - {Object} additional params to send in the Ajax calls
 
70
     */
 
71
    initialize: function(map, config, overrides, dpi, params) {
 
72
        this.config = config;
 
73
        this.spec = {pages: []};
 
74
        this.addMapParams(overrides, map, dpi);
 
75
        this.params = params;
 
76
    },
 
77
 
 
78
    /**
 
79
     * APIMethod: getAllInOneUrl
 
80
     *
 
81
     * Creates the URL string for generating a PDF using the print module. Using
 
82
     * the direct method.
 
83
     *
 
84
     * WARNING: this method has problems with accents and requests with lots of
 
85
     *          pages. But it has the advantage to work without proxy.
 
86
     */
 
87
    getAllInOneUrl: function() {
 
88
        var json = new OpenLayers.Format.JSON();
 
89
        var result = this.config.printURL + "?spec=" +
 
90
               json.write(this.encodeForURL(this.spec));
 
91
        if (this.params) {
 
92
            result += "&" + OpenLayers.Util.getParameterString(this.params);
 
93
        }
 
94
        return result;
 
95
    },
 
96
 
 
97
    /**
 
98
     * APIMethod: createPDF
 
99
     *
 
100
     * Uses AJAX to create the PDF on the server and then gets it from the
 
101
     * server. If it doesn't work (different URL and OpenLayers.ProxyHost not
 
102
     * set), try the GET direct method.   
 
103
     *
 
104
     * Parameters:
 
105
     * success - {Function} The function to call in case of success.
 
106
     * failure - {Function} The function to call in case of failure. Gets the
 
107
     *                      request object in parameter. If getURL is defined,
 
108
     *                      the popup where blocked and the PDF can still be
 
109
     *                      recovered using this URL.
 
110
     * context - {Object} The context to use to call the success of failure
 
111
     *                    method.
 
112
     */
 
113
    createPDF: function(success, failure, context) {
 
114
        var specTxt = new OpenLayers.Format.JSON().write(this.spec);
 
115
        OpenLayers.Console.info(specTxt);
 
116
 
 
117
        try {
 
118
            //The charset seems always to be UTF-8, regardless of the page's
 
119
            var charset = "UTF-8";  /*+document.characterSet*/
 
120
 
 
121
            var params = OpenLayers.Util.applyDefaults({
 
122
                url: this.config.createURL
 
123
            }, this.params);
 
124
            
 
125
            OpenLayers.Request.POST({
 
126
                url: this.config.createURL,
 
127
                data: specTxt,
 
128
                params: params,
 
129
                headers: {
 
130
                    'CONTENT-TYPE': "application/json; charset=" + charset
 
131
                },
 
132
                callback: function(request) {
 
133
                    if (request.status >= 200 && request.status < 300) {
 
134
                        var json = new OpenLayers.Format.JSON();
 
135
                        var answer = json.read(request.responseText);
 
136
                        if (answer && answer.getURL) {
 
137
                            this.openPdf(answer, success, failure, context);
 
138
                        } else {
 
139
                            failure.call(context, request);
 
140
                        }
 
141
                    } else {
 
142
                        failure.call(context, request);
 
143
                    }
 
144
                },
 
145
                scope: this
 
146
            });
 
147
        } catch (err) {
 
148
            OpenLayers.Console.warn(
 
149
                    "Cannot request the print service by AJAX. You must set " +
 
150
                    "the 'OpenLayers.ProxyHost' variable. Fallback to GET method");
 
151
            //try the other method
 
152
            window.open(this.getAllInOneUrl());
 
153
            success.call(context, err);
 
154
        }
 
155
    },
 
156
 
 
157
    /**
 
158
     * Method: openPdf
 
159
     *
 
160
     * Work around the browsers security "features" and open the given PDF
 
161
     * document.
 
162
     *
 
163
     * answer - {Object} The answer for the AJAX call to the print service.
 
164
     * success - {Function} The function to call in case of success.
 
165
     * failure - {Function} The function to call in case of failure. Gets the
 
166
     *                      request object in parameter. If getURL is defined,
 
167
     *                      the popup where blocked and the PDF can still be
 
168
     *                      recovered using this URL.
 
169
     * context - {Object} The context to use to call the success of failure
 
170
     *                    method
 
171
     */
 
172
    openPdf: function(answer, success, failure, context) {
 
173
        OpenLayers.Console.info(answer.getURL);
 
174
        var popup = window.open(answer.getURL, '_blank');
 
175
        if (popup) {
 
176
            // OK, we can at least open the popup
 
177
            if (mapfish.Util.isIE7()) {
 
178
                // IE7's anti-popup system tends to close the popup window
 
179
                // afterwards. We have to check if it has not done that.
 
180
                function checkWindowStillOpen() {
 
181
                    if (!popup.closed) {
 
182
                        success.call(context);
 
183
                    } else {
 
184
                        failure.call(context, answer);
 
185
                    }
 
186
                }
 
187
                window.setTimeout(checkWindowStillOpen, 300);
 
188
            } else {
 
189
                // we can assume the user received his PDF
 
190
                success.call(context);
 
191
            }
 
192
        } else {
 
193
            // we can say for sure that popups are blocked
 
194
            failure.call(context, answer);
 
195
        }
 
196
    },
 
197
 
 
198
    /**
 
199
     * Method: addMapParams
 
200
     *
 
201
     * Takes an OpenLayers Map and build the configuration needed for
 
202
     * the print module.
 
203
     */
 
204
    addMapParams: function(overrides, map, dpi) {
 
205
        var spec = this.spec;
 
206
        spec.dpi = dpi;
 
207
        spec.units = map.baseLayer.units;
 
208
        spec.srs = map.baseLayer.projection.getCode();
 
209
        var layers = spec.layers = [];
 
210
        for (var i = 0; i < map.layers.length; ++i) {
 
211
            var olLayer = map.layers[i];
 
212
            var layerOverrides = OpenLayers.Util.extend({}, overrides[olLayer.name]);
 
213
 
 
214
            //allows to have some attributes overriden in fct of the resolution
 
215
            OpenLayers.Util.extend(layerOverrides, layerOverrides[dpi]);
 
216
 
 
217
            if (olLayer.getVisibility() && layerOverrides.visibility != false) {
 
218
                var type = olLayer.CLASS_NAME;
 
219
                var handler = mapfish.PrintProtocol.SUPPORTED_TYPES[type];
 
220
                if (handler) {
 
221
                    var layer = handler.call(this, olLayer);
 
222
                    if (layer) {
 
223
                        this.applyOverrides(layer, layerOverrides);
 
224
 
 
225
                        if (olLayer.isBaseLayer) {
 
226
                            // base layers are always first
 
227
                            layers.unshift(layer);
 
228
                        } else {
 
229
                            layers.push(layer);
 
230
                        }
 
231
                    }
 
232
                } else if (!handler) {
 
233
                    OpenLayers.Console.error(
 
234
                            "Don't know how to print a layer of type " + type +
 
235
                            " (" + olLayer.name + ")");
 
236
                }
 
237
            }
 
238
        }
 
239
    },
 
240
 
 
241
    /**
 
242
     * Method: applyOverrides
 
243
     *
 
244
     * Change the layer config according to the overrides.
 
245
     */
 
246
    applyOverrides: function(layer, overrides) {
 
247
        for (var key in overrides) {
 
248
            if (isNaN(parseInt(key))) {
 
249
                var value = overrides[key];
 
250
                if (key == 'layers' || key == 'styles') {
 
251
                    value = mapfish.Util.fixArray(value);
 
252
                }
 
253
                if (layer[key] != null) {
 
254
                    layer[key] = value;
 
255
                } else {
 
256
                    layer.customParams[key] = value;
 
257
                }
 
258
            }
 
259
        }
 
260
    },
 
261
 
 
262
    /**
 
263
     * Method: convertLayer
 
264
     *
 
265
     * Handles the common parameters of all supported layer types.
 
266
     *
 
267
     * Parameters:
 
268
     * olLayer - {<OpenLayers.Layer>} The OL layer.
 
269
     *
 
270
     * Returns:
 
271
     * {Object} The config for this layer
 
272
     */
 
273
    convertLayer: function(olLayer) {
 
274
        var url = olLayer.url;
 
275
        if (url instanceof Array) {
 
276
            url = url[0];
 
277
        }
 
278
        return {
 
279
            baseURL: mapfish.Util.relativeToAbsoluteURL(url),
 
280
            opacity: (olLayer.opacity != null) ? olLayer.opacity : 1.0,
 
281
            singleTile: olLayer.singleTile,
 
282
            customParams: {}
 
283
        };
 
284
    },
 
285
 
 
286
    /**
 
287
     * Method: convertWMSLayer
 
288
     *
 
289
     * Builds the layer configuration from an {<OpenLayers.Layer.WMS>} layer.
 
290
     * The structure expected from the print module is:
 
291
     * (start code)
 
292
     * {
 
293
     *   type: 'WMS'
 
294
     *   baseURL: {String}
 
295
     *   layers: [{String}]
 
296
     *   styles: [{String}]
 
297
     *   format: {String}
 
298
     *   opacity: {Float}
 
299
     *   singleTile: {boolean}
 
300
     *   customParams: {
 
301
     *     {String}: {String}
 
302
     *   }
 
303
     * }
 
304
     * (end)
 
305
     *
 
306
     * Parameters:
 
307
     * olLayer - {<OpenLayers.Layer.WMS>} The OL layer.
 
308
     *
 
309
     * Returns:
 
310
     * {Object} The config for this layer
 
311
     */
 
312
    convertWMSLayer: function(olLayer) {
 
313
        var layer = OpenLayers.Util.extend(this.convertLayer(olLayer),
 
314
        {
 
315
            type: 'WMS',
 
316
            layers: mapfish.Util.fixArray(olLayer.params.LAYERS),
 
317
            format: olLayer.params.FORMAT || olLayer.DEFAULT_PARAMS.format,
 
318
            styles: mapfish.Util.fixArray(olLayer.params.STYLES ||
 
319
                                          olLayer.DEFAULT_PARAMS.styles)
 
320
        });
 
321
        for (var paramName in olLayer.params) {
 
322
            var paramNameLow = paramName.toLowerCase();
 
323
            if (olLayer.DEFAULT_PARAMS[paramNameLow] == null &&
 
324
                paramNameLow != 'layers' &&
 
325
                paramNameLow != 'width' &&
 
326
                paramNameLow != 'height' &&
 
327
                paramNameLow != 'srs') {
 
328
                layer.customParams[paramName] = olLayer.params[paramName];
 
329
            }
 
330
        }
 
331
        return layer;
 
332
    },
 
333
    /**
 
334
     * Method: convertMapServerLayer
 
335
     *
 
336
     * Builds the layer configuration from an {<OpenLayers.Layer.MapServer>} layer.
 
337
     * The structure expected from the print module is:
 
338
     * (start code)
 
339
     * {
 
340
     *   type: 'Mapserver'
 
341
     *   baseURL: {String}
 
342
     *   layers: [{String}]
 
343
     *   styles: [{String}]
 
344
     *   format: {String}
 
345
     *   opacity: {Float}
 
346
     *   singleTile: {boolean}
 
347
     *   customParams: {
 
348
     *     {String}: {String}
 
349
     *   }
 
350
     * }
 
351
     * (end)
 
352
     *
 
353
     * Parameters:
 
354
     * olLayer - {<OpenLayers.Layer.MapServer>} The OL layer.
 
355
     *
 
356
     * Returns:
 
357
     * {Object} The config for this layer
 
358
     */
 
359
    convertMapServerLayer: function(olLayer) {
 
360
        var layer = OpenLayers.Util.extend(this.convertLayer(olLayer),
 
361
        {
 
362
            type: 'MapServer',
 
363
            layers: mapfish.Util.fixArray(olLayer.params.LAYERS || olLayer.params.layers),
 
364
            format: olLayer.params.FORMAT || olLayer.params.format || olLayer.DEFAULT_PARAMS.format
 
365
        });
 
366
        for (var paramName in olLayer.params) {
 
367
            var paramNameLow = paramName.toLowerCase();
 
368
            if (olLayer.DEFAULT_PARAMS[paramNameLow] == null &&
 
369
                paramNameLow != 'layers' &&
 
370
                paramNameLow != 'format' &&
 
371
                paramNameLow != 'width' &&
 
372
                paramNameLow != 'height' &&
 
373
                paramNameLow != 'srs') {
 
374
                layer.customParams[paramName] = olLayer.params[paramName];
 
375
            }
 
376
        }
 
377
        return layer;
 
378
    },
 
379
 
 
380
    /**
 
381
     * Method: convertTileCacheLayer
 
382
     *
 
383
     * Builds the layer configuration from an {<OpenLayers.Layer.TileCache>} layer.
 
384
     * The structure expected from the print module is:
 
385
     * (start code)
 
386
     * {
 
387
     *   type: 'TileCache'
 
388
     *   baseURL: {String}
 
389
     *   layer: {String}
 
390
     *   opacity: {Float}
 
391
     *   maxExtent: [minx, miny]
 
392
     *   tileSize: [width, height]
 
393
     *   extension: {String}
 
394
     *   resolutions: [{Float}]
 
395
     * }
 
396
     * (end)
 
397
     *
 
398
     * Parameters:
 
399
     * olLayer - {<OpenLayers.Layer.TileCache>} The OL layer.
 
400
     *
 
401
     * Returns:
 
402
     * {Object} The config for this layer
 
403
     */
 
404
    convertTileCacheLayer: function(olLayer) {
 
405
        return OpenLayers.Util.extend(this.convertLayer(olLayer), {
 
406
            type: 'TileCache',
 
407
            layer: olLayer.layername,
 
408
            maxExtent: olLayer.maxExtent.toArray(),
 
409
            tileSize: [olLayer.tileSize.w, olLayer.tileSize.h],
 
410
            extension: olLayer.extension,
 
411
            resolutions: olLayer.resolutions
 
412
        });
 
413
    },
 
414
 
 
415
    /**
 
416
     * Method: convertVectorLayer
 
417
     *
 
418
     * Builds the layer configuration from an {OpenLayers.Layer.Vector} layer.
 
419
     * The structure expected from the print module is:
 
420
     * (start code)
 
421
     * {
 
422
     *   type: 'Vector'
 
423
     *   styles: {Object}
 
424
     *   styleProperty: {String}
 
425
     *   geoJson: {Object}
 
426
     *   opacity: {Float}
 
427
     * }
 
428
     * (end)
 
429
     *
 
430
     * Parameters:
 
431
     * olLayer - {OpenLayers.Layer.Vector} The OL layer.
 
432
     *
 
433
     * Returns:
 
434
     * {Object} The config for this layer
 
435
     */
 
436
    convertVectorLayer: function(olLayer) {
 
437
        var olFeatures = olLayer.features;
 
438
        var features = [];
 
439
        var styles = {
 
440
        };
 
441
        var formatter = new OpenLayers.Format.GeoJSON();
 
442
        var nextId = 1;
 
443
        for (var i = 0; i < olFeatures.length; ++i) {
 
444
            var feature = olFeatures[i];
 
445
            var style = feature.style || olLayer.style || olLayer.styleMap.createSymbolizer(feature, feature.renderIntent);
 
446
            var styleName;
 
447
            if (style._printId) {
 
448
                //this style is already known
 
449
                styleName = style._printId;
 
450
            } else {
 
451
                //new style
 
452
                style._printId = styleName = nextId++;
 
453
                styles[styleName] = style;
 
454
 
 
455
                //Make the URLs absolute
 
456
                if(style.externalGraphic) {
 
457
                    style.externalGraphic = mapfish.Util.relativeToAbsoluteURL(style.externalGraphic);
 
458
                }
 
459
            }
 
460
            var featureGeoJson = formatter.extract.feature.call(formatter, feature);
 
461
            featureGeoJson.properties._style = styleName;
 
462
            features.push(featureGeoJson);
 
463
        }
 
464
        for (var key in styles) {
 
465
            delete styles[key]._printId;
 
466
        }
 
467
 
 
468
        var geoJson = {
 
469
            "type": "FeatureCollection",
 
470
            "features": features
 
471
        };
 
472
        return OpenLayers.Util.extend(this.convertLayer(olLayer), {
 
473
            type: 'Vector',
 
474
            styles: styles,
 
475
            styleProperty: '_style',
 
476
            geoJson: geoJson,
 
477
            opacity:  (olLayer.opacity != null) ? olLayer.opacity : 1.0
 
478
        });
 
479
    },
 
480
 
 
481
    /**
 
482
     * Method: encodeForURL
 
483
     *
 
484
     * Takes a JSON structure and encode it so it can be added to an HTTP GET
 
485
     * URL. Does a better job with the accents than encodeURIComponent().
 
486
     * @param cur
 
487
     */
 
488
    encodeForURL: function(cur) {
 
489
        if (cur == null) return null;
 
490
        var type = typeof cur;
 
491
        Ext.type(cur);
 
492
        if (type == 'string') {
 
493
            return escape(cur.replace(/[\n]/g, "\\n"));
 
494
        } else if (type == 'object' && cur.constructor == Array) {
 
495
            var array = [];
 
496
            for (var i = 0; i < cur.length; ++i) {
 
497
                var val = this.encodeForURL(cur[i]);
 
498
                if (val != null) array.push(val);
 
499
            }
 
500
            return array;
 
501
        } else if (type == 'object' && cur.CLASS_NAME &&
 
502
                   cur.CLASS_NAME == 'OpenLayers.Feature.Vector') {
 
503
            return new OpenLayers.Format.WKT().write(cur);
 
504
        } else if (type == 'object') {
 
505
            var hash = {};
 
506
            for (var j in cur) {
 
507
                var val2 = this.encodeForURL(cur[j]);
 
508
                if (val2 != null) hash[j] = val2;
 
509
            }
 
510
            return hash;
 
511
        } else {
 
512
            return cur;
 
513
        }
 
514
    },
 
515
 
 
516
    CLASS_NAME: "mapfish.PrintProtocol"
 
517
});
 
518
 
 
519
 
 
520
//TODO 2.0: pass a config object instead of those 5 params
 
521
/**
 
522
 * APIFunction: getConfiguration
 
523
 *
 
524
 * Does an AJAX call to get the print configuration from the server.
 
525
 *
 
526
 * Parameters:
 
527
 * url - {String} the URL to access .../config.json
 
528
 * success - {Function} the function that will be called with the
 
529
 *           configuration object when/if its received.
 
530
 * failure - {Function} the function that is called in case of error.
 
531
 * context - {Object} The context to use when calling the callbacks.
 
532
 * params -  {Object} additional params to send in the Ajax calls
 
533
 */
 
534
mapfish.PrintProtocol.getConfiguration = function(url, success,
 
535
                                                  failure, context, params) {
 
536
    try {
 
537
        params = OpenLayers.Util.extend(params, { url: url });
 
538
 
 
539
        OpenLayers.Request.GET({
 
540
            url: url,
 
541
            params: params,
 
542
            callback: function(request) {
 
543
                if (request.status >= 200 && request.status < 300) {
 
544
                    var json = new OpenLayers.Format.JSON();
 
545
                    var answer = json.read(request.responseText);
 
546
                    if (answer) {
 
547
                        success.call(context, answer);
 
548
                    } else {
 
549
                        failure.call(context, request);
 
550
                    }
 
551
                } else {
 
552
                    failure.call(context, request);
 
553
                }
 
554
            }
 
555
        });
 
556
    } catch(err) {
 
557
        failure.call(context, err);
 
558
    }
 
559
};
 
560
 
 
561
 
 
562
mapfish.PrintProtocol.IGNORED = function() {
 
563
    return null;
 
564
};
 
565
 
 
566
mapfish.PrintProtocol.SUPPORTED_TYPES = {
 
567
    'OpenLayers.Layer': mapfish.PrintProtocol.IGNORED,
 
568
    'OpenLayers.Layer.WMS': mapfish.PrintProtocol.prototype.convertWMSLayer,
 
569
    'OpenLayers.Layer.WMS.Untiled': mapfish.PrintProtocol.prototype.convertWMSLayer,
 
570
    'OpenLayers.Layer.TileCache': mapfish.PrintProtocol.prototype.convertTileCacheLayer,
 
571
    'OpenLayers.Layer.Vector': mapfish.PrintProtocol.prototype.convertVectorLayer,
 
572
    'OpenLayers.Layer.MapServer': mapfish.PrintProtocol.prototype.convertMapServerLayer,
 
573
    'OpenLayers.Layer.MapServer.Untiled': mapfish.PrintProtocol.prototype.convertMapServerLayer
 
574
};
 
575