Hi,
I'm new to The Esri Community and Android SDK programming. I'm having an issue where I need to offline a section of the map as outlined in this tutorial - https://developers.arcgis.com/android/latest/guide/take-map-offline-on-demand.htm but I'm using inherited code outlined below to do so and the output map doesn't show all of the layers of the maps and throws an exception:
I/System.out: Non Outage Completion cannot be taken offline
Error : com.esri.arcgisruntime.ArcGISRuntimeException: Illegal state: Unable to support duplicate feature layers already exists
I use this code to generate the map and the exception:
fun generateOfflineMap() {
// create a progress dialog to show map download progress
progressDialog!!.setTitle("Generate Offline Map")
progressDialog!!.setMessage("Downloading map...")
progressDialog!!.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
progressDialog!!.isIndeterminate = false
progressDialog!!.setCanceledOnTouchOutside(false)
progressDialog!!.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel") { dialog, which ->
downloadMsg!!.visibility = View.GONE
mGraphicsOverlay!!.graphics.clear()
mapdownloadJob!!.cancel()
//set the flag to false to re-select the download area for consecutive downloads
isMapDownloadAreaSelected = false
isMapDownloadInProgress = false
dialog.dismiss()
}
if (!isMapDownloadAreaSelected) {
downloadMsg!!.visibility = View.VISIBLE
// create a graphics overlay for the map view
mGraphicsOverlay = GraphicsOverlay()
mapView!!.graphicsOverlays.add(mGraphicsOverlay)
// create a graphic to show a box around the extent we want to download
mDownloadArea = Graphic()
mGraphicsOverlay!!.graphics.add(mDownloadArea)
val simpleLineSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.RED, 2F)
mDownloadArea!!.symbol = simpleLineSymbol
// upper left corner of the area to take offline
minScreenPoint = Point(200, 200)
// lower right corner of the downloaded area
maxScreenPoint = Point(mapView!!.width - 200,
mapView!!.height - 200)
// convert screen points to map points
minPoint = mapView!!.screenToLocation(minScreenPoint)
maxPoint = mapView!!.screenToLocation(maxScreenPoint)
// use the points to define and return an envelope
if (minPoint != null && maxPoint != null) {
val envelope = Envelope(minPoint, maxPoint)
mDownloadArea!!.geometry = envelope
}
//Debugging - use this to get errors with layers and tables
var offlineMapTask = OfflineMapTask(map);
var minScale = mapView!!.mapScale
val maxScale = mapView!!.map.maxScale
val generateOfflineMapParameters = GenerateOfflineMapParameters(mDownloadArea!!.geometry, minScale, maxScale)
val offlineMapCapabilitiesFuture: ListenableFuture<OfflineMapCapabilities> = offlineMapTask.getOfflineMapCapabilitiesAsync(generateOfflineMapParameters)
offlineMapCapabilitiesFuture.addDoneListener {
val offlineMapCapabilities: OfflineMapCapabilities = offlineMapCapabilitiesFuture.get()
if (offlineMapCapabilities.hasErrors()) {
// Handle possible errors with layers
for ((key, value) in offlineMapCapabilities.getLayerCapabilities()) {
if (!value.isSupportsOffline()) {
println(key.getName().toString() + " cannot be taken offline\n" +
"Error: " + value.getError())
}
}
// Handle possible errors with tables
for ((key, value) in offlineMapCapabilities.getTableCapabilities()) {
if (!value.isSupportsOffline()) {
println(key.tableName.toString() + " cannot be taken offline\n" +
"Error : " + value.getError())
}
}
} else {
// All layers and tables can be taken offline!
showMessage("All layers are good to go!")
}
}
//End Debugging
// update the download area box whenever the viewpoint changes
mapView!!.addViewpointChangedListener { viewpointChangedEvent: ViewpointChangedEvent? ->
if (map!!.loadStatus == LoadStatus.LOADED) {
// upper left corner of the area to take offline
minScreenPoint = Point(200, 200)
// lower right corner of the downloaded area
maxScreenPoint = Point(mapView!!.width - 200,
mapView!!.height - 200)
// convert screen points to map points
minPoint = mapView!!.screenToLocation(minScreenPoint)
maxPoint = mapView!!.screenToLocation(maxScreenPoint)
// use the points to define and return an envelope
if (minPoint != null && maxPoint != null) {
val envelope = Envelope(minPoint, maxPoint)
mDownloadArea!!.geometry = envelope
}
}
}
// map download area selected
isMapDownloadAreaSelected = true
} else {
if (!isMapDownloadInProgress) {
progressDialog!!.progress = 0
progressDialog!!.show()
// delete any offline map already in the cache
deleteDirectory(File(offlineMapDirectoryPath))
// specify the extent, min scale, and max scale as parameters
var minScale = mapView!!.mapScale
val maxScale = mapView!!.map.maxScale
// minScale must always be larger than maxScale
if (minScale <= maxScale) {
minScale = maxScale + 1
}
val generateOfflineMapParameters = GenerateOfflineMapParameters(mDownloadArea!!.geometry, minScale, maxScale)
// create an offline map offlineMapTask with the map
val offlineMapTask = OfflineMapTask(mapView!!.map)
// create an offline map job with the download directory path and parameters and start the job
mapdownloadJob = offlineMapTask.generateOfflineMap(generateOfflineMapParameters, offlineMapDirectoryPath)
isMapDownloadInProgress = true
// replace the current map with the result offline map when the job finishes
mapdownloadJob.addJobDoneListener(Runnable {
if (mapdownloadJob.getStatus() == Job.Status.SUCCEEDED) {
val result = mapdownloadJob.getResult()
downloadMsg!!.visibility = View.GONE
mGraphicsOverlay!!.graphics.clear()
//set the flag to false to re-select the download area for consecutive downloads
isMapDownloadAreaSelected = false
isMapDownloadInProgress = false
//show the map
mapView!!.map = result.offlineMap
//hide save map, show cancel map
//save_map.visibility = View.GONE
//cancel_map.visibility = View.VISIBLE
//hide the legend - it must be online
legendHeader!!.visibility = View.GONE
if (mAttributes != null) {
sharedPreferencesPseg.setString("ESDNUMBERFORSAVEDMAP", mAttributes!!.cadjobno)
} else {
sharedPreferencesPseg.setString("ESDNUMBERFORSAVEDMAP", "MAINTAB")
}
Toast.makeText(requireContext(), "Map downloaded successfully", Toast.LENGTH_LONG).show()
} else if (mapdownloadJob.getStatus() == Job.Status.FAILED) {
Toast.makeText(requireContext(), "Error in generate offline map", Toast.LENGTH_LONG).show()
} else if (mapdownloadJob.getStatus() == Job.Status.PAUSED) {
Toast.makeText(requireContext(), "Map download cancelled", Toast.LENGTH_LONG).show()
}
progressDialog!!.dismiss()
})
mapdownloadJob.addProgressChangedListener(Runnable { progressDialog!!.progress = mapdownloadJob.getProgress() })
mapdownloadJob.start()
} else {
mapdownloadJob!!.addProgressChangedListener { progressDialog!!.progress = mapdownloadJob!!.progress }
progressDialog!!.show()
}
}
}
When the code to display the map is called the map is displayed but with minimal detail and it appears layer(s) are left out. I'm not sure what is causing this. I also use this function below to open the map and wanted to provide this to see if there are errors in this code as well:
private fun openOfflineMap() {
val offlineMapPackage = MobileMapPackage(offlineMapDirectoryPath)
offlineMapPackage.loadAsync()
offlineMapPackage.addDoneLoadingListener {
if (offlineMapPackage.maps.size == 0) {
mapNotDownloadedMsg!!.visibility = View.VISIBLE
progressBar!!.visibility = View.GONE
} else {
mapView!!.map = offlineMapPackage.maps[0]
callout = mapView!!.callout
progressBar!!.visibility = View.GONE
// downloadMapIcon.setVisibility(View.GONE);
mapView!!.visibility = View.VISIBLE
mapView!!.onTouchListener = object : DefaultMapViewOnTouchListener(context, mapView) {
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
if (callout.isShowing()) {
callout.dismiss()
}
val screenPoint = Point(Math.round(e.x), Math.round(e.y))
// identifyResult(screenPoint);
identifyandDisplayResults(screenPoint)
return true
}
}
}
}
}
Any help you can provide is much appreciated! If there is a need for additional detail please let me know and I will provide as best I can.
Thanks,
John Meah