Search code examples
javascripthtmlpdfopenlayers

Save Openlayers map as PDF (offline version)


I would like to save my openlayers map as PDF.

Unfortunately all the options I tried don't work.

The first option, I guess the easiest one was here:

https://jsfiddle.net/Ljnya5gp/1/

from which I developed something like this:

 function createPdf() {

 var doc = new jsPDF();

 source = $('#map')[0];

 specialElementHandlers = {
    '#map': function (element, renderer) {
        return true
    }
  };

 doc.fromHTML(
  source,
 15,
 15, 
 {
  'width': 170,
  'elementHandlers': specialElementHandlers
 }
   );
     doc.save('Area 5 map - alterations.pdf')

but the script downloads only blank document for me.

I want to have this section downloaded (from index.html)

      <div id="map">
        <div id="popup" class="ol-popup">
            <a href="#" id="popup-closer" class="ol-popup-closer"></a>
            <div id="popup-content"></div>
        </div>
      </div>

Why the output PDF document is blank? Is it caused by Map script, which is embedded into the HTML file?

My <div id="map"> refers to the script attached to the HTML file as the:

enter image description here

The jsfiddle can be found here:

https://jsfiddle.net/rjetdvyo/

Is it something which causes the problem too?

UPDATE:

Based on the answer below I formed something like this:

function createPdf() {

var mapElement = $("#map");

html2canvas(mapElement, {
    useCORS: true,
    onrendered: function (canvas) {
        var img = canvas.toDataURL("image/jpeg,1.0");
        var pdf = new jsPDF();
        pdf.addImage(img, 'JPEG', 15, 40, 180, 180);
        pdf.save('a4.pdf')
    }
});
  }

 var map = (document.getElementById('map'), createPdf);

The printpdf button only refreshes the page.


Solution

  • I think everything is ok, it was showing blank because you didn't write any text into div. Have a look:

    function createPdf() {
    
        var doc = new jsPDF();
    
        source = $('#map')[0];
    
        specialElementHandlers = {
            '#map': function (element, renderer) {
                return true
            }
        };
    
        doc.fromHTML(
            source,
            15,
            15,
            {
                'width': 170,
                'elementHandlers': specialElementHandlers
            }
        );
        doc.save('Area 5 map - alterations.pdf')
    
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"
        integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg=="
        crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js"></script>
    
    <div id="map">
        <div id="popup" class="ol-popup">
            <a href="#" id="popup-closer" class="ol-popup-closer">Hello</a>
            <div id="popup-content"><h1>Hello, this is a H1 tag</h1></div>
        </div>
    </div>
    
    <button onclick="createPdf()">generate PDF</button>

    Update:

    By using jsPDF and html2canvas both, you can achieve your goal.

    html2canvas creates a canvas object from the DOM element. From canvas, you can create an image and finally convert that image into pdf by jsPDF. Have a look at the code below:

    function createPdf() {
    
        var mapElement = $("#map-canvas");
    
        html2canvas(mapElement, {
            useCORS: true,
            onrendered: function (canvas) {
                var img = canvas.toDataURL("image/jpeg,1.0");
                var pdf = new jsPDF();
                pdf.addImage(img, 'JPEG', 15, 40, 180, 180);
                pdf.save('a4.pdf')
            }
        });
    
    }
    
    // if HTML DOM Element that contains the map is found...
    if (document.getElementById('map-canvas')) {
    
        // Coordinates to center the map
        var myLatlng = new google.maps.LatLng(52.525595, 13.393085);
    
        // Other options for the map, pretty much selfexplanatory
        var mapOptions = {
            zoom: 14,
            center: myLatlng,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };
    
        // Attach a map to the DOM Element, with the defined settings
        var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
    
    }
    #map-canvas {
        width: 500px;
        height: 400px;
    }
    <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false" type="text/javascript"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"
        integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg=="
        crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"
        integrity="sha512-s/XK4vYVXTGeUSv4bRPOuxSDmDlTedEpMEcAQk0t/FMd9V6ft8iXdwSBxV0eD60c6w/tjotSlKu9J2AAW1ckTA=="
        crossorigin="anonymous"></script>
    
    <button onclick="createPdf()">generate PDF</button>
    <div id="map-canvas"></div>

    Update 2: I don't know which ways you're following. Just follow these steps to get the job done:

    1. Follow these steps first.
    2. Replace your index.js code by this:
    import 'ol/ol.css';
    import { Map, View } from 'ol';
    import TileLayer from 'ol/layer/Tile';
    import OSM from 'ol/source/OSM';
    
    const map = new Map({
        target: 'map',
        layers: [
            new TileLayer({
                source: new OSM()
            })
        ],
        view: new View({
            center: [0, 0],
            zoom: 0
        })
    });
    
    var exportButton = document.getElementById('export-pdf');
    
    exportButton.addEventListener(
        'click',
        function () {
            exportButton.disabled = true;
            document.body.style.cursor = 'progress';
    
            // var format = [297, 210]; 
            var resolution = 72; // The term 72 dpi(dots per inch) is used to express the resolution of a screen
            var dim = [297, 210]; // a4 size's dimension
            var width = Math.round((dim[0] * resolution) / 25.4);
            var height = Math.round((dim[1] * resolution) / 25.4);
            var size = map.getSize();
            var viewResolution = map.getView().getResolution();
    
            map.once('rendercomplete', function () {
                var mapCanvas = document.createElement('canvas');
                mapCanvas.width = width;
                mapCanvas.height = height;
                var mapContext = mapCanvas.getContext('2d');
                Array.prototype.forEach.call(
                    document.querySelectorAll('.ol-layer canvas'),
                    function (canvas) {
                        if (canvas.width > 0) {
                            var opacity = canvas.parentNode.style.opacity;
                            mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
                            var transform = canvas.style.transform;
                            // Get the transform parameters from the style's transform matrix
                            var matrix = transform
                                .match(/^matrix\(([^\(]*)\)$/)[1]
                                .split(',')
                                .map(Number);
                            // Apply the transform to the export map context
                            CanvasRenderingContext2D.prototype.setTransform.apply(
                                mapContext,
                                matrix
                            );
                            mapContext.drawImage(canvas, 0, 0);
                        }
                    }
                );
                var pdf = new jsPDF('landscape');
                pdf.addImage(
                    mapCanvas.toDataURL('image/jpeg'),
                    'JPEG',
                    0,
                    0,
                    dim[0],
                    dim[1]
                );
                pdf.save('map.pdf');
                // Reset original map size
                map.setSize(size);
                map.getView().setResolution(viewResolution);
                exportButton.disabled = false;
                document.body.style.cursor = 'auto';
            });
    
            // Set print size
            var printSize = [width, height];
            map.setSize(printSize);
            var scaling = Math.min(width / size[0], height / size[1]);
            map.getView().setResolution(viewResolution / scaling);
        },
        false
    );
    
    1. Replace your index.html by this:
    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="utf-8">
        <title>Using Parcel with OpenLayers</title>
        <style>
            #map {
                width: 400px;
                height: 250px;
            }
        </style>
    </head>
    
    <body>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js"></script>
    
        <button id="export-pdf" >Export PDF</button>
        <div id="map"></div>
        <script src="./index.js"></script>
    
    </body>
    
    </html>