/* Copyright (c) 2015-present The Open Source Geospatial Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Provides an interface to a Mapfish or GeoServer print module.
*
* @class GeoExt.data.MapfishPrintProvider
*/
Ext.define('GeoExt.data.MapfishPrintProvider', {
extend: 'Ext.Base',
mixins: ['Ext.mixin.Observable', 'GeoExt.mixin.SymbolCheck'],
requires: [
'GeoExt.data.model.print.Capability',
'Ext.data.JsonStore',
'Ext.data.Store',
'Ext.data.proxy.Ajax',
'Ext.data.proxy.JsonP',
],
// <debug>
symbols: [
'ol.Collection',
'ol.geom.Polygon.fromExtent',
'ol.Feature',
'ol.layer.Layer#getSource',
'ol.layer.Group',
'ol.source.Vector.prototype.addFeature',
'ol.View#calculateExtent',
],
// </debug>
/**
* @event ready
* Fires after the PrintCapability store is loaded.
*
* @param {GeoExt.data.MapfishPrintProvider} provider The
* GeoExt.data.MapfishPrintProvider itself
*/
config: {
capabilities: null,
url: '',
useJsonp: true,
},
inheritableStatics: {
/**
* An array of objects specifying a serializer and a connected
* OpenLayers class. This should not be manipulated by hand, but rather
* with the method #registerSerializer.
*
* @private
*/
_serializers: [],
/**
* Registers the passed serializer class as an appropriate serializer
* for the passed OpenLayers source class.
*
* @param {ol.source.Source} olSourceCls The OpenLayers source class
* that the passed serializer can serialize.
* @param {GeoExt.data.serializer.Base} serializerCls The serializer
* that can serialize the passed source.
*/
registerSerializer: function (olSourceCls, serializerCls) {
const staticMe = GeoExt.data.MapfishPrintProvider;
staticMe._serializers.push({
olSourceCls: olSourceCls,
serializerCls: serializerCls,
});
},
/**
* Unregisters the passed serializer class from the array of available
* serializers. This may be useful if you want to register a new
* serializer that is different from a serializer that we provide.
*
* @param {GeoExt.data.serializer.Base} serializerCls The serializer
* that can serialize the passed source.
* @return {boolean} Whether we could unregister the serializer.
*/
unregisterSerializer: function (serializerCls) {
const available = GeoExt.data.MapfishPrintProvider._serializers;
let index;
Ext.each(available, function (candidate, idx) {
if (candidate.serializerCls === serializerCls) {
index = idx;
return false; // break early
}
});
if (Ext.isDefined(index)) {
Ext.Array.removeAt(available, index);
return true;
}
return false;
},
/**
* Returns a GeoExt.data.serializer.Base capable of serializing the
* passed source instance or undefined, if no such serializer was
* previously registered.
*
* @param {ol.source.Source} source The source instance to find a
* serializer for.
* @return {GeoExt.data.serializer.Base} A serializer for the passed
* source or `undefined`.
*/
findSerializerBySource: function (source) {
const available = GeoExt.data.MapfishPrintProvider._serializers;
let serializer;
Ext.each(available, function (candidate) {
if (source instanceof candidate.olSourceCls) {
serializer = candidate.serializerCls;
return false; // break early
}
});
if (!serializer) {
Ext.log.warn(
"Couldn't find a suitable serializer for source." +
' Did you require() an appropriate serializer class?',
);
}
return serializer;
},
/**
* Will return an array of ol-layers by the given collection. Layers
* contained in `ol.layer.Group`s get extracted and groups get removed
* from returning array
*
* @param {GeoExt.data.store.Layers | ol.Collection | Array<ol.layer.Base>} coll
* The 'collection' of layers to get as array. If passed as
* ol.Collection, all items must be `ol.layer.Base`.
* @return {Array} The flat layers array.
*/
getLayerArray: function (coll) {
const me = this;
let inputLayers = [];
const outputLayers = [];
if (coll instanceof GeoExt.data.store.Layers) {
coll.each(function (layerRec) {
const layer = layerRec.getOlLayer();
inputLayers.push(layer);
});
} else if (coll instanceof ol.Collection) {
inputLayers = Ext.clone(coll.getArray());
} else {
inputLayers = Ext.clone(coll);
}
inputLayers.forEach(function (layer) {
if (layer instanceof ol.layer.Group) {
Ext.each(me.getLayerArray(layer.getLayers()), function (subLayer) {
outputLayers.push(subLayer);
});
} else {
outputLayers.push(layer);
}
});
return outputLayers;
},
/**
* Will return an array of serialized layers for mapfish print servlet
* v3.0.
*
* @param {GeoExt.component.Map} mapComponent The GeoExt map component
* to get the the layers from.
* @param {Function} [filterFn] A function to filter the layers to be
* serialized.
* @param {ol.layer.Base} filterFn.item The layer to check for
* inclusion.
* @param {number} filterFn.index The index of the layer in the
* flattened list.
* @param {Array} filterFn.array The complete flattened array of layers.
* @param {boolean} filterFn.return Return a truthy value to keep the
* layer and serialize it.
* @param {Object} [filterScope] The scope in which the filtering
* function will be executed.
* @return {Array<Object>} An array of serialized layers.
* @static
*/
getSerializedLayers: function (mapComponent, filterFn, filterScope) {
const layers = mapComponent.getLayers();
const viewRes = mapComponent.getView().getResolution();
const serializedLayers = [];
let inputLayers = this.getLayerArray(layers);
if (Ext.isDefined(filterFn)) {
inputLayers = Ext.Array.filter(inputLayers, filterFn, filterScope);
}
Ext.each(
inputLayers,
function (layer) {
const source = layer.getSource();
let serialized = {};
const serializer = this.findSerializerBySource(source);
if (serializer) {
serialized = serializer.serialize(
layer,
source,
viewRes,
mapComponent.map,
);
serializedLayers.push(serialized);
}
},
this,
);
return serializedLayers;
},
/**
* Renders the extent of the printout. Will ensure that the extent is
* always visible and that the ratio matches the ratio that clientInfo
* contains.
*
* @param {GeoExt.component.Map} mapComponent The map component to
* render the print extent to.
* @param {ol.layer.Vector} extentLayer The vector layer to render the
* print extent to.
* @param {Object} clientInfo Information about the desired print
* dimensions.
* @param {number} clientInfo.width The target width.
* @param {number} clientInfo.height The target height.
* @return {ol.Feature} The feature representing the print extent.
*/
renderPrintExtent: function (mapComponent, extentLayer, clientInfo) {
const mapComponentWidth = mapComponent.getWidth();
const mapComponentHeight = mapComponent.getHeight();
const currentMapRatio = mapComponentWidth / mapComponentHeight;
const scaleFactor = 0.6;
const desiredPrintRatio = clientInfo.width / clientInfo.height;
let targetWidth;
let targetHeight;
if (desiredPrintRatio >= currentMapRatio) {
targetWidth = mapComponentWidth * scaleFactor;
targetHeight = targetWidth / desiredPrintRatio;
} else {
targetHeight = mapComponentHeight * scaleFactor;
targetWidth = targetHeight * desiredPrintRatio;
}
const geomExtent = mapComponent
.getView()
.calculateExtent([targetWidth, targetHeight]);
const feat = new ol.Feature(ol.geom.Polygon.fromExtent(geomExtent));
extentLayer.getSource().addFeature(feat);
return feat;
},
},
/**
* The capabiltyRec is an instance of 'GeoExt.data.model.print.Capability'
* and contains the PrintCapabilities of the Printprovider.
*
* @property {GeoExt.data.model.print.Capability} capabilityRec PrintCapabilities record
* @readonly
*/
capabilityRec: null,
constructor: function (cfg) {
this.mixins.observable.constructor.call(this, cfg);
if (!cfg.capabilities && !cfg.url) {
Ext.Error.raise('Print capabilities or Url required');
}
this.initConfig(cfg);
this.fillCapabilityRec();
},
/**
* Creates the store from object or url.
*
* @private
*/
fillCapabilityRec: function () {
// enhance checks
let store;
const capabilities = this.getCapabilities();
const url = this.getUrl();
const fillRecordAndFireEvent = function () {
this.capabilityRec = store.getAt(0);
if (!this.capabilityRec) {
this.fireEvent('error', this);
} else {
this.fireEvent('ready', this);
}
};
if (capabilities) {
// if capability object is passed
store = Ext.create('Ext.data.JsonStore', {
model: 'GeoExt.data.model.print.Capability',
listeners: {
datachanged: fillRecordAndFireEvent,
scope: this,
},
});
store.loadRawData(capabilities);
} else if (url) {
// if servlet url is passed
const proxy = {
url: url,
};
if (this.getUseJsonp()) {
proxy.type = 'jsonp';
proxy.callbackKey = 'jsonp';
} else {
proxy.type = 'ajax';
proxy.reader = {
type: 'json',
};
}
store = Ext.create('Ext.data.Store', {
autoLoad: true,
model: 'GeoExt.data.model.print.Capability',
proxy: proxy,
listeners: {
load: fillRecordAndFireEvent,
scope: this,
},
});
}
},
});