Snippets

Piotr Szrajber Smart M.App - Create PDF reports with ability to upload it to M.App Chest as "My Content"

Created by Piotr Szrajber
.spinner {
   position: absolute;
   left: 50%;
   top: 50%;
   height:60px;
   width:60px;
   margin:0px auto;
   -webkit-animation: rotation .6s infinite linear;
   -moz-animation: rotation .6s infinite linear;
   -o-animation: rotation .6s infinite linear;
   animation: rotation .6s infinite linear;
   border-left:6px solid rgba(0,174,239,.15);
   border-right:6px solid rgba(0,174,239,.15);
   border-bottom:6px solid rgba(0,174,239,.15);
   border-top:6px solid rgba(0,174,239,.8);
   border-radius:100%;
}

@-webkit-keyframes rotation {
   from {-webkit-transform: rotate(0deg);}
   to {-webkit-transform: rotate(359deg);}
}
@-moz-keyframes rotation {
   from {-moz-transform: rotate(0deg);}
   to {-moz-transform: rotate(359deg);}
}
@-o-keyframes rotation {
   from {-o-transform: rotate(0deg);}
   to {-o-transform: rotate(359deg);}
}
@keyframes rotation {
   from {transform: rotate(0deg);}
   to {transform: rotate(359deg);}
}

.box-my-content .widget-chart, .box-pdf-report .widget-chart {
    color: #000;
}

#pdfReport1 {
    width: 100%;
    border: none;
    height: calc(100% - 30px);
}

.pdf-tools {
    color: #000;
}
//# sourceURL=custom.js
/*
* PDF Reports and My Content window (my PDF reports on M.App Chest)
*
* Assumptions:
* - the App contains 2 "custom chart containers":
*   - one with CSS class "box-pdf-report"
*   - second with CSS class "box-my-content"
* - There is a folder called "REPORTS" on your M.App Chest
* - You change the API Key below to your API Key from M.App Studio (IMPORTANT!!!)
*
* Creating PDF reports works thanks to the PDFMake library http://pdfmake.org
 * The PDFMake library is licensed under MIT.
 *
 * In order to use this snippet you need to add references (EXT in the Studio Customization Step) to the following external scripts
 *   - "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.33/pdfmake.js",
 *   - "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.33/vfs_fonts.js"
 *
 * This script assumes that you have 2 Custom Widget Containers created in the UI:
 *   - one with CSS class set to "box-pdf-report".
 *   - second with CSS class set to "box-my-content".
 *
 * 2017-11-10 Piotr Szrajber <piotr.szrajber@hexagongeospatial.com>
 */

//TODO: change the API key!!!
var API_KEY = "!!!CHANGE ME!!!",
    PDF_WINDOW_CSS_CLASS = ".box-pdf-report",
    MY_CONTENT_WINDOW_CSS_CLASS = ".box-my-content";

// global variables

var $myContent;
var $myContentInterior;
var $reportWindow;

//Here you can change folder name.
var folderName = "REPORTS";
var folderPath = "ROOT/" + folderName;

function fetchFilesFromMappChest(folderName, callback, errback) {
    gsp.m_app.utils.connection({
      path: "api/v1/search.json",
      method: "POST",
      entity: {
    	maxresults: 999999,
        orderby: ["name asc"],
        owner: "me",
        profile: "eac-brief",
        template: {
          "class": [
            "com.erdas.rsp.babel.model.ResourceAggregate",
            "com.erdas.rsp.babel.model.imagery.ImageReference",
            "com.erdas.rsp.babel.model.vector.VectorReference",
            "com.erdas.rsp.babel.model.pointcloud.PointCloudResource",
            "com.erdas.rsp.babel.model.GenericItem"
          ]
        }
      }
    }).then(function(request){
        let dataObjs = request.entity.results;
        callback(dataObjs);
    }).catch(function(error){
        if (typeof errback === "function")
            errback(error);
    });
}

function refreshMyItems() {
    $myContentInterior.html(`<div class="spinner"></div>`);
    fetchFilesFromMappChest(folderName, renderMyItems);
}

function showMyItems() {
    $myContent.show();
    refreshMyItems();
}

function renderMyItems(dataObjs) {
    var html = "";
    
    for (var i = 0; i <= dataObjs.length; i++) {
        let dataset = dataObjs[i];
        if (!dataset || !dataset.parent || dataset.parent === "undefined") {
            continue;
        } else if (dataset.parent.name === folderName) {
            //Injecting list items
            var href = `https://mapp.hexagongeospatial.com/api/v1/items/${dataset.id}/attachments/default?apiKey=${API_KEY}`;
            html += `<li><b>Name:</b> <a href="${href}" target="_blank">${dataset.name}</a></li>`;
        }
    }
    $myContentInterior.html(`<ul>${html}</ul>`);
}


function uploadFile(file, callback, errback) {
    $GP.m_app.platform.catalog.upload(file, folderPath, function (response) {
        if (!((typeof response !== "undefined" && response !== null) || !reponse.success)) {
            if (typeof errback === "function")
            errback();
            return;
        }
        itemId = response.itemId;
        if (typeof callback === "function")
            callback();
    }, errback);
}

function init() {
    $myContent = $(MY_CONTENT_WINDOW_CSS_CLASS);
    $myContentInterior = $myContent.find(".widget-chart");
    
    $myContent.hide();
    $myContent.find("a.hide-content").off("click");
    $myContent.find("a.hide-content").click(function() {
        $myContent.hide();
    });

    $reportWindow = $(PDF_WINDOW_CSS_CLASS);

    $reportWindow.hide();
    $reportWindow.find(".widget-chart").html(`
    <iframe id="pdfReport1" src=""></iframe>
    <div class="pdf-tools">
        <button class="download">Download</button>
        <button class="save">Save to My Content</button>
    </div>
    `);
    $reportWindowCloseButton = $reportWindow.find("a.hide-content");
    $reportWindowCloseButton.off("click");
    $reportWindowCloseButton.click(function() {
        $reportWindow.hide();
    });

    // Generate PDF report
    gsp.ui.toolbar.add({
        id: "new-toolbar-item",
        title: "PDF Report",
        style: "background-position: 0px 0px;" /* CHANGE_ICON */
    }, function (ret){
        ret.div.onclick = generateReport;
    });

    // List My Content
    gsp.ui.toolbar.add({
        id: "new-toolbar-item",
        title: "My Content",
        style: "background-position: 0px 0px;" /* CHANGE_ICON */
    }, function (ret){
        ret.div.onclick = showMyItems
    });
}

init();

function generateReport() {
    gsp.bi.stage.findStage(null, function(stage) {
        // open the PDF in a new window
        var timestamp = new Date();
        var filename = `PDF Report ${timestamp.getHours()} ${timestamp.getMinutes()} ${timestamp.getSeconds()}.PDF`;
        var dd = getPdfContent(stage);
        var doc = pdfMake.createPdf(dd);
        var f = document.getElementById('pdfReport1');

        $reportWindow.find("button.download").click(function() {
            doc.download(filename);
        });
        var callback = function(url) {
            f.setAttribute('src', url);
        }
        doc.getDataUrl(callback, doc);
        $reportWindow.show();
    }, function() {
        console.warn("Could not find stage");
    });
}

// Prepare PDF content using PDFMake templates
// It's really easy to prepare complex PDFs with the PDFMake Playground http://pdfmake.org/playground.html
function getPdfContent(stage) {
    //var burntAreasByLandCover = prepareAggregation(stage, "Desc_Easy", "Land Cover");
    //var burntAreasByMunicipality = prepareAggregation(stage, "COMUNE", "COMUNE");
    //var burntAreasByFireId = prepareAggregation(stage, "Fire_ID", "Fire ID");

    return {
        content: [{
                text: 'Tables',
                style: 'header'
            },
            'Official documentation is in progress, this document is just a glimpse of what is possible with pdfmake and its layout engine.',
            {
                text: 'A simple table (no headers, no width specified, no spans, no styling)',
                style: 'subheader'
            },
            'The following table has nothing more than a body array',
            {
                style: 'tableExample',
                table: {
                    body: [
                        ['Column 1', 'Column 2', 'Column 3'],
                        ['One value goes here', 'Another one here', 'OK?']
                    ]
                }
            }
        ],
        styles: {
            header: {
                fontSize: 18,
                bold: true,
                margin: [0, 0, 0, 10]
            },
            subheader: {
                fontSize: 16,
                bold: true,
                margin: [0, 10, 0, 5]
            },
            tableExample: {
                margin: [0, 5, 0, 15]
            },
            tableHeader: {
                bold: true,
                fontSize: 13,
                color: 'black'
            }
        },
        defaultStyle: {
            // alignment: 'justify'
        }

    }
}

// sample aggregations. this is just an example as it really depends on the data model
function prepareAggregation(stage, aggregateBy, title) {
    //var allRowsInCurrentSelection = stage.facts().dimension(function(d){
    //    return d.Object_ID;
    //}).top(Infinity);

    var firesByDesc = stage.facts().dimension(function(d) {
        return [d[aggregateBy], d.Severity];
    }); // dimension that splits data according to description and severity

    var results = firesByDesc.group(function(a) {
        return a;
    }).reduce(function(p, v) {
        return p + v.Area_ha;
    }, function(p, v) {
        return p - v.Area_ha;
    }, function() {
        return 0;
    }).top(Infinity);

    var resultsAsMatrix = results.reduce(function(acc, v) {
        acc[v.key[0]] = acc[v.key[0]] || {};
        acc[v.key[0]][v.key[1]] = v.value;
        return acc;
    }, {});

    var tableBody = [
        [{
            text: title,
            style: 'tableHeader'
        }, {
            text: 'Low Severity',
            style: 'tableHeader'
        }, {
            text: 'Moderate Severity',
            style: 'tableHeader'
        }, {
            text: 'High Severity',
            style: 'tableHeader'
        }],
    ];

    for (var desc in resultsAsMatrix) {
        var s5 = resultsAsMatrix[desc][5] || 0.0;
        var s6 = resultsAsMatrix[desc][6] || 0.0;
        var s7 = resultsAsMatrix[desc][7] || 0.0;

        var tableRow = [{
            text: desc,
            style: 'tableHeader'
        }, s5.toFixed(2), s6.toFixed(2), s7.toFixed(2)];
        tableBody.push(tableRow);
    }

    return tableBody;
}

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.