I'm creating a map centric application that will be used on an isolated network so I will not be able to download map tiles on the fly. I need to be able to load the tiles from local storage. I intend to have a full basemap layer that shows the entire globe from a high level and then want to be able to load specific areas with greater levels of detail as needed. I have ArcGIS Pro and I'm able to create .tpk files with out issue. I am using them like this:
TileCache* fullBasemap = new TileCache("D:\\MapData\\World Imagery_807F69C9-D40F-47F2-991E-FA43858D63F6.tpk", this);
TileCache* santaMargBasemap = new TileCache("D:\\MapData\\World Imagery_63490B3C-EC2F-4EBA-BBFC-33080B4F1878.tpk", this);
ArcGISTiledLayer* baseTileLayer = new ArcGISTiledLayer(fullBasemap, this);
ArcGISTiledLayer* detailTileLayer = new ArcGISTiledLayer(santaMargBasemap, this);
basemap = new Basemap(baseTileLayer, this);
basemap->baseLayers()->append(detailTileLayer);
The full basemap does display as expected but when I am trying to zoom into the area where the detailed map tiles have been loaded I can only zoom into level of the baseTileLayer and no further. How do I enable the ability to zoom into the detailed map layer? In ArcGIS Pro I can zoom in to the detailed layer, and when I do, the lower detailed base layer surrounding the detailed part disappears.
At low detail zoom level:
Just past the low level detail threshold and into the detailed tile layer:
I was hoping the zooming would work similarly in the SDK but I don't think I'm setting it up properly.
How can I display the detailed tile layer properly?
Thanks for any and all assistance!
Brad DuBois
I've tried a few more things, still not able to load the detailed tpk file. I'm now using the doneLoading signal to set up the map. The call to load the detailed tile package finished with an error "An invalid argument was passed in." The function call is the same as the basemap's load call which has no error. I am able to load the basemap without issue with the doneLoading method (I found this for reference: Trying to load more than one tpk file when I am offline ) as long as I don't conditionally gate the map setup based on the successful completion of the loading of the detailed tile package. The .tpk files were both created using the "Download Map" option in ArcGIS Pro, latest version. Both of the maps have the same spatial reference. They both are viewable in ArcGIS Pro. Once difference is the level of detail captured in the detailed tile package is that there seem to be multiple layers of detail contained therein and its size is very large compared to the basemap:
Is there a tile package size threshold that I am hitting? I haven't seen anything about a file size limit either. I'm not sure about how it uses the tpk files internally. Does loading put the entire contents into RAM? Or does the loading process just index the tpk file and then load only the tiles needed for current display (I'm assuming it's similar to the latter)?
As to the spatial reference, in ArcGIS Pro I see this in the properties:
But when queried in code the spatial reference returned is the "Previous WKID" not the "WKID":
"Base Map Layer Spatial Reference WKID: 102100"
Here is the code that is initiating the tile cache loads from tpk files. I current hacked it to load the basemap regardless of the failure in the loading of the detailed map. I also commented out the attempt to load that layer in setMap(). The setUpMapLayers() is called in the same context of where the MapQuickView is set from QML.
void MissionMap::setUpMapLayers()
{
TileCache * tileCacheBaseMap = new TileCache( "D:\\MapData\\FullBasemap.tpk", this);
baseMapTpkLayer_ = std::make_shared<ArcGISTiledLayer>( tileCacheBaseMap, this );
baseMapTpkLayer_->load();
TileCache * tileCacheSantaMarg = new TileCache("D:\\MapData\\SantaMargDetail.tpk", this);
santaMargTpkLayer_ = std::make_shared<ArcGISTiledLayer>( tileCacheSantaMarg, this );
santaMargTpkLayer_->load();
connect(santaMargTpkLayer_.get(), &ArcGISTiledLayer::doneLoading, this, [this](Error error)
{
qDebug() << "Error loading: " << error.code() << " - message: " << error.message();
if (santaMargTpkLayer_->loadStatus() != LoadStatus::Loaded)
return;
qDebug() << "santaMargTpkLayer_ loaded";
qDebug() << santaMargTpkLayer_->spatialReference().wkid();
if (baseMapTpkLayer_->loadStatus() == LoadStatus::Loaded)
{
qDebug() << "santaMargTpkLayer_" << "setMap()";
setMap();
}
});
connect(baseMapTpkLayer_.get(), &ArcGISTiledLayer::doneLoading, this, [this](Error error)
{
if (baseMapTpkLayer_->loadStatus() != LoadStatus::Loaded)
return;
qDebug() << "baseMapTpkLayer_ loaded" << " Santa Marg Map Loading State: " << (int)santaMargTpkLayer_->loadStatus();
qDebug() << baseMapTpkLayer_->spatialReference().wkid();
if (santaMargTpkLayer_->loadStatus() == LoadStatus::Loaded || true)
{
qDebug() << "baseMapTpkLayer_" << "setMap()";
setMap();
}
});
}
void MissionMap::setMap()
{
Basemap* bm = new Esri::ArcGISRuntime::Basemap( this );
//bm->baseLayers()->append(santaMargTpkLayer_.get());
bm->baseLayers()->append(baseMapTpkLayer_.get());qDebug() << "Base Map Layer Spatial Reference WKID: " << baseMapTpkLayer_->spatialReference().wkid();map_ = new Map(baseMapTpkLayer_->spatialReference(), this);
map_->setBasemap(bm);
mapView_->setMap(map_);
}
void MissionMap::setMapView(MapQuickView *mapView){mapView_ = mapView;mapView_->setAttributionTextVisible(false);connect(mapView_, &MapQuickView::mouseClicked, this, &MissionMap::onMouseClicked);connect(mapView_, &MapQuickView::mouseReleased, this, &MissionMap::onMouseReleased);connect(mapView_, &MapQuickView::mousePressed, this, &MissionMap::onMousePressed);connect(mapView_, &MapQuickView::mousePressedAndHeld, this, &MissionMap::onMousePressedAndHeld);connect(mapView_, &MapQuickView::mouseMoved, this, &MissionMap::onMouseMoved);connect(mapView_, &MapQuickView::keyReleased, this, &MissionMap::onKeyReleased);connect(mapView_, &MapQuickView::identifyGraphicsOverlayCompleted, this, &MissionMap::identifyGraphicsOverlayCompleted);// connect(sceneView_, &SceneQuickView::identifyLayerCompleted, this, &MissionMap::identifyGraphicsLayerCompleted);connect(mapView_, &MapQuickView::identifyLayersCompleted, this, &MissionMap::identifyGraphicsLayerCompleted);if (!is3DMode_){
mapView_->graphicsOverlays()->append(routeOverlay_);mapView_->graphicsOverlays()->append(zoneOverlay_);}
connect(mapView_, &MapQuickView::viewpointChanged, this,[this]{
if (sceneView_ && mapView_->isNavigating()){
sceneView_->setViewpoint(mapView_->currentViewpoint(ViewpointType::CenterAndScale), 0);}
},
Qt::UniqueConnection);qDebug() << "MapView WKID: " << mapView_->spatialReference().wkid();emit mapViewChanged();setUpMapLayers();}
Ultimately I am looking to have the ability to load any number of .tpk files to provide the level of detail needed for a particular mission as well as providing the full globe basemap. I need this in both the regular 2D map and in a scene view (I am also attempting to create and load elevation data exported from ArcGIS Pro into a .tif file. I am probably going to be asking a question about that on this forum very soon). If anybody has any tips on how to make this work for me I would greatly appreciate it.
Thanks,
Brad DuBois
Did you try setting the mix/max scale for each of the ArcGISTiledLayers? Layer Class | ArcGIS for Developers
Hello Lucas,
I have 3 levels of tile detail working now for 2D map and I'm not setting the layer min/max scale at this time. I don't have a complete set of tiles that I'm working with so far, just enough to test concepts with. I am currently specifying a tile top level directory to pull .tpk files from and then I'm creating a list of ArcGISTiledLayer objects from that. Then I sort that list by max scale of each layer before loading them into the Basemap baseLayers. This is working for me but may be a naïve/inefficient way to handle this. Not sure what it will look like once I have a larger set of tiles.
I haven't been able to really find a comprehensive resource that talks about best practices when implementing offline maps. Is there any reference or example you can point me to?
I tried similar method for trying to use same offline map tile packages for a scene view but not having any luck with that. In the online maps initialization there is an operationalLayer that we append the ArcGISSceneLayer to, not the basemap. Not sure how to handle that in offline mode so far. Anything you can point me to with getting this to work?
I have had success in setting up offline elevation data using dted.
So I'm getting close but need some help getting over the threshold. It's a big API but I'm not sure how to use it all the pieces to get me where I need to go.
As always, your help is greatly appreciated.
Thanks,
Brad
I just found my issue with getting the offline map scene view to display imagery. I now can see my full basemap but the subsequent behavior with regard to the high detail tiles is not working the same as it does with the 2D map. The same method of creating the basemap is being used with the same tiles but in the 2D map I can zoom down to this level of detail:
But with 3D scene it is zooming all they way down but keeping the same high level basemap imagery:
Perhaps this is controlled by using the min/max layer settings as you mentioned earlier. I would love to see a write up on how those values are used so I can dynamically set the values based on discovered tile packages. Can anybody point me to something like that?
Thanks!
Brad
Sorting the tile packages by min scale allows the scene tiles to display and zoom as expected. With my limited set of tiles currently available:
So, one step close to a working scheme but still not sure this is the best way to do it.
I'm not sure what your larger plan/requirements are for your app, but have you looked into authoring mobile map packages and mobile scene packages from ArcGIS Pro. You should be able to lay out all of the data and layers exactly like you want it in pro, publish the mmpk and mspk, and then open those in Runtime. That could help simplify your app logic perhaps, but I'm not sure if this pattern fits into you workflow or if you need to build it all up programmatically.
I think overall having the ability to drop in tiles and be able to use them immediately is going to be better for me. Having to edit with ArcGIS Pro and move large files seems like it might be a bit of a bottleneck for the intended environment.
It feels like if I had a good understanding of how to link the min/max scale values per tile together based on their scales then I would be in good shape. Yesterday I was able to load all my tpk files in both the map and the scene and all were zooming and behaving as expected but today only a few of the tpks are showing up on the scene, while all are visible on the map. I think it might be due to the min/max scale settings bein uninitialized or otherwise incorrectly set and some sort of different in load times is affecting the visibility, somewhat at random.
Any insight on that would be really helpful.
Thanks,
Brad