2
* Copyright (C) 2008 Camptocamp
4
* This file is part of MapFish Client
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.
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.
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/>.
21
* @requires OpenLayers/Console.js
22
* @requires OpenLayers/Format/JSON.js
23
* @requires OpenLayers/Request/XMLHttpRequest.js
27
* Class: mapfish.PrintProtocol
28
* Class to communicate with the print module.
30
* This class will automatically pick the layers from the OL map and create the
31
* configuration structure accordingly.
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
39
mapfish.PrintProtocol = OpenLayers.Class({
42
* {String} the configuration as returned by the MapPrinterServlet.
48
* {Object} The complete spec to send to the servlet. You can use it to add
55
* {Object} Additional params to send in the print service Ajax calls. Can
56
* be used to set the "locale" parameter.
61
* Constructor: OpenLayers.Layer
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
68
* dpi - {Integer} the DPI resolution
69
* params - {Object} additional params to send in the Ajax calls
71
initialize: function(map, config, overrides, dpi, params) {
73
this.spec = {pages: []};
74
this.addMapParams(overrides, map, dpi);
79
* APIMethod: getAllInOneUrl
81
* Creates the URL string for generating a PDF using the print module. Using
84
* WARNING: this method has problems with accents and requests with lots of
85
* pages. But it has the advantage to work without proxy.
87
getAllInOneUrl: function() {
88
var json = new OpenLayers.Format.JSON();
89
var result = this.config.printURL + "?spec=" +
90
json.write(this.encodeForURL(this.spec));
92
result += "&" + OpenLayers.Util.getParameterString(this.params);
98
* APIMethod: createPDF
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.
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
113
createPDF: function(success, failure, context) {
114
var specTxt = new OpenLayers.Format.JSON().write(this.spec);
115
OpenLayers.Console.info(specTxt);
118
//The charset seems always to be UTF-8, regardless of the page's
119
var charset = "UTF-8"; /*+document.characterSet*/
121
var params = OpenLayers.Util.applyDefaults({
122
url: this.config.createURL
125
OpenLayers.Request.POST({
126
url: this.config.createURL,
130
'CONTENT-TYPE': "application/json; charset=" + charset
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);
139
failure.call(context, request);
142
failure.call(context, request);
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);
160
* Work around the browsers security "features" and open the given PDF
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
172
openPdf: function(answer, success, failure, context) {
173
OpenLayers.Console.info(answer.getURL);
174
var popup = window.open(answer.getURL, '_blank');
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() {
182
success.call(context);
184
failure.call(context, answer);
187
window.setTimeout(checkWindowStillOpen, 300);
189
// we can assume the user received his PDF
190
success.call(context);
193
// we can say for sure that popups are blocked
194
failure.call(context, answer);
199
* Method: addMapParams
201
* Takes an OpenLayers Map and build the configuration needed for
204
addMapParams: function(overrides, map, dpi) {
205
var spec = this.spec;
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]);
214
//allows to have some attributes overriden in fct of the resolution
215
OpenLayers.Util.extend(layerOverrides, layerOverrides[dpi]);
217
if (olLayer.getVisibility() && layerOverrides.visibility != false) {
218
var type = olLayer.CLASS_NAME;
219
var handler = mapfish.PrintProtocol.SUPPORTED_TYPES[type];
221
var layer = handler.call(this, olLayer);
223
this.applyOverrides(layer, layerOverrides);
225
if (olLayer.isBaseLayer) {
226
// base layers are always first
227
layers.unshift(layer);
232
} else if (!handler) {
233
OpenLayers.Console.error(
234
"Don't know how to print a layer of type " + type +
235
" (" + olLayer.name + ")");
242
* Method: applyOverrides
244
* Change the layer config according to the overrides.
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);
253
if (layer[key] != null) {
256
layer.customParams[key] = value;
263
* Method: convertLayer
265
* Handles the common parameters of all supported layer types.
268
* olLayer - {<OpenLayers.Layer>} The OL layer.
271
* {Object} The config for this layer
273
convertLayer: function(olLayer) {
274
var url = olLayer.url;
275
if (url instanceof Array) {
279
baseURL: mapfish.Util.relativeToAbsoluteURL(url),
280
opacity: (olLayer.opacity != null) ? olLayer.opacity : 1.0,
281
singleTile: olLayer.singleTile,
287
* Method: convertWMSLayer
289
* Builds the layer configuration from an {<OpenLayers.Layer.WMS>} layer.
290
* The structure expected from the print module is:
299
* singleTile: {boolean}
307
* olLayer - {<OpenLayers.Layer.WMS>} The OL layer.
310
* {Object} The config for this layer
312
convertWMSLayer: function(olLayer) {
313
var layer = OpenLayers.Util.extend(this.convertLayer(olLayer),
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)
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];
334
* Method: convertMapServerLayer
336
* Builds the layer configuration from an {<OpenLayers.Layer.MapServer>} layer.
337
* The structure expected from the print module is:
346
* singleTile: {boolean}
354
* olLayer - {<OpenLayers.Layer.MapServer>} The OL layer.
357
* {Object} The config for this layer
359
convertMapServerLayer: function(olLayer) {
360
var layer = OpenLayers.Util.extend(this.convertLayer(olLayer),
363
layers: mapfish.Util.fixArray(olLayer.params.LAYERS || olLayer.params.layers),
364
format: olLayer.params.FORMAT || olLayer.params.format || olLayer.DEFAULT_PARAMS.format
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];
381
* Method: convertTileCacheLayer
383
* Builds the layer configuration from an {<OpenLayers.Layer.TileCache>} layer.
384
* The structure expected from the print module is:
391
* maxExtent: [minx, miny]
392
* tileSize: [width, height]
393
* extension: {String}
394
* resolutions: [{Float}]
399
* olLayer - {<OpenLayers.Layer.TileCache>} The OL layer.
402
* {Object} The config for this layer
404
convertTileCacheLayer: function(olLayer) {
405
return OpenLayers.Util.extend(this.convertLayer(olLayer), {
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
416
* Method: convertVectorLayer
418
* Builds the layer configuration from an {OpenLayers.Layer.Vector} layer.
419
* The structure expected from the print module is:
424
* styleProperty: {String}
431
* olLayer - {OpenLayers.Layer.Vector} The OL layer.
434
* {Object} The config for this layer
436
convertVectorLayer: function(olLayer) {
437
var olFeatures = olLayer.features;
441
var formatter = new OpenLayers.Format.GeoJSON();
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);
447
if (style._printId) {
448
//this style is already known
449
styleName = style._printId;
452
style._printId = styleName = nextId++;
453
styles[styleName] = style;
455
//Make the URLs absolute
456
if(style.externalGraphic) {
457
style.externalGraphic = mapfish.Util.relativeToAbsoluteURL(style.externalGraphic);
460
var featureGeoJson = formatter.extract.feature.call(formatter, feature);
461
featureGeoJson.properties._style = styleName;
462
features.push(featureGeoJson);
464
for (var key in styles) {
465
delete styles[key]._printId;
469
"type": "FeatureCollection",
472
return OpenLayers.Util.extend(this.convertLayer(olLayer), {
475
styleProperty: '_style',
477
opacity: (olLayer.opacity != null) ? olLayer.opacity : 1.0
482
* Method: encodeForURL
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().
488
encodeForURL: function(cur) {
489
if (cur == null) return null;
490
var type = typeof cur;
492
if (type == 'string') {
493
return escape(cur.replace(/[\n]/g, "\\n"));
494
} else if (type == 'object' && cur.constructor == Array) {
496
for (var i = 0; i < cur.length; ++i) {
497
var val = this.encodeForURL(cur[i]);
498
if (val != null) array.push(val);
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') {
507
var val2 = this.encodeForURL(cur[j]);
508
if (val2 != null) hash[j] = val2;
516
CLASS_NAME: "mapfish.PrintProtocol"
520
//TODO 2.0: pass a config object instead of those 5 params
522
* APIFunction: getConfiguration
524
* Does an AJAX call to get the print configuration from the server.
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
534
mapfish.PrintProtocol.getConfiguration = function(url, success,
535
failure, context, params) {
537
params = OpenLayers.Util.extend(params, { url: url });
539
OpenLayers.Request.GET({
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);
547
success.call(context, answer);
549
failure.call(context, request);
552
failure.call(context, request);
557
failure.call(context, err);
562
mapfish.PrintProtocol.IGNORED = function() {
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