はじめに
OpenLayersのデモに地図画像を出力する処理があったので、それを参考に印刷処理を実装してみた。Webの印刷処理は非常に難しいので、DevExpress社のASPxDocumentViewerクラスを利用した。
処理概要は以下の通り
リボンメニューに印刷ボタンを作成し、地図が画像を生成し、Base64化してサーバーに送る。送られたBase64化データを画像に変換し、地図レポートに設定し、印刷画面を表示させる。
report.js
OpenLayersのデモである、Map Exportを参考に地図画像をBase64化する。Base64化で文字列になるのでhiddenフィールドに設定し、サーバーに送信する。
※地図画像をBase64化する場合は、toDataURL()メソッドを使用しているが地図画像に他ドメインから取得した画像を使用している場合は、エラーが発生する。今回の場合、背景地図に、国土地理院のタイル画像を使用しているが、ドメインが異なるのでデフォルトではエラーが発生する。「汚染されたキャンバス」の問題
それを回避するため、ol.source.XYZを生成する際に、crossOriginオプションに’anonymous’を設定しておく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
function InitMap() { var map = new ol.Map({ target: 'MapPanel', view: new ol.View({ center: [14897735, 4070125], zoom: 15, minZoom: 10, maxZoom: 18 }) }); var layer = new ol.layer.Tile({ name: 'bl_std', type: 'base', source: new ol.source.XYZ({ url: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', crossOrigin: 'anonymous', projection: "EPSG:3857" }) }); map.addLayer(layer); } function OnViewRibbonButtonClick(s, e) { var text = e.item.text; if (text === '印刷') { const mapCanvas = document.createElement('canvas'); const size = expmap.getSize(); mapCanvas.width = size[0]; mapCanvas.height = size[1]; const mapContext = mapCanvas.getContext('2d'); Array.prototype.forEach.call( expmap.getViewport().querySelectorAll('.ol-layer canvas, canvas.ol-layer'), function (canvas) { if (canvas.width > 0) { const opacity = 1; mapContext.globalAlpha = Number(opacity); const backgroundColor = canvas.parentNode.style.backgroundColor; if (backgroundColor) { mapContext.fillStyle = backgroundColor; mapContext.fillRect(0, 0, canvas.width, canvas.height); } let matrix; const transform = canvas.style.transform; if (transform) { matrix = transform .match(/^matrix\(([^\(]*)\)$/)[1] .split(',') .map(Number); } else { matrix = [parseFloat(canvas.style.width) / canvas.width,0,0, parseFloat(canvas.style.height) / canvas.height,0,0,]; } CanvasRenderingContext2D.prototype.setTransform.apply(mapContext,matrix ); mapContext.drawImage(canvas, 0, 0); } } ); var imageDate = mapCanvas.toDataURL(); ImageData.Set('ImageData', imageDate); CallbackPrint.PerformCallback("CommandName=PrintPopup"); } } |
PrintDialog.aspx.cs
受信したBase64化データは付加情報が入っており、そのままではbyte[]に変換できないので、付加情報を削除してbyte[]に変換する。変換したbyte[]を画像化し、reportに設定する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
protected void OnDataBinding(object sender, EventArgs e) { var reportData = this.Session["MapReportData"] as MapReportData; var mapReport = new MapReport(); var dataSource = new List(); dataSource.Add(reportData); mapReport.DataSource = dataSource; this.DocumentViewer.Report = mapReport; } public void LoadData() { this.DocumentViewer.DataBind(); } protected void OnCallbackPrint(object sender, CallbackEventArgsBase e) { var parameters = AspxUtil.GetCallbackParameters(e); var imageString = this.ImageData["ImageData"] + ""; var image64String = Regex .Match(imageString, @"data:image/(?.+?),(?.+)") .Groups["data"] .Value.Trim('\0'); var imageData = Convert.FromBase64String(image64String); var mapImage = ImageUtil.ConvertBytesToImage(imageData); var reportData = new MapReportData(); reportData.Title = "地図"; reportData.MapImage = mapImage; this.Session["ReportData"] = reportData; this.LoadData(); } |
地図表示画面
印刷画面
印刷画面に、地図画像が表示されている。残念なのがブラウザの地図画面領域が
小さい場合、印刷画面が切れてしまうことである。これは今後の課題