Example QML/JS code for unpacking and displaying a MobileMapPackage with a raster layer?

4327
9
06-28-2018 04:29 PM
ChristopherSwingley
New Contributor II

I get that I probable need to use MobileMapPackageUtility.unpack(), but the details of how you'd actually do that to display the package as the background layer within a Map escape me, and I don't see an example that does this. Thanks!

Tags (2)
0 Kudos
9 Replies
LucasDanzinger
Esri Frequent Contributor

something along these lines should do the trick:

function unpackMmpk(path) {
    // once the unpack status is complete, add the mmpk
    MobileMapPackageUtility.unpackStatusChanged.connect(function() {
        if (MobileMapPackageUtility.unpackStatus === Enums.TaskStatusCompleted) 
            addMmpk(path);
        }
    });

    // unpackge the mmpk
    MobileMapPackageUtility.unpack(path);
}

function addMmpk(path) {
    // create the mmpk and set the path
    var mobileMapPackage = ArcGISRuntimeEnvironment.createObject("MobileMapPackage");
    mobileMapPackage.path = path;

    // wait for the package to load
    mobileMapPackage.loadStatusChanged.connect(function() {
        if (mobileMapPackage.loadStatus === Enums.LoadStatusLoaded) {
            // set the mapview's map to the first map in the package
            mapView.map = mobileMapPackage.maps[0];            
        }
    });

    // load the mmpk
    mobileMapPackage.load();
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
ChristopherSwingley
New Contributor II

Lucas Danzinger Thanks! I will give this a try tomorrow and let you know if I have any issues. Appreciate the rapid reply.

0 Kudos
ChristopherSwingley
New Contributor II

Lucas Danzinger A few questions. I'm using this code:

MapView {
    id: mapView
    anchors.fill: parent
    focus: true

    Map {
        id: map
        BasemapTopographic{}
    }

    function unpackMmpk(path, outputDirectory) {
        // once the unpack status is complete, add the mmpk
        MobileMapPackageUtility.unpackStatusChanged.connect(function() {
            if (MobileMapPackageUtility.unpackStatus === Enums.TaskStatusCompleted) {
                console.log("unpacked mmpk");
                addMmpk(path);
            } else if (MobileMapPackageUtility.unpackStatus === Enums.TaskStatusErrored) {
                console.log("unpacking TaskStatusErrored");
            }
        }); 

        console.log("unpacking mmpk");
        // unpackage the mmpk
        MobileMapPackageUtility.unpack(path, outputDirectory);
    }

    function addMmpk(path) {
        console.log("adding mmpk");
        // create the mmpk and set the path
        var mobileMapPackage = ArcGISRuntimeEnvironment.createObject("MobileMapPackage");
        mobileMapPackage.path = path;

        // wait for the package to load
        mobileMapPackage.loadStatusChanged.connect(function() {
            if (mobileMapPackage.loadStatus === Enums.LoadStatusLoaded) {
                // set the mapview's map to the first map in the package
                mapView.map = mobileMapPackage.maps[0];
            }
        });

        // load the mmpk
        mobileMapPackage.load();
    }

    Component.onCompleted: {
        unpackMmpk("/tmp/goldstream_valley.mmpk", "/tmp/unpacked")
    }
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

MobileMapPackageUtility.unpack() requires the mmpk file as the first argument and the directory to unpack as the second, so I updated that. I also added a test to see if there's an error unpacking.

When I run this, I see "unpacking mmpk" in the logs, it never reaches "unpacked mmpk" or "adding mmpk", but it does hit the "unpacking TaskStatusError" section. But I don't know what might be going wrong. The "unpacked" directory exists and I can write to it, and the mmpk file unzips just fine with the Unix "unzip" utility.

Any suggestions as to what I might be doing wrong or how to have it show me the error? Once I get it unpacked, I'm wondering whether I need to pass the unpack directory to the addMmpk() function rather than the mmpk file itself.

Finally, all I'm really trying to do is get a TIF image as the background for my map. Is there a simpler way to accomplish this? It seems like a lot of work to create a mobile map package of a TIF, then have the application unpack it back into a TIF again for display.

Thanks!

0 Kudos
ChristopherSwingley
New Contributor II

I'm getting closer. I think the error in unpacking was because I wasn't using "file:/// ..." links in the unpack() function. Now that I am, the mmpk successfully unpacks into the directory I specify. But nothing loads. I'm passing the unpacked directory path to the addMmpk() function (which sets mobileMapPackage.path to the unpacked directory), but it doesn't load when I do that. When I check loadStatusChanged, it reports Enums.LoadStatusLoaded but the space where the imagery should be is grey. I've checked the TIF that's in the unpacked/commondata/raster_data/ subdirectory and it displays fine in GIS software. It's EPSG:26906. Does that matter? Should I transform the raster to EPSG:3857 first?

I also notice that after the first time I run the application I get a TaskStatusErrored status from unpack(), which I gather is because the mmpk is already unpacked. How do I handle that?

0 Kudos
ChristopherSwingley
New Contributor II

I'm out of ideas. For what it's worth, the mobile map packages I've tried are here: https://seafile.abrinc.com/d/bd9ed1edfc5a47e39d26/

The one without an SRID in the name is the one with the original TIF and original projection in it (EPSG:26906).

0 Kudos
LucasDanzinger
Esri Frequent Contributor

Looks like there was a small error in the code - when the addMmpk function is called (line 11 below), the unpacked location should be passed in instead of the original location. Once I did this, all 3 of your MMPKs displayed for me.

MapView {
    id: mapView
    anchors.fill: parent
    focus: true

    function unpackMmpk(path, outputDirectory) {
        // once the unpack status is complete, add the mmpk
        MobileMapPackageUtility.unpackStatusChanged.connect(function() {
            if (MobileMapPackageUtility.unpackStatus === Enums.TaskStatusCompleted) {
                console.log("unpacked mmpk");
                addMmpk(outputDirectory);
            } else if (MobileMapPackageUtility.unpackStatus === Enums.TaskStatusErrored) {
                console.log("unpacking TaskStatusErrored", MobileMapPackageUtility.error.message);
            }
        });

        console.log("unpacking mmpk");
        // unpackage the mmpk
        MobileMapPackageUtility.unpack(path, outputDirectory);
    }

    function addMmpk(path) {
        console.log("adding mmpk");
        // create the mmpk and set the path
        var mobileMapPackage = ArcGISRuntimeEnvironment.createObject("MobileMapPackage");
        mobileMapPackage.path = path;

        // wait for the package to load
        mobileMapPackage.loadStatusChanged.connect(function() {
            if (mobileMapPackage.loadStatus === Enums.LoadStatusLoaded) {
                // set the mapview's map to the first map in the package
                console.log("loaded")
                mapView.map = mobileMapPackage.maps[0];
            } else if (mobileMapPackage.loadStatus === Enums.LoadStatusFailedToLoad) {
                console.log("failed to load", error.message, error.additionalMessage)
            }
        });

        // load the mmpk
        mobileMapPackage.load();
    }

    Component.onCompleted: {
        var timeStamp = new Date();
        var hms = "-" + timeStamp.getHours() + "-" + timeStamp.getMinutes() + "-" + timeStamp.getSeconds();
        var tempPath = System.temporaryFolder.url + "/goldstream_valley"  + hms;
        console.log(tempPath)
        unpackMmpk("file:///Users/<username>/Downloads/goldstream_valley.mmpk", tempPath)
    }
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
ChristopherSwingley
New Contributor II

Lucas Danzinger‌ Thanks much. I copied your code into a new test application and it works!

The same block doesn't draw anything when pasted into my existing code, so I have some work ahead to figure out what the differences are that would allow BasemapTopographic{} to draw, but won't draw the background raster from the mobile map package as in the example/test code.

0 Kudos
ChristopherSwingley
New Contributor II

It turns out the issue is with licensing. I used a "runtimelite" license, defined in AppInfo.h. As soon as I add the #define kLicense with a runtimelite license the Mobile Map Package doesn't display. This surprises me because the Android SDK I've been using allows me to display tile packages without an elevated license in an Android app.

Is this intentional? If so, are there any other ways to display offline content like a tile package or raster imagery on a map using the Qt SDK?

0 Kudos
LucasDanzinger
Esri Frequent Contributor

Please refer to this document License your app—ArcGIS Runtime SDK for Qt | ArcGIS for Developers 

Tile packages should work with lite, as they fall under the "Viewing maps, scenes, layers, and packages from the ArcGIS platform". However, reading local raster files in the Runtime SDKs falls under a Standard license - "Access to additional data, including shapefiles, GeoPackages, ENC layers (S-57), local raster layers, use of raster functions, and local raster elevation sources."

So in your case, if using tile packages will fit your workflow, I suggest you give this a try. If you need to use rasters, you will need to purchase a standard license. Hope that helps.

0 Kudos