MMPK Limitations / Layer Questions

6986
6
05-03-2016 08:44 AM
GlennWilson1
New Contributor III

What are the limitations for utilizing MMPK's?  Specifically, here are several questions I have:

  • Q1: How many MMPK's can you reference in a document?
  • Q2: Can you legally point to a specific layer in an MMPK and add it to a map?
  • Q3: Is it legal to have a MMPK with no basemap?
  • Q4: If you have no basemap, then how to you set the default color of a map created by the mmpk?

Regarding Q1 & Q2, If I have two MMPK's, and I'd like layer "A" off of a.mmpk and layer "B" off of b.mmpk, is this possible?

If I add the layers via the mmpk, the layers appear:

 self.mapView.map = mobileMapPackage.maps[0]

However, if you try and utilize the individual layers from the mmpk to a default AGSMap, but the layers don't appear. Here's the complete function for clarification:

if let filepath = NSBundle.mainBundle().pathForResource("mobile", ofType:"mmpk") {
            print("found map")
            let mobileMapPackage = AGSMobileMapPackage(path: filepath)
            
            mobileMapPackage.loadWithCompletion { (error) -> Void in
                if let error = error {
                    print(error.localizedDescription)
                }
                else {
                    // In this case the first map in the array is obtained
                    print ("setting map")
                    print("The mobile map package contains \(mobileMapPackage.maps.count) maps")
                    
                    if (mobileMapPackage.maps.count > 0) {
                        var map = mobileMapPackage.maps[0]
                        print ("There are \(map.operationalLayers.count) layers.")
                        self.mapView.backgroundColor = UIColor.whiteColor() // will be hidden by basemap
                        self.mapView.map = AGSMap(basemap:AGSBasemap.lightGrayCanvasBasemap()) // required?
                        for obj in map.operationalLayers{
                            if let layer = obj as? AGSLayer {
                                print("Adding layer \(layer.name).") // the log shows them loading...
                                self.mapView.map!.operationalLayers.addObject(layer)
                            }
                        }
                    }
                }
            }
        }

Q3 & Q4 are related, I believe.  I'm trying to minimize the data footprint distributed to my mobile devices, which all have to work completely offline.  I'm really not wanting to include a basemap (to reduce size), and all I care about is showing the data from a few layers.  Although there are no constraints on the mmpk export, the data is clipping weirdly on the device, and it's showing up on a solid black background:

Simulator Screen Shot May 3, 2016, 9.40.38 AM.png

Attempts to change the background color do not work -- the mmpk is actually inserting the black background on top of a mapview that has a solid green background color set.

This leads me to believe that a basemap is required to set extents properly.

Thoughts?  Suggestions?

0 Kudos
6 Replies
DiveshGoyal
Esri Regular Contributor

Q1 - we don't have any limits on the number of mmpks you can use.

PS - what do you mean by reference MMPKs in a document? Which document do you mean?

Q2 - I think same layer instance cannot exist in two maps. Either remove it from the first map before adding it to the new one, or make a copy of it.

Q3 -  Yes, this should be supported

Q4 - There are 2 things at play here - a UIView backgroundColor (which the mapview inherits because it is a UIView), and a MapView background, which takes precedence over the UIView backgroundcolor whenver the mapview has a map to display. The mapview background is of type AGSMapBackground, and by defauilt, it set to black. So when you open a map with no basemap, the mapview background will be displayed (the UIView backgroundColor will only be displayed if mapview background is set to transparent). To change the mapview background, change properties on the AGSMapBackground instance and set it back on the mapview. Now when you open a map without a basemap, it should display your mapview background

0 Kudos
GlennWilson1
New Contributor III

I'll try to play with Q4 and verify this.  If I'm successful, I'll post the code in case it helps others.

With regard to Q1, the I should have said map, instead of document.  I'm working with regional data, and I was curious if it might be possible for me to allow the user to download two regions of mmpk's and show the layers from both of them on a single, coherent map.

Thanks for the help on all my questions/posts.  I had to step away from my project for a few days.  I should be back on it Monday, and I'll do my best to respond/confirm solutions as I work on it.

MathanManohar
New Contributor III

Hi Glenn Wilson,

I am facing an similar scenario like loading 2 .mmpk as Overlay. 

Ex: Basemap will be in Boundary.mmpk as initial loading when the app is installed. Another .mmpk [details.mmpk] files will be loaded according to users requirement, like Manager can view the sales /Finance Lead can view expense details. These Details.mmpk will be downloaded from online when the login is successfull. 

So I loaded the .mmpk in mapview and try to add the details.mmpk, But I am unable to it, since mapview is get replaced. I can display any one of the .mmpk.

Your post resembles the same scenario. Can you show insight on this issue.

Thanks in advance.

0 Kudos
ReedHunter
New Contributor III

Just to expand on Divesh's response:

   "Q2 - I think same layer instance cannot exist in two maps. Either remove it from the first map before adding it to the new one, or make a copy of it."

On line 16 you declared

   var map = mobileMapPackage.maps[0]  

But then on line 18 that map was not assigned to MapView.map

   self.mapView.map = AGSMap(basemap:AGSBasemap.lightGrayCanvasBasemap())  

Depending on when/whether mobileMapPackage.maps[0] ever loads its layers, you may have a race condition on your hands between your line 18 AGSMap and the AGSMap at mobileMapPackage.maps[0] for which one gets to claim the layers in the operationalLayers property.

You can avoid this by using the AGSMap at mobileMapPackage.maps[0] instead of the created one from line 18, meaning make line 18 into 

   self.mapView.map = mobileMapPackage.maps[0]

You could then add the grey basemap in manually as an AGSArcGISTiledLayer, inserting it either at index 0 of the .operationalLayers array, or in the basemap.basemapLayers array of the AGSMap.  

I ran into similar problems in my app when I first switched from the Runtime SDK 10.2.x .geodatabase files over to the 100.x SDK's MMPKs, and found that a layer coming from an MMPK can only be assigned/used in one AGSMap at a time.  If it's already assigned to an AGSMap's operationalLayers (or manually assigned to basemap.basemapLayers), it couldn't be used in another AGSMap.  But wait, you think to yourself, I'm only assigning the layers to the map I created once - like on line 22 of the code above.  The trick is with MMPKs that they already come with an AGSMap, the one you grab them from at mobileMapPackage.maps[0].  That means your layers are already being claimed.   

And yet, your map successfully loaded those layers anyway.  What gives?  The layers after line 22 existed in the .operationalLayers array of both your mapView.map.operationalLayers and in mobileMapPackage.maps[0].operationalLayers.  The answer I believe is in the async nature of the 100.x SDK.  Just to muddy things up further the 100.x SDK is asynchronous for faster startup times.  Objects like the AGSMap and the AGSLayers are only partially initialized at first (you can check this in debug during startup on the .loaded property of these objects).  Loading GIS data can be slooow, and to keep that from killing your startup time for big datasets the new SDK gives you more power to create objects before you load them with data.  One of the tripwires to tell the SDK it's time to load the data is to assign a map to the MapView.map object, which you do on line 18.  That means your created map is starting to lay claim to its AGSLayers first (probably loading your grey basemap layer first), and then getting first crack at the AGSLayers you assign to it on line 22.  The early bird gets the worm, and I think your created AGSMap gets the operationalLayers even if they're already assigned to mobileMapPackage.maps[0].  The part I can't advise you on is when the mobileMapPackage.maps[0] AGSMap starts to contend for those layers.  I can only say that in my app the MMPK object won the race instead and I couldn't load my layers in the mapView.map until I removed them from the MMPK's operationalLayers array.

One more thought that crosses my mind is whether the spatial reference of your operational layers can play nice with web mercator of the light grey basemap.  That's probably not it, but bears mention as part of our mental checklist as GIS developers.

I realize this is an old post I'm responding to, but I hope this helps in case others run across this problem.

-Reed

0 Kudos
Nicholas-Furness
Esri Regular Contributor

You've pretty much got it, Reed Hunter 

I think of it this way (this is pretty loosey-goosey, but it describes things well enough):

A FeatureLayer and the underlying FeatureTable are pretty tightly coupled once they've been brought to life. By brought to life, in practice I mean showing it in an AGSMapView through an AGSMap's operationalLayers (an exception may be simply loading the AGSMap with AGSLoadable which may trigger that coupling for some layers/tables to determine a spatial reference, but you'd have to be missing a basemap). Once the Map is alive, it pretty much "owns" the layer - you can't add that layer to another map at the same time (you'd have to remove it from the map's operational layers and then you should be able to use it in another AGSMap).

Given that tight coupling, what you should be able to do is take a map's FeatureLayer, and call copy() on it. This should give you an instance of the feature layer that you can use in another map (this will also copy() the underlying FeatureTable so you don't break that tight coupling). It'll point at the same source data, but will be an entirely separate layer unbeholden to the "owning" map of the original layer (since the copy won't be in any operationalLayers collection).

e.g. let mySafeToUseFeatureLayer = featureLayerAlreadyInAMap.copy() as! AGSFeatureLayer

Safest way, to avoid these races as maps are brought to life and claim layers, would be to cherry pick the layers you want from the various maps in various MMPKs, and copy() them to add to your own map that you're compiling.

Just be aware that if you're doing this with multiple visible map views at once, you may end up hitting that single underlying geodatabase data table at the same time for both map views if you're navigating them both at the same time. Should work just fine, but you might notice some performance drops as data is read.

I should stress that this is not something I have delved into myself.

And map projection should work pretty well. The projection engine is fast and flexible, but it is an additional performance hit if every geometry has to be projected.

0 Kudos
Nicholas-Furness
Esri Regular Contributor

An MMPK is a container for Maps. Each AGSMapView can display one Map at a time. You can open a TPK and add it to a map.

If you need to show a different map in a map view, simply replace the AGSMapView.map property with the other AGSMap.

0 Kudos