|
POST
|
I believe that not all areas of the world have tiles to the highest LOD level. That is probably what you're encountering here. One option you could consider is exporting the vector tile version of the basemap. See here. Can you check the output of estimateExportTileSize to know that the call to export will fail? Update: Note that there are also For Export versions of the Vector Tile layers which have a smaller set of fonts. Exporting those is about 70MB smaller than the default Vector Tile layers, but may not have all the fonts you need for non-English labels. The World Streets raster basemap does not include tiles for levels 20-23. The LODs are defined so that the layer can be used in maps that resample lower resolution tiles without limiting the zoom level of the map. The world imagery service *does* include tiles up to LOD level 23.
... View more
02-21-2020
10:04 AM
|
0
|
1
|
2242
|
|
POST
|
A couple of notes. The openstreetmap basemap that Runtime provides a shortcut for pulls tiles from openstreetmap.org, so William Craft's response above is correct: add *.openstreetmap.org to the whitelist. You can find this out by turning on URL logging in the Runtime and checking the console output in Xcode. You can turn on global debugging with this code (though don't put this in production code as it affects performance): AGSRequestConfiguration.global().debugLogRequests = true From that I could see that you'd need to whitelist openstreetmap.org. 2020-02-21 12:20:23.475207-0500 TestOSM[30781:5832027] **------>> sending request**
- [https://a.tile.openstreetmap.org/0/0/0.png](https://a.tile.openstreetmap.org/0/0/0.png)
- GET
-----
2020-02-21 12:20:23.608706-0500 TestOSM[30781:5832024] **------>> sending request**
- [https://b.tile.openstreetmap.org/1/0/0.png](https://b.tile.openstreetmap.org/1/0/0.png)
- GET
----- There is a Vector Tile version of the OpenStreetMap basemap which Esri hosts now, and for which a whitelist entry of basemaps.arcgis.com or *.arcgis.com would be suitable. You can find that here. It is not currently part of the Runtime's shortcut enumerations when defining an AGSMap, but you can make use of it like this: let osmItem = AGSPortalItem(portal: .arcGISOnline(withLoginRequired: false),
itemID: "3e1a00aeae81496587988075fe529f71")
let osmVectorLayer = AGSArcGISVectorTiledLayer(item: osmItem)
let map = AGSMap(basemap: AGSBasemap(baseLayer: osmVectorLayer)) Hope this helps!
... View more
02-21-2020
09:19 AM
|
1
|
1
|
5027
|
|
POST
|
Reading your response I was going to suggest Group Layers (or else using Feature Collection Layers as your placeholders). I think Group Layer is your friend here. Note also the new AGSSubtypeFeatureLayer that was released with 100.7. That's very helpful where you have a single feature service layer with multiple subtypes that you want to treat separately. It's conceptually like an automated group layer: It'll give you a sub-layer per subtype, but is smart about how it queries back to the server when it needs to get data for those sub-layers.
... View more
02-14-2020
08:07 AM
|
0
|
0
|
1230
|
|
POST
|
Can you explain a little more how you're using it this way that makes it useful for you? Not sure I follow. Thanks! Nick.
... View more
02-13-2020
03:06 PM
|
0
|
2
|
1230
|
|
POST
|
We don't have a way to lean on UIKit system colors (one of the side-effects of having a cross-platform core to the Runtime). While you should be able to manually modify the labels, we don't have an API for this yet. But if you're willing to tinker with some JSON you should be able to get this to work. On an AGSFeatureLayer there is a labelDefinitions property. This is an array of AGSLabelDefinition objects. You could call toJSON on each AGSLabelDefinition in that array, modify the appropriate symbol color and then create a new AGSLabelDefinition from the modified JSON using AGSLabelDefinition.fromJSON, and replace the definition in the labelDefinitions array. It's messy but should work. We're working on fleshing out these APIs in future releases, but for now ask that you get the JSON and work with that. Let me know if that helps. Nick
... View more
01-28-2020
04:47 PM
|
0
|
0
|
826
|
|
POST
|
As you work with the Sketch Editor, it maintains a current geometry (could be a point, polyline, polygon, etc. depending on the creationMode). In your respondToGeomChanged function, you can read the geometry property of the AGSSketchEditor. That function will be called whenever the user has changed the sketched geometry in some way. Hold on to that AGSGeometry to pass in to the Sketch Editor next time you call start(with:creationMode:). Looks like your code is from the Sample app. You could try something like this: private var previouslySketchedGeometries = [AGSSketchCreationMode:AGSGeometry]()
@objc
func respondToGeomChanged() {
//Enable/disable UI elements appropriately
undoBBI.isEnabled = sketchEditor.undoManager.canUndo
clearBBI.isEnabled = sketchEditor.geometry != nil && !sketchEditor.geometry!.isEmpty
// Store the current geometry for this mode
if sketchEditor.isSketchValid {
previouslySketchedGeometries[sketchEditor.creationMode] = sketchEditor.geometry
}
}
// MARK: - Actions
@IBAction func geometryValueChanged(_ segmentedControl: UISegmentedControl) {
var creationMode: AGSSketchCreationMode = .unset
switch segmentedControl.selectedSegmentIndex {
case 0://point
creationMode = .point
case 1://polyline
creationMode = .polyline
case 2://freehand polyline
creationMode = .freehandPolyline
case 3://polygon
creationMode = .polygon
case 4://freehand polygon
creationMode = .freehandPolygon
default:
return
}
// If we previously drew something for this mode, get hold of it…
let previousGeometryForMode = previouslySketchedGeometries[creationMode]
self.sketchEditor.start(with: previousGeometryForMode, creationMode: creationMode)
self.mapView.sketchEditor = self.sketchEditor
} Hope this helps.
... View more
01-22-2020
10:40 AM
|
1
|
1
|
1693
|
|
POST
|
Aha. I think you're thinking of angleAlignment on AGSMarkerSymbol (guessing you're using AGSPictureMarkerSymbol which is a subclass). Set that to .map. If you're using a renderer you can also use the rotationExpression. That way you can just update an attribute on each graphic as needed and the renderer will take care of everything else.
... View more
01-15-2020
12:45 PM
|
0
|
0
|
1428
|
|
POST
|
Can you explain your question a bit more please? For example: Do you want the graphic's symbol to point in the direction that your device is pointing? Does the graphic represent your location, or that of some other vehicle?
... View more
01-15-2020
12:09 PM
|
0
|
2
|
1428
|
|
POST
|
Hi Cory, As Mary and Joe pointed out, you shouldn't have to call downloadGeodatabaseForLayers and you shouldn't set returnSchemaOnlyForEditableLayer to true (that's why you're not getting any data). There's a more fundamental issue which is, I think, why you might be seeing an error. You are not holding on to the OfflineMapTask, nor the Job. Not entirely sure why that would give you the error you're seeing but I suspect something is bubbling up internally and being caught by code that expects something specific. Anyway, if you provide a class level var to hold the AGSOfflineMapTask and the AGSGenerateOfflineMapJob (see lines 34 and 35 below), and you remove the line that sets returnSchemaForEditableLayers, your code works: import UIKit
import ArcGIS
class ViewController: UIViewController {
@IBOutlet weak var mapView: AGSMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
mapView.map = AGSMap(url: URL(string: "https://geeknixta.maps.arcgis.com/home/item.html?id=8e88bb4d6cc048cbb61686fbfb26a0b9")!)
mapView.touchDelegate = self
}
var offlineMapTask: AGSOfflineMapTask!
var offlineMapJob: AGSGenerateOfflineMapJob!
func takeMapOffline(areaOfInterest: AGSGeometry) {
//1. Declare offline map task
//let offlineMapTask = AGSOfflineMapTask(onlineMap: self.mapView.map!)
guard let onlineMap = mapView.map else { return }
// IMPORTANT: hold on to the Task while it executes
offlineMapTask = AGSOfflineMapTask(onlineMap: onlineMap)
//2. Define parameters
offlineMapTask.defaultGenerateOfflineMapParameters(withAreaOfInterest: areaOfInterest, completion: { [weak self] (parameters, error) in
// IMPORTANT: If we hadn't held on to the task above, this is at risk of not executing.
// However, because offlineMapTask was referenced below when creating the job,
// Swift would have kept it around, but it's better practice to keep hold of it just in case.
print("@@@ Generate parameters 1: Start")
if let error = error {
print("@@@ Generate parameters 2b: Error, getting parameters failed")
print(error)
self?.showDownloadFailedAlert("Error generating offline parameters: \(error.localizedDescription)")
return
}
guard parameters != nil else {
print("takeMapOffline: No parameters provided")
return
}
guard let self = self else { return }
if let parameters = parameters {
parameters.maxScale = 3600
parameters.minScale = 30000
parameters.includeBasemap = true
parameters.isDefinitionExpressionFilterEnabled = true
parameters.continueOnErrors = true
// parameters.returnSchemaOnlyForEditableLayers = true
parameters.attachmentSyncDirection = .bidirectional
parameters.returnLayerAttachmentOption = .allLayers
print("@@@ Generate parameters 2a: Got parameters!")
//4. Create the job
print("@@@ Generate parameters 3: create job")
let mapStorageURL = self.getNewOfflineMapDirectoryURL()
UserDefaults.standard.set(mapStorageURL, forKey: "OfflineMapDirectoryUrl")
// IMPORTANT: Hold on to the Job while it executes. This is where the original code was
// exposing itself to not completing properly.
self.offlineMapJob = self.offlineMapTask.generateOfflineMapJob(with: parameters, downloadDirectory: mapStorageURL)
//5. Run the job
self.startOfflineMapJob(job: self.offlineMapJob, areaOfInterest: areaOfInterest)
}
})
}
var downloadProgressObserver: NSKeyValueObservation?
func startOfflineMapJob(job: AGSGenerateOfflineMapJob, areaOfInterest: AGSGeometry) {
downloadProgressObserver = job.progress.observe(\.fractionCompleted, changeHandler: { (progress, _) in
DispatchQueue.main.async {
print("Progress at \(progress.fractionCompleted * 100)%")
}
})
print("Job Started: Downloading map selection. This may take several minutes. Tap here to cancel.")
job.start(statusHandler: { (status) in
print("Status [\(String(describing: job.progress.fractionCompleted))]: \(status)")
// You really want to wait for the error to come through the completion
// block rather than checking on status updates.
if job.status == .failed {
DispatchQueue.main.async {
// SVProgressHUD.dismiss()
print("Progress Done")
}
self.showDownloadFailedAlert("Server Error")
return
}
}) { (result, error) in
DispatchQueue.main.async {
// SVProgressHUD.dismiss()
print("Progress Done")
}
if let error = error {
print(error)
self.showDownloadFailedAlert(error.localizedDescription)
return
}
guard let result = result else {
self.showDownloadFailedAlert("Didn't receive a response.")
return
}
if result.hasErrors {
result.layerErrors.forEach{(layerError) in
print((layerError.key.name), " Error taking this layer offline: ", (layerError.value))
}
result.tableErrors.forEach{(tableError) in
print((tableError.key.tableName), " Error taking this table offline")
}
self.showDownloadFailedAlert("Error taking layers offline.")
}
else {
//display the offline map
self.mapView.map = result.offlineMap
for layer in result.offlineMap.operationalLayers {
print(layer)
}
}
}
}
}
extension ViewController: AGSGeoViewTouchDelegate {
func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
guard let areaOfInterest = mapView.currentViewpoint(with: .boundingGeometry)?.targetGeometry else { return }
takeMapOffline(areaOfInterest: areaOfInterest)
}
func showDownloadFailedAlert(_ message: String) {
print(message)
}
func getNewOfflineMapDirectoryURL() -> URL {
FileManager.default.getNewOfflineMapDirectoryURL()
}
}
extension FileManager {
private var onDemandMapsDirectory: URL {
get {
let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let onDemandDirectoryURL = documentDirectoryURL.appendingPathComponent("on-demand-maps")
return onDemandDirectoryURL
}
}
func getNewOfflineMapDirectoryURL() -> URL {
try? FileManager.default.createDirectory(at: onDemandMapsDirectory, withIntermediateDirectories: true, attributes: nil)
return onDemandMapsDirectory.appendingPathComponent("\(ISO8601DateFormatter().string(from: Date()))")
}
func clearUpOfflineMaps() {
try? FileManager.default.contentsOfDirectory(at: onDemandMapsDirectory, includingPropertiesForKeys: nil, options: FileManager.DirectoryEnumerationOptions.skipsSubdirectoryDescendants).forEach {
do {
try FileManager.default.removeItem(at: $0)
print("Deleted '\($0)'")
} catch {
print("Could not delete '\($0)': \(error.localizedDescription)")
}
}
}
}
... View more
01-08-2020
08:09 AM
|
0
|
0
|
3933
|
|
POST
|
Hi. Are you asking for a programatic way to import the geometry JSON into ArcGIS Pro, or for a tool in ArcGIS Pro to read the geometry JSON? If the former, I think this question belongs in the ArcGIS Pro SDK place. Programatically, perhaps start here. It also looks like there's a JSON Import tool in ArcGIS Pro. See here. But note that geometry.tojson() will only output the JSON for the geometry object, not a full feature. The ArcGIS Pro links above require full Featureset JSON - see here. You may need to process your geometry JSON into that format to import it.
... View more
01-03-2020
08:00 AM
|
0
|
0
|
1006
|
|
POST
|
Hi Robert Koch, do you still need to load your layer before it'll work? I wasn't able to reproduce this behavior but am keen to understand if there's some case that triggers it which we haven't considered and need to fix.
... View more
01-02-2020
07:07 AM
|
0
|
1
|
1845
|
|
POST
|
Actually, looking at your code, I must not understand what you're trying to achieve. I'm not an Android dev, but it looks like you're stripping out all the attributes from all the identified features, converting each attribute value to a string (is there a reason you want to discard the type info here?), and putting a Map of these converted values in a list - one Map for each feature. If you're saying that in the case of a Feature Collection you're not getting any GeoElements back to read the attributes from, have you tried looking at the subLayerResults?
... View more
12-21-2019
01:49 PM
|
2
|
1
|
2453
|
|
POST
|
Fields returns you a list of Field objects. Each Field has a Name and an Alias: Field (ArcGIS Runtime SDK for Android 100.7.0)
... View more
12-21-2019
01:39 PM
|
0
|
0
|
2453
|
|
POST
|
If I understand your question properly, you're nearly there! Each FeatureTable has Fields you can read. See getFields(). Does that help?
... View more
12-21-2019
09:01 AM
|
0
|
4
|
2453
|
|
BLOG
|
The latest release of the Runtime SDK for iOS is here (see the release notes, and the general announcement over on the ArcGIS Blog), and it builds upon the foundations introduced in update 6. Key highlights include adding a LOT of network utility functionality, so that you can now build really powerful field apps that include a full suite of network tracing and more. We've also added the ability to license runtime at all levels using a named user (previously you could only license at Lite or Basic). See the announcement for more details. Of significance from the iOS perspective: Support for iOS 11 is being deprecated. At the next Runtime update, a minimum of iOS 12 will be required. Augmented Reality Scenes can be clipped to help them fit better onto a real-world surface. The OAuth panel in ArcGIS Online now honors the iOS device's dark mode state. The Toolkit has already been updated for 100.7. Download Update 7 today, and let us know what you're building.
... View more
12-20-2019
09:21 AM
|
0
|
0
|
761
|
| Title | Kudos | Posted |
|---|---|---|
| 2 | 05-14-2026 07:07 AM | |
| 2 | 04-30-2026 10:59 AM | |
| 4 | 04-22-2026 08:07 AM | |
| 1 | 01-29-2026 09:39 AM | |
| 1 | 12-17-2025 10:12 AM |