Skip to content

Metadata editor / fix generate thumbnail from metadata layer when the map background layer is a WMTS layer#9267

Open
josegar74 wants to merge 1 commit into
geonetwork:mainfrom
GeoCat:generate-thumbnail-wmts-backgroundlayer
Open

Metadata editor / fix generate thumbnail from metadata layer when the map background layer is a WMTS layer#9267
josegar74 wants to merge 1 commit into
geonetwork:mainfrom
GeoCat:generate-thumbnail-wmts-backgroundlayer

Conversation

@josegar74
Copy link
Copy Markdown
Member

@josegar74 josegar74 commented May 4, 2026

When configuring a WTMS map background layer an error occurs generating the thumbnail of a metadata with a map layer.

http://localhost:8080/geonetwork/srv/api/records/f5a3e80e-8d60-4e06-ab6e-eb45dc5ef560/attachments/print-thumbnail?jsonConfig={%22layout%22:%22overviewPrintTemplate%22,%22srs%22:%22EPSG:28992%22,%22units%22:%22m%22,%22rotation%22:0,%22lang%22:%22dut%22,%22dpi%22:%22127%22,%22outputFormat%22:%22png%22,%22layers%22:[{%22opacity%22:1,%22type%22:%22WMTS%22,%22baseURL%22:%22http://wmts.geo.admin.ch%22,%22maxExtent%22:[-285401,22598,595401,903401],%22tileSize%22:[null,null],%22resolutions%22:[3440.64,1720.32,860.16,430.08,215.04,107.52,53.76,26.88,13.44,6.72,3.36,1.68,0.84,0.42,0.21],%22zoomOffset%22:0,%22version%22:%221.0.0%22,%22requestEncoding%22:%22REST%22,%22formatSuffix%22:%22jpeg%22,%22style%22:%22default%22,%22dimensions%22:[%22TIME%22],%22params%22:{},%22matrixSet%22:%2221781%22},{%22opacity%22:1,%22type%22:%22WMS%22,%22baseURL%22:%22https://services.geodataoverijssel.nl/geoserver/B22_wegen/wms%22,%22layers%22:[%22B22_Rotondes_in_provinciale_wegen%22],%22styles%22:[%22%22],%22format%22:%22image/png%22,%22customParams%22:{%22EXCEPTIONS%22:%22XML%22,%22TRANSPARENT%22:%22true%22,%22CRS%22:%22EPSG:28992%22},%22singleTile%22:true}],%22pages%22:[{%22title%22:%22%22,%22center%22:[233300.26503093337,493898.61395412975],%22scale%22:%22781250.0%22,%22dataOwner%22:%22%C2%A9+%22,%22rotation%22:0,%22langdut%22:true}],%22hasNoTitle%22:true}&rotationAngle=0

{
    "message": "attribute [spec.layers[0].layer] missing",
    "code": "runtime_exception",
    "description": null
}

The code had hardcoded values for the layer URL and projection.

Apart of this, there is a similar code with some unclear differences:

this.encoders = {
layers: {
Layer: function (layer) {
var enc = {
layer: layer.bodId,
opacity: layer.getOpacity()
};
return enc;
},
Group: function (layer, proj) {
var encs = [];
var subLayers = layer.getLayers();
subLayers.forEach(function (subLayer, idx, arr) {
if (subLayer.visible) {
var enc = self.encoders.layers["Layer"].call(this, layer);
var layerEnc = encodeLayer(subLayer, proj);
if (layerEnc && layerEnc.layer) {
$.extend(enc, layerEnc);
encs.push(enc.layer);
}
}
});
return encs;
},
Vector: function (layer, features) {
var enc = self.encoders.layers["Layer"].call(this, layer);
var format = new ol.format.GeoJSON();
var encStyles = {};
var encFeatures = [];
var stylesDict = {};
var styleId = 0;
angular.forEach(features, function (feature) {
var encStyle = {
id: styleId
};
var styles,
featureStyle = feature.get("_style");
if (angular.isFunction(featureStyle)) {
styles = featureStyle(feature);
} else if (angular.isArray(featureStyle)) {
styles = featureStyle;
} else if (featureStyle) {
styles = [featureStyle];
} else {
styles = defaultStyles;
}
var geometry = feature.getGeometry();
// Transform an ol.geom.Circle to a ol.geom.Polygon
if (geometry.getType() === "Circle") {
var polygon = circleToPolygon(geometry);
feature = new ol.Feature(polygon);
}
var encJSON = format.writeFeatureObject(feature);
if (!encJSON.properties) {
encJSON.properties = {};
} else if (encJSON.properties.Style) {
delete encJSON.properties.Style;
}
encJSON.properties._gx_style = styleId;
encFeatures.push(encJSON);
if (styles && styles.length > 0) {
$.extend(encStyle, transformToPrintLiteral(feature, styles[0]));
}
encStyles[styleId] = encStyle;
styleId++;
});
angular.extend(enc, {
type: "Vector",
styles: encStyles,
styleProperty: "_gx_style",
geoJson: {
type: "FeatureCollection",
features: encFeatures
},
name: layer.bodId,
opacity: layer.opacity != null ? layer.opacity : 1.0
});
return enc;
},
WMS: function (layer, config, proj) {
var enc = self.encoders.layers["Layer"].call(this, layer);
var params = layer.getSource().getParams();
var layers = params.LAYERS.split(",") || [];
var styles =
params.STYLES !== undefined
? params.STYLES.split(",")
: new Array(layers.length).join(",").split(",");
var url =
layer.getSource() instanceof ol.source.ImageWMS
? layer.getSource().getUrl()
: layer.getSource().getUrls()[0];
url = gnGlobalSettings.getNonProxifiedUrl(url);
angular.extend(enc, {
type: "WMS",
baseURL: config.wmsUrl || url,
layers: layers,
styles: styles,
legend: layer.get("legend"),
format: "image/" + (config.format || "png"),
customParams: {
EXCEPTIONS: "XML",
TRANSPARENT: "true",
CRS: proj.getCode(),
TIME: params.TIME
},
singleTile: config.singleTile || false
});
return enc;
},
OSM: function (layer, config) {
var enc = self.encoders.layers["Layer"].call(this, layer);
angular.extend(enc, {
type: "OSM",
baseURL: "http://a.tile.openstreetmap.org/",
extension: "png",
// Hack to return an extent for the base
// layer in case of undefined
maxExtent: layer.getExtent() || [
-20037508.34, -20037508.34, 20037508.34, 20037508.34
],
resolutions: layer.getSource().tileGrid.getResolutions(),
tileSize: [
layer.getSource().tileGrid.getTileSize(),
layer.getSource().tileGrid.getTileSize()
]
});
return enc;
},
XYZ: function (layer, config) {
var enc = self.encoders.layers["Layer"].call(this, layer);
var url = layer.getSource().getUrls()[0];
// parse url template to determine the `url` and `path_format` parameters for mapfish-print
var pathFormat = "${z}/${x}/${y}.${extension}";
var templateStart = url.indexOf("/{z}");
if (templateStart !== -1) {
var urlTemplate = url.substring(templateStart, url.length);
if (urlTemplate.indexOf("/{z}/{y}/{x}") === 0) {
pathFormat = "${z}/${y}/${x}.${extension}";
}
url = url.substring(0, templateStart);
}
url = gnGlobalSettings.getNonProxifiedUrl(url);
angular.extend(enc, {
type: "XYZ",
extension: "png",
baseURL: url,
// Hack to return an extent for the base
// layer in case of undefined
maxExtent: layer.getExtent() || [
-20037508.34, -20037508.34, 20037508.34, 20037508.34
],
resolutions: layer.getSource().getTileGrid().getResolutions(),
tileSize: [
layer.getSource().getTileGrid().getTileSize(),
layer.getSource().getTileGrid().getTileSize()
],
path_format: pathFormat
});
return enc;
},
Bing: function (layer, config) {
var enc = self.encoders.layers["Layer"].call(this, layer);
angular.extend(enc, {
type: "OSM",
baseURL: "http://a.tile.openstreetmap.org/",
extension: "png",
// Hack to return an extent for the base
// layer in case of undefined
maxExtent: layer.getExtent() || [
-20037508.34, -20037508.34, 20037508.34, 20037508.34
],
resolutions: layer.getSource().tileGrid.getResolutions(),
tileSize: [
layer.getSource().tileGrid.getTileSize(),
layer.getSource().tileGrid.getTileSize()
]
});
return enc;
},
WMTS: function (layer, config, proj) {
// sextant specific
var enc = self.encoders.layers["Layer"].call(this, layer);
var source = layer.getSource();
var tileGrid = source.getTileGrid();
var matrixSet = source.getMatrixSet();
var matrixIds = new Array(tileGrid.getResolutions().length);
var layerUrl = layer.get("url");
for (var z = 0; z < tileGrid.getResolutions().length; ++z) {
var mSize =
ol.extent.getWidth(proj.getExtent()) /
tileGrid.getTileSize(z) /
tileGrid.getResolutions()[z];
matrixIds[z] = {
identifier: tileGrid.getMatrixIds()[z],
resolution: tileGrid.getResolutions()[z],
tileSize: [tileGrid.getTileSize(z), tileGrid.getTileSize(z)],
topLeftCorner: tileGrid.getOrigin(z),
matrixSize: [mSize, mSize]
};
}
// workaround for Mapfish v2.1.2 with REST and matrixIds
if (matrixIds.length > 0) {
if (/{style}/gi.test(decodeURI(layerUrl))) {
layerUrl = encodeURI(
decodeURI(layerUrl).replace(/{style}/gi, source.getStyle())
);
}
if (/layer/gi.test(decodeURI(layerUrl))) {
layerUrl = encodeURI(
decodeURI(layerUrl).replace(/{layer}/gi, source.getLayer())
);
}
}
layerUrl = gnGlobalSettings.getNonProxifiedUrl(layerUrl);
angular.extend(enc, {
type: "WMTS",
baseURL: layerUrl,
layer: source.getLayer(),
version: source.getVersion(),
requestEncoding: source.getRequestEncoding() || "KVP",
// Dimensions is not a mandatory parameter
// but it is required by Mapfish v2.1.2 if requestEncoding is REST
dimensions: [],
format: source.getFormat(),
style: source.getStyle(),
matrixSet: matrixSet,
matrixIds: matrixIds
});
return enc;
}
},
legends: {
base: function (layer, config) {
if (!layer.get("legend")) {
return;
}
return {
name: layer.get("title") || layer.get("label"),
classes: [
{
name: "",
icon: new URL(layer.get("legend")).toString()
}
]
};
}
}
};

@fxprunayre do you know the reason for this kind of duplication? It seems like the GaPrintDirectiveController could use gnPrint, but it's not the case. And with the code differences, there are quite chances to break some stuff

Checklist

  • I have read the contribution guidelines
  • Pull request provided for main branch, backports managed with label
  • Good housekeeping of code, cleaning up comments, tests, and documentation
  • Clean commit history broken into understandable chucks, avoiding big commits with hundreds of files, cautious of reformatting and whitespace changes
  • Clean commit messages, longer verbose messages are encouraged
  • API Changes are identified in commit messages
  • Testing provided for features or enhancements using automatic tests
  • User documentation provided for new features or enhancements in manual
  • Build documentation provided for development instructions in README.md files
  • Library management using pom.xml dependency management. Update build documentation with intended library use and library tutorials or documentation

@fxprunayre
Copy link
Copy Markdown
Member

do you know the reason for this kind of duplication?

I've to admit I don't remember all the history for this. The component was initially coming from https://github.com/geoadmin/mf-geoadmin3 but then some changes were added. Indeed some method are the same eg toHexa, some are almost identical transformToPrintLiteral.

Good that you add support for WMTS.

@josegar74 josegar74 modified the milestones: 4.4.11, 4.4.12 Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants