I'm running an Angular application that displays a map using Leaflet, and has a custom print where the user can set some options before sending a request to the ArcGIS Online print service (https://utility.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task/execute). The issue is that the resultant exported map always appears stretched horizontally, so that a polygon that was square will be clearly rectangular. The entire map - basemap and all polygons - are all stretched the same amount, not just the polygons.
Here is the the code that builds the request:
let requestParams: any = {
f: 'json',
Web_Map_as_JSON: {
"mapOptions":
{
"showAttribution": true,
"extent": {
"xmin": map.getBounds().getWest(),
"ymin": map.getBounds().getSouth(),
"xmax": map.getBounds().getEast(),
"ymax": map.getBounds().getNorth(),
"spatialReference": {"wkid": 4326}
},
"spatialReference": {"wkid": 4326},
},
"operationalLayers": this.getOperationalLayers(map),
"exportOptions": {"dpi": printOption.printQualityDPI},
"layoutOptions":
{
"titleText": printOption.title,
"authorText": printOption.author,
"copyrightText": printOption.copyright,
"customTextElements": [{"Date": new DateTimePipe(this.userPreferencesService).transform(new Date())}],
"legendOptions": {"operationalLayers": []}
}
},
Format: printOption.format,
Layout_Template: printOption.getParsedLayout(),
printFlag: true
};
switch (printOption.scalebarUnit) {
case 'Feet':
case 'Meters':
requestParams.Web_Map_as_JSON.layoutOptions.scaleBarOptions = {
"metricUnit": "esriMeters", "metricLabel": "m", "nonMetricUnit": "esriFeet", "nonMetricLabel": "ft"
};
break;
case 'Kilometers':
case 'Miles':
default:
requestParams.Web_Map_as_JSON.layoutOptions.scaleBarOptions = {
"metricUnit": "esriKilometers", "metricLabel": "km", "nonMetricUnit": "esriMiles", "nonMetricLabel": "mi"
};
break;
}
switch (printOption.preserveOpt) {
case 'extent':
// don't do anything. We always set the extent as all options require it (scale uses the center)
break;
case 'forceScale':
requestParams.Web_Map_as_JSON.mapOptions.scale = printOption.forceScaleValue;
break;
case 'scale':
default:
requestParams.Web_Map_as_JSON.mapOptions.scale = MapUtil.SCALE_ZOOM_LEVEL_MAP[map.getZoom() + 3];
break;
}
if (requestParams.Layout_Template == 'MAP_ONLY') {
if (printOption.mapOnlyWidth && printOption.mapOnlyHeight) {
requestParams.Web_Map_as_JSON.exportOptions.outputSize = [printOption.mapOnlyWidth, printOption.mapOnlyHeight];
} else {
requestParams.Web_Map_as_JSON.exportOptions.outputSize = [map.getContainer().clientWidth, map.getContainer().clientHeight];
}
}
The code that builds the operational layers is as follows:
getOperationalLayers(map) {
let pointLayerSpec = {
"layerDefinition": {
"name": "pointLayer",
"geometryType": "esriGeometryPoint",
"drawingInfo": {
"renderer": {
"type": "simple",
"symbol": {
"type": "esriPMS",
// The following is the map pin icon, converted first to a PNG then to base64. Map Print hates SVG.
"imageData": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABwlBMVEUAAAAAAAAAAABAQEAzMzMrKyskJCQgICBAQEA5OTkzMzMuLi4rKysnJyc3NzcrKysoKCgxMTEuLi4zMzMvLy8uLi4zMzMwMDAtLS0zMzMyMjIvLy8uLi4yMjIxMTEvLy8uLi4tLS0yMjIxMTEwMDAvLy8uLi4yMjIxMTEwMDAvLy8uLi4uLi4yLi4xLS0xMTEwMDA0MDAvLy8uLi4yMjI2LjJBMTVFMTVILjVIMTlTLThRMDtPLzlOKzVRLjVcLjxQLTRULThXLThbLTtTLTdaLTtYLDpmLT1rLD9vK0F3LEGQKUiRKUeWK0mXKkiYKkqbKUqrKU2uKU+6JlG8JlG8JVC+JVLEJVLFJVPFJlTIJlTKJVXJJVTMJFTOJFXPJVbRJFbSJlbTJVfUJVjVJFfXJFbXI1fZI1ffJFneIljfJFjeI1jgI1rgI1ngJFniJFriJFriI1nkI1vlJFvnI1vpJFzrIlvsIlzuIl3uIlzvI13vI13wI17xI17yI17yI17yIl3yIl7yIl3yI13zIl7zIl30Il7zI170I1/1I171Il72Il/2I1/1Il72Il/2Il72Il73Il/4Il/4Il/4Il/////ycNrEAAAAlHRSTlMAAQIEBQYHCAgJCgsMDQ4SExUWGRscHiAiIyQmJykqKywtLi8wMTIzNDU2Nzg4OTk6Ozw9PT0/Q0NDREVHSEhISUlJSUpKS1BRU1ZjZGZnaGt3d4WIiYmPkJKVlpianKCho6SmqKutrbe5uby8vb/AwcLEx8zQ19re4ODh5OXm5+nq6+zt7vHy8vP09ff4+Pn7/f3+W5jqtQAAAAFiS0dElQhgeoMAAAGASURBVBgZfcGLO1NhAAfgn9lWwyZJK81t1rIZlrt0cSukdKGrUnRxydwiJo5UVkqK/f7gvvM9Ht85Z5v3hYGjqDJSXx8JnHEgHXtx21BsPfFT+/jkxrlspPDUPdrioR/Pa3JhkdM6TYPJZhdMbFUxmkyFs2HkHabF0HkY2K98o6TNz2uUvrY7oBQ+pi450uT3N71IUjdcCKVigbpXATvguDhO3VwZlGqNQqLNCcHZ/Y/CxiUojXsUloKQgmsU/lRDafhNYbkKUmiDwq8wlNAKhd2bbgju20kKn/xQfBPUzdS4AU/tInVvvVBO3af0oTca7Y1RuuuGknV5ndLB5uYBpc8hGBWP0uLlWRjl3dqhyU6XEyblb2gydgFmnv6/NNjrc8EiME6D12Wwyuvc5pHv13OQouQZjzz1IZWrJc5Dqy0nkYb33j6l/UEv0rGF31N6F7EhrfyeLxS0qwXIoPwhhQeVyMQenSNnG04go9Md8fi1IhzDd2egBMfJKi21weQ/SGPVKkttvyQAAAAASUVORK5CYII=",
"contentType": "image/png",
"width": 16,
"height": 16
}
}
}
},
"featureSet": {"features": []}
};
let polygonLayerSpec = {
"layerDefinition": {
"name": "polygonLayer",
"geometryType": "esriGeometryPolygon",
"drawingInfo": {
"renderer": {
"type": "simple",
"symbol": {
"type": "esriSFS",
"color": MapUtil.PRINT_FILL_COLOR,
"outline": {"color": MapUtil.PRINT_LINE_COLOR, "width": 1}
}
}
}
},
"featureSet": {"features": []}
};
let lineLayerSpec = {
"layerDefinition": {
"name": "lineLayer",
"geometryType": "esriGeometryPolyline",
"drawingInfo": {
"renderer": {
"type": "simple",
"symbol": {
"type": "esriSLS",
"color": MapUtil.PRINT_LINE_COLOR,
"width": 1
}
}
}
},
"featureSet": {"features": []}
};
let layers = [pointLayerSpec, polygonLayerSpec, lineLayerSpec];
let addPointFeature = (geoJSON) => {
pointLayerSpec.featureSet.features.push({
"geometry": {
"x": geoJSON.geometry.coordinates[0],
"y": geoJSON.geometry.coordinates[1],
"spatialReference": {
"wkid": 4326
}
}
});
};
let addPolygonFeature = (geoJSON) => {
polygonLayerSpec.featureSet.features.push({
"geometry": {
"rings": geoJSON.geometry.coordinates,
"hasZ": false,
"hasM": false,
"spatialReference": {
"wkid": 4326
}
}
});
};
let addMultiPolygonFeature = (geoJSON) => {
geoJSON.geometry.coordinates.forEach(coordinates => {
polygonLayerSpec.featureSet.features.push({
"geometry": {
"rings": coordinates,
"hasZ": false,
"hasM": false,
"spatialReference": {
"wkid": 4326
}
}
});
});
};
let addLineFeature = (geoJSON) => {
lineLayerSpec.featureSet.features.push({
"geometry": {
"paths": geoJSON.geometry.coordinates,
"hasZ": false,
"hasM": false,
"spatialReference": {
"wkid": 4326
}
}
});
};
let addFeature = (geoJSON) => {
if (geoJSON.geometry.type === "Point") {
addPointFeature(geoJSON);
} else if (geoJSON.geometry.type === "Polygon") {
addPolygonFeature(geoJSON);
} else if (geoJSON.geometry.type === "MultiPolygon") {
addMultiPolygonFeature(geoJSON);
} else if (geoJSON.geometry.type === "LineString" || geoJSON.geometry.type === "MultiLineString") {
addLineFeature(geoJSON);
}
};
map.eachLayer((layer) => {
if (layer.toGeoJSON) {
let geoJSON = layer.toGeoJSON();
if (geoJSON.type === "Feature") {
addFeature(geoJSON);
} else if (geoJSON.type === "FeatureCollection") {
_.forEach(geoJSON.features, (geoJSONFeature) => {
addFeature(geoJSONFeature);
});
}
}
});
return [
{
"id": "defaultBasemap",
"title": "World Topographic Map",
"opacity": 1,
"minScale": 0,
"maxScale": 0,
"url": "https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"
},
{
"id": "map_graphics",
"featureCollection": {
"layers": layers
}
}
];
}
I don't think there's anything wrong with the request, but every single time the map is exported it is stretched. Is there something I need to be doing to the leaflet map before this stage to prevent the stretching? I'm pretty new to these plugins, I don't know where to start.