I've been looking for an answer to this.
Suppose I load a Mobile Map Package.
It has layers in it.
If I do either of these things:
The result is that I have an invalid map that shows nothing.
Is it still all or nothing for Mobile Map Packages?
This way, I can't distribute them as .mmpk - I have to extract the individual .geodatabases and use them that way.
Hi,
You should be able to open a map from an MMPK and add new layers to it just fine. Note that if the layer is not reprojectable, and it's spatial reference does not match that of the map you're adding them too, then you might not be able to see the data.
For adding layers from one map to another map - if a layer already belongs to one map, you cannot add that to another map (you should get an exception to this effect); in this case you can try using the `copy` method to create a copy of the layer, and add that copy to the other map. This would be the same regardless of whether the layers are from a map loaded from an mmpk, or if the layers and maps were created programmatically from data directly.
If you're still having issues, post your code and details about your data, and perhaps we can reproduce any problems you're seeing.
Regards,
Shelly
Where is the 'copy' method?
I don't see that method on the layer class.
Good point Nathan, apologies for not being clear here. We have implemented the copy method on the specific types of Layer class, not up at the Layer superclass level. There was a few types of layer that we didn't support the copy method on, but I believe we're filling in this support in the upcoming release. Note however if you get an 'UnsupportedLayer' or 'UnknownLayer' (for example from a web map) then you won't be able to copy these I think.
I've done everything you have asked and this is still the result. In the code, i can debug and see that one MobileBaseMapLayer is added to the basemap and four feature layers are added to the operational layers.
But none of that helps because I see absolutely nothing on screen. Not even a grid. No basemap, no feature layers. Nothing.
This is with your very own Yellowstone.mmpk example.
Here is the code.
/* Copyright 2016 Esri * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.esri.arcgisruntime.sample.openmobilemappackage; import java.io.File; import java.util.List; import java.util.concurrent.ExecutionException; import android.Manifest; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Environment; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.widget.Toast; import com.esri.arcgisruntime.ArcGISRuntimeEnvironment; import com.esri.arcgisruntime.concurrent.ListenableFuture; import com.esri.arcgisruntime.geometry.Point; import com.esri.arcgisruntime.geometry.SpatialReference; import com.esri.arcgisruntime.geometry.SpatialReferences; import com.esri.arcgisruntime.layers.ArcGISTiledLayer; import com.esri.arcgisruntime.layers.ArcGISVectorTiledLayer; import com.esri.arcgisruntime.layers.FeatureLayer; import com.esri.arcgisruntime.layers.ImageTiledLayer; import com.esri.arcgisruntime.layers.Layer; import com.esri.arcgisruntime.layers.LegendInfo; import com.esri.arcgisruntime.layers.MobileBasemapLayer; import com.esri.arcgisruntime.layers.WebTiledLayer; import com.esri.arcgisruntime.loadable.LoadStatus; import com.esri.arcgisruntime.mapping.ArcGISMap; import com.esri.arcgisruntime.mapping.Basemap; import com.esri.arcgisruntime.mapping.MobileMapPackage; import com.esri.arcgisruntime.mapping.Viewpoint; import com.esri.arcgisruntime.mapping.view.MapView; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private static final String FILE_EXTENSION = ".mmpk"; private static File extStorDir; private static String extSDCardDirName; private static String filename; private static String mmpkFilePath; // define permission to request String[] reqPermission = new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }; private MapView mMapView; private MobileMapPackage mapPackage; private int requestCode = 2718; private ArcGISMap mMap; /** * Create the mobile map package file location and name structure */ private static String createMobileMapPackageFilePath() { return extStorDir.getAbsolutePath() + File.separator + "agis" + File.separator + filename + FILE_EXTENSION; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ArcGISRuntimeEnvironment.setLicense("runtimelite,1000,rud8488948081,none,TRB3LNBHPD0H4P7EJ008"); // get sdcard resource name extStorDir = Environment.getExternalStorageDirectory(); // get the directory extSDCardDirName = this.getResources().getString(R.string.config_data_sdcard_offline_dir); // get mobile map package filename filename = "Yellowstone";//this.getResources().getString(R.string.config_mmpk_name); // create the full path to the mobile map package file mmpkFilePath = createMobileMapPackageFilePath(); // retrieve the MapView from layout mMapView = (MapView) findViewById(R.id.mapView); mMap = new ArcGISMap(Basemap.Type.TOPOGRAPHIC_VECTOR,44.417,-112.08,11); mMapView.setMap(mMap); // For API level 23+ request permission at runtime if (ContextCompat.checkSelfPermission(MainActivity.this, reqPermission[0]) == PackageManager.PERMISSION_GRANTED) { loadMobileMapPackage(mmpkFilePath); } else { // request permission ActivityCompat.requestPermissions(MainActivity.this, reqPermission, requestCode); } } /** * Handle the permissions request response */ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { loadMobileMapPackage(mmpkFilePath); } else { // report to user that permission was denied Toast.makeText(MainActivity.this, getResources().getString(R.string.location_permission_denied), Toast.LENGTH_SHORT).show(); } } /** * Load a mobile map package into a MapView * * @param mmpkFile Full path to mmpk file */ private void loadMobileMapPackage(String mmpkFile) { //[DocRef: Name=Open Mobile Map Package-android, Category=Work with maps, Topic=Create an offline map] // create the mobile map package File mfile = new File(mmpkFile); boolean exists = mfile.exists(); Log.i(TAG,"File " + mmpkFile + " exists=" + exists); mapPackage = new MobileMapPackage(mmpkFile); // load the mobile map package asynchronously mapPackage.loadAsync(); // add done listener which will invoke when mobile map package has loaded mapPackage.addDoneLoadingListener(new Runnable() { @Override public void run() { // check load status and that the mobile map package has maps if (mapPackage.getLoadStatus() == LoadStatus.LOADED && mapPackage.getMaps().size() > 0) { // add the map from the mobile map package to the MapView ArcGISMap amap = mapPackage.getMaps().get(0); //mMap.setBasemap(Basemap.createNavigationVector()); //ArcGISMap defaultMap = new ArcGISMap(Basemap.Type.TOPOGRAPHIC_VECTOR,44.417,-112.08,11); // Basemap basemap = Basemap.createNavigationVector(); //mMap.setBasemap(basemap); //mMap.getBasemap().getBaseLayers().add(new ArcGISTiledLayer("https://www.arcgis.com/home/item.html?id=30e5fe3149c34df1ba922e6f5bbf808f")); ///mMapView.setMap(defaultMap); for(int i=0;i<amap.getBasemap().getBaseLayers().size(); i++) { Layer l = amap.getBasemap().getBaseLayers().get(i); Layer layerCopy = copyLayer(l); if(layerCopy!=null) mMap.getBasemap().getBaseLayers().add(layerCopy); } for(int j=0;j<amap.getOperationalLayers().size(); j++) { Layer o = amap.getOperationalLayers().get(j); Layer layerCopy = copyLayer(o); if(layerCopy!=null) mMap.getOperationalLayers().add(layerCopy); } //Log.i(TAG,"Mobile Map SR: " + mMap.getSpatialReference().getWKText()); //ArcGISTiledLayer tiledLayerBaseMap = new ArcGISTiledLayer("http://services.arcgisonline.com/arcgis/rest/services/World_Topo_Map/MapServer"); //mMap.getBasemap().getBaseLayers().add(tiledLayerBaseMap); // Log.i(TAG,"Mobile Map SR: " + tiledLayerBaseMap.getSpatialReference().getWKText()); // mMapView.setMap(mMap); /* for (final Layer layer:mMap.getOperationalLayers() ) { SpatialReference sr =layer.getSpatialReference(); if(sr== null) Log.i(TAG,"Layer "+ layer.getName() + " : SR==null" ); else Log.i(TAG,"Layer "+ layer.getName() + " : " + sr.getWKText() ); final ListenableFuture<List<LegendInfo>> future = layer.fetchLegendInfosAsync(); future.addDoneListener(new Runnable() { @Override public void run() { Log.i(TAG,"Layer "+ layer.getName() + " : "+ future.isDone()); if(future.isDone()) { try { List<LegendInfo> legendInfos = future.get(); for(LegendInfo linfo:legendInfos) { Log.i(TAG,"info: " + linfo.getName() + " : " + linfo.getSymbol().toJson() ); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } }); } */ Log.i("mpackage","mMapView" + mMapView.getCurrentViewpoint(Viewpoint.Type.CENTER_AND_SCALE).toJson()); //45.872546, -121.976170 Wind River Road //-110.3626,46.8797 mMapView.setViewpointCenterAsync(new Point(-121.44,45.115, SpatialReferences.getWgs84()),144000); Log.i("mpackage","mMapView" + mMapView.getCurrentViewpoint(Viewpoint.Type.CENTER_AND_SCALE).toJson()); } else { // Log an issue if the mobile map package fails to load Log.e(TAG, mapPackage.getLoadError().getMessage()); } } }); //[DocRef: END] } Layer copyLayer(Layer l) { if(l instanceof FeatureLayer) { return ((FeatureLayer) l).copy(); } if(l instanceof ImageTiledLayer) { return((WebTiledLayer)l).copy(); } if(l instanceof ArcGISTiledLayer) { return ((ArcGISTiledLayer)l).copy(); } if(l instanceof ArcGISVectorTiledLayer) { return ((ArcGISVectorTiledLayer)l).copy(); } if(l instanceof MobileBasemapLayer) { return ((MobileBasemapLayer)l).copy(); } return null; } @Override protected void onPause() { super.onPause(); mMapView.pause(); } @Override protected void onResume() { super.onResume(); mMapView.resume(); } @Override protected void onDestroy() { super.onDestroy(); mMapView.dispose(); } }
Hi Nathan,
Apologies for the delay. I had a look at your code and I believe I've got it working.
The problem is one of spatial references, I believe. mMap is being constructed with a base map type whose spatial reference is different to that of the mobile map package.
So to make your code run, change the constructor for mMap from:
mMap = new ArcGISMap(Basemap.Type.TOPOGRAPHIC_VECTOR,44.417,-112.08,11);
to
mMap = new ArcGISMap();
I'd also recommend commenting out
mMapView.setViewpointCenterAsync(new Point(-121.44,45.115, SpatialReferences.getWgs84()),144000);
to begin with, since it points slightly west of the data.
Hope this helps! Please let me know if you have any more issues.
Trevor
Okay, but if I take out that line, I am no longer mixing layers, am I?
I still don't have any example of mixing layers from a mobile map package.
Your yellowstone map had this SR:
SR= {"wkid":26912,"wkt":"PROJCS[\"NAD_1983_UTM_Zone_12N\",GEOGCS[\"GCS_North_American_1983\",DATUM[\"D_North_American_1983\",SPHEROID[\"GRS_1980\",6378137.0,298.257222101]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"False_Easting\",500000.0],PARAMETER[\"False_Northing\",0.0],PARAMETER[\"Central_Meridian\",-111.0],PARAMETER[\"Scale_Factor\",0.9996],PARAMETER[\"Latitude_Of_Origin\",0.0],UNIT[\"Meter\",1.0]]"}
So maybe that's not compatible with the basemap.
I then tried one of my own with this SR:
SR= {"wkid":102100,"latestWkid":3857,"wkt":"PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Mercator_Auxiliary_Sphere\"],PARAMETER[\"False_Easting\",0.0],PARAMETER[\"False_Northing\",0.0],PARAMETER[\"Central_Meridian\",0.0],PARAMETER[\"Standard_Parallel_1\",0.0],PARAMETER[\"Auxiliary_Sphere_Type\",0.0],UNIT[\"Meter\",1.0]]"}
Seems like that should be compatible with BaseMap Topographic, right? In fact it should be compatible with just about any map layers I plan to use.
I tried putting a basemap back in.
mMap = new ArcGISMap(Basemap.Type.TOPOGRAPHIC,44.417,-112.08,11);
Screen is blank. No grid. Nothing.
Back where I started.
If it is the spatial reference that's the problem, how do I make an mmpk that will be compatible with standard tiled basemaps?
If it is not the spatial reference, then what is the problem?
Hi Again,
I've dug into this and you're right its doesn't seem to be a spatial reference issue and there doesn't seem to be a reason why it shouldn't work. So I've logged this as a bug.
In the meantime, it sounds like you know the work around?
yourMobileMapPackage.unpackAsync(pathToMMPK, destinationFolder)
When its done unpacking, get the path to the geodatabase locally on the device. Use the geodatabase to access feature tables. Build feature layers from the feature tables.
Let me know how you get on.
Cheers,
Trevor
Support helped me and we narrowed it down to a problem with the sample.
It had little to do with the mappackage itself.
The sample didn't have Internet permission in the manifest so it choked when trying to load the map layer, without any meaningful messages that that was the case.
Internet permission is default on 6.0, but it stills need to be in the manifest.
I can't believe it was that easy.
Nathan
Confirmed that I still get zero results if I try this on 100.4.
Should I file a support ticket on this?
If this isn't going to work, I can't keep wasting time on it. I will have to extract the pieces, .geodatabase or whatever, from the .mmpk, and distribute them that way. However silly that is, it seems like the only option.