Skip to content
This repository has been archived by the owner on Jan 6, 2023. It is now read-only.

Commit

Permalink
Merge pull request #1198 from Esri/v.next
Browse files Browse the repository at this point in the history
Release 100.15.0.0
  • Loading branch information
yo1995 authored Aug 17, 2022
2 parents 67946e1 + 45fffc6 commit f2f05af
Show file tree
Hide file tree
Showing 39 changed files with 1,137 additions and 285 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ The ```main``` branch of this repository contains samples configured for the lat

## Requirements

* [ArcGIS Runtime SDK for iOS](https://developers.arcgis.com/ios/) 100.14.0 (or newer)
* [ArcGIS Runtime Toolkit for iOS](https://github.com/Esri/arcgis-runtime-toolkit-ios) 100.14.0 (or newer)
* [ArcGIS Runtime SDK for iOS](https://developers.arcgis.com/ios/) 100.15.0 (or newer)
* [ArcGIS Runtime Toolkit for iOS](https://github.com/Esri/arcgis-runtime-toolkit-ios) 100.15.0 (or newer)
* Xcode 13.0 (or newer)

The *ArcGIS Runtime SDK Samples app* has a *Target SDK* version of *14.0*, meaning that it can run on devices with *iOS 14.0* or newer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
'Open Street Map',
'OpenStreetMap',
'Play a KML Tour',
'SwiftUI'
'SwiftUI',
'Arcade'
}

# A set of category folder names in current sample viewer.
Expand Down
65 changes: 52 additions & 13 deletions arcgis-ios-sdk-samples.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

21 changes: 19 additions & 2 deletions arcgis-ios-sdk-samples/Content Display Logic/ContentPList.plist
Original file line number Diff line number Diff line change
Expand Up @@ -871,11 +871,11 @@
</dict>
<dict>
<key>displayName</key>
<string>Generate geodatabase</string>
<string>Generate geodatabase replica from feature service</string>
<key>storyboardName</key>
<string>GenerateGeodatabase</string>
<key>descriptionText</key>
<string>Generate a local geodatabase from an online feature service.</string>
<string>Generate a local geodatabase replica from an online feature service.</string>
<key>dependency</key>
<array>
<string>SanFranciscoTilePackage</string>
Expand Down Expand Up @@ -1114,6 +1114,14 @@
<key>storyboardName</key>
<string>FilterByTimeExtent</string>
</dict>
<dict>
<key>displayName</key>
<string>Query features with Arcade expression</string>
<key>descriptionText</key>
<string>Query features on a map using an Arcade expression.</string>
<key>storyboardName</key>
<string>QueryFeaturesArcadeExpression</string>
</dict>
</array>
</dict>
<dict>
Expand Down Expand Up @@ -1278,6 +1286,14 @@
<key>descriptionText</key>
<string>Create and add features whose attribute values satisfy a predefined set of contingencies.</string>
</dict>
<dict>
<key>displayName</key>
<string>Create mobile geodatabase</string>
<key>storyboardName</key>
<string>CreateMobileGeodatabase</string>
<key>descriptionText</key>
<string>Create and share a mobile geodatabase.</string>
</dict>
</array>
</dict>
<dict>
Expand Down Expand Up @@ -1475,6 +1491,7 @@
<key>dependency</key>
<array>
<string>SanDiegoDetour</string>
<string>SanDiegoNetworkData</string>
</array>
</dict>
<dict>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Y6W-OH-hqX">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Query Features Arcade Expression View Controller-->
<scene sceneID="s0d-6b-0kx">
<objects>
<viewController id="Y6W-OH-hqX" customClass="QueryFeaturesArcadeExpressionViewController" customModule="ArcGIS_Runtime_SDK_Samples" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eFF-Xw-cnc" customClass="AGSMapView">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
<color key="backgroundColor" systemColor="tertiarySystemBackgroundColor"/>
<constraints>
<constraint firstItem="eFF-Xw-cnc" firstAttribute="top" secondItem="5EZ-qb-Rvc" secondAttribute="top" id="0b7-R3-JFE"/>
<constraint firstAttribute="trailing" secondItem="eFF-Xw-cnc" secondAttribute="trailing" id="LC1-V0-Kcy"/>
<constraint firstAttribute="bottom" secondItem="eFF-Xw-cnc" secondAttribute="bottom" id="X2K-Dx-CNp"/>
<constraint firstItem="eFF-Xw-cnc" firstAttribute="leading" secondItem="5EZ-qb-Rvc" secondAttribute="leading" id="uQZ-ZU-cZ1"/>
</constraints>
</view>
<connections>
<outlet property="mapView" destination="eFF-Xw-cnc" id="RKB-Jt-3Z3"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="139" y="78"/>
</scene>
</scenes>
<resources>
<systemColor name="tertiarySystemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright 2022 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
//
// https://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.

import UIKit
import ArcGIS

class QueryFeaturesArcadeExpressionViewController: UIViewController {
@IBOutlet var mapView: AGSMapView! {
didSet {
mapView.map = makeMap()
// Set the touch delegate.
mapView.touchDelegate = self
}
}

/// The Arcade expression evaluation operation.
var evaluateOperation: AGSCancelable?
/// The evaluator to evaluate an `AGSArcadeExpression` under a given
/// `AGSArcadeProfile`.
var evaluator: AGSArcadeEvaluator?
/// Make and load a map.
func makeMap() -> AGSMap {
// Create a portal item with the portal and item ID.
let portalItem = AGSPortalItem(portal: .arcGISOnline(withLoginRequired: false), itemID: "539d93de54c7422f88f69bfac2aebf7d")
// Make a map with the portal item.
let map = AGSMap(item: portalItem)
return map
}

/// Evaluate the Arcade expression for the selected feature at the map point.
func evaluateArcadeInCallout(for feature: AGSArcGISFeature, at mapPoint: AGSPoint) {
guard let map = mapView.map else { return }
evaluateOperation?.cancel()
// Instantiate a string containing the Arcade expression.
let expressionValue =
"""
var crimes = FeatureSetByName($map, 'Crime in the last 60 days');
return Count(Intersects($feature, crimes));
"""
// Create an Arcade expression using the string.
let expression = AGSArcadeExpression(expression: expressionValue)
// Create an Arcade evaluator with the Arcade expression and an Arcade profile.
evaluator = AGSArcadeEvaluator(expression: expression, profile: .formCalculation)
let profileVariables = ["$feature": feature, "$map": map]
// Show progress hud.
UIApplication.shared.showProgressHUD(message: "Evaluating")
// Get the Arcade evaluation result given the previously set profile variables.
evaluateOperation = evaluator!.evaluate(withProfileVariables: profileVariables) { [weak self] result, error in
// Dismiss progress hud.
UIApplication.shared.hideProgressHUD()
guard let self = self else { return }
self.evaluateOperation = nil
self.evaluator = nil
if let result = result, let crimeCount = result.cast(to: .string) as? String {
self.mapView.setViewpointCenter(mapPoint)
// Hide the accessory button.
self.mapView.callout.isAccessoryButtonHidden = true
// Set the detail text.
self.mapView.callout.detail = String(format: "Crimes in the last 60 days: %@", crimeCount)
// Prompt the callout at the map point.
self.mapView.callout.show(for: feature, tapLocation: mapPoint, animated: true)
} else if let error = error {
// Present an error if needed.
self.presentAlert(error: error)
}
}
}

// MARK: UIViewController

override func viewDidLoad() {
super.viewDidLoad()
// Add the source code button item to the right of navigation bar.
(navigationItem.rightBarButtonItem as? SourceCodeBarButtonItem)?.filenames = [
"QueryFeaturesArcadeExpressionViewController"
]
}
}

// MARK: - AGSGeoViewTouchDelegate

extension QueryFeaturesArcadeExpressionViewController: AGSGeoViewTouchDelegate {
func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
// Dismiss any presenting callout.
mapView.callout.dismiss()
// Identify features at the tapped location.
mapView.identifyLayers(atScreenPoint: screenPoint, tolerance: 12, returnPopupsOnly: false) { [weak self] results, error in
guard let self = self else { return }
// Get the selected feature.
if let elements = results?.first?.geoElements, let identifiedFeature = elements.first as? AGSArcGISFeature {
// Evaluate the Arcade for the given feature.
self.evaluateArcadeInCallout(for: identifiedFeature, at: mapPoint)
} else if let error = error {
// Present an error alert if needed.
self.presentAlert(error: error)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Query features with Arcade expression

Query features on a map using an Arcade expression.

![Query features with Arcade expression](query-features-arcade-expression.png)

## Use case

Arcade is a portable, lightweight, and secure expression language used to create custom content in ArcGIS applications. Like other expression languages, it can perform mathematical calculations, manipulate text, and evaluate logical statements. It also supports multi-statement expressions, variables, and flow control statements. What makes Arcade particularly unique when compared to other expression and scripting languages is its inclusion of feature and geometry data types. This sample uses an Arcade expression to query the number of crimes in a neighborhood in the last 60 days.

## How to use the sample

Tap on any neighborhood to see the number of crimes in the last 60 days in a callout.

## How it works

1. Create an `AGSPortalItem` using the portal and ID.
2. Create an `AGSMap` using the portal item.
3. Set the `AGSGeoViewTouchDelegate` to the map.
4. Identify the layers tapped on the map view with `AGSGeoView.identifyLayers(atScreenPoint:tolerance:returnPopupsOnly:completion:)`.
5. Create the following `AGSArcadeExpression`:

```swift
expressionValue = "var crimes = FeatureSetByName($map, 'Crime in the last 60 days');\n"
"return Count(Intersects($feature, crimes));"
```

6. Create an `AGSArcadeEvaluator` using the Arcade expression and `ArcadeProfile.FORM_CALCULATION`.
7. Create a dictionary of profile variables with the following pairs:

`{"$feature", identifiedFeature}`

`{"$map", map}`

8. Use `AGSArcadeEvaluator.evaluate(withProfileVariables:completion:)` with the profile variables to evaluate the Arcade expression.
9. Convert the result to a `String` to display the crime count in a callout.

## Relevant API

* AGSArcadeEvaluationResult
* AGSArcadeEvaluator
* AGSArcadeExpression
* AGSArcadeProfile
* AGSPortal
* AGSPortalItem

## About the data

This sample uses the [Crimes in Police Beats Sample](https://www.arcgis.com/home/item.html?id=539d93de54c7422f88f69bfac2aebf7d) ArcGIS Online Web Map which contains 2 layers for city beats borders and crimes in the last 60 days as recorded by the Rochester, NY police department.

## Additional information

Visit [Getting Started](https://developers.arcgis.com/arcade/) on the *ArcGIS Developer* website to learn more about Arcade expressions.

## Tags

Arcade evaluator, Arcade expression, identify layers, portal, portal item, query
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"category": "Display information",
"description": "Query features on a map using an Arcade expression.",
"ignore": false,
"images": [
"query-features-arcade-expression.png"
],
"keywords": [
"Arcade evaluator",
"Arcade expression",
"identify layers",
"portal",
"portal item",
"query",
"AGSArcadeEvaluationResult",
"AGSArcadeEvaluator",
"AGSArcadeExpression",
"AGSArcadeProfile",
"AGSPortal",
"AGSPortalItem"
],
"redirect_from": [],
"relevant_apis": [
"AGSArcadeEvaluationResult",
"AGSArcadeEvaluator",
"AGSArcadeExpression",
"AGSArcadeProfile",
"AGSPortal",
"AGSPortalItem"
],
"snippets": [
"QueryFeaturesArcadeExpressionViewController.swift"
],
"title": "Query features with Arcade expression"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,43 @@ import UIKit
import ArcGIS

class CalloutViewController: UIViewController, AGSGeoViewTouchDelegate {
@IBOutlet private weak var mapView: AGSMapView!

private var map: AGSMap!
@IBOutlet var mapView: AGSMapView! {
didSet {
// Initialize map with topographic basemap.
mapView.map = AGSMap(basemapStyle: .arcGISTopographic)

// Set the map view's touch delegate to `self` in order to get
// tap notifications.
mapView.touchDelegate = self

// Set the map view's viewpoint.
mapView.setViewpointCenter(AGSPoint(x: -1.2e7, y: 5e6, spatialReference: .webMercator()), scale: 4e7)

// Configure the map view's callout to hide accessory button.
mapView.callout.isAccessoryButtonHidden = true
}
}

override func viewDidLoad() {
super.viewDidLoad()

// add the source code button item to the right of navigation bar
(self.navigationItem.rightBarButtonItem as! SourceCodeBarButtonItem).filenames = ["CalloutViewController"]

// initialize map with topographic basemap
self.map = AGSMap(basemapStyle: .arcGISTopographic)
// assign map to the map view
self.mapView.map = self.map
// register as the map view's touch delegate
// in order to get tap notifications
self.mapView.touchDelegate = self
// zoom to custom view point
self.mapView.setViewpointCenter(AGSPoint(x: -1.2e7, y: 5e6, spatialReference: .webMercator()), scale: 4e7)
// Add the source code button item to the right of navigation bar.
(navigationItem.rightBarButtonItem as? SourceCodeBarButtonItem)?.filenames = ["CalloutViewController"]
}

// MARK: - AGSGeoViewTouchDelegate

// user tapped on the map
func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
// if the callout is not shown, show the callout with the coordinates of the tapped location
if self.mapView.callout.isHidden {
self.mapView.callout.title = "Location"
self.mapView.callout.detail = String(format: "x: %.2f, y: %.2f", mapPoint.x, mapPoint.y)
self.mapView.callout.isAccessoryButtonHidden = true
self.mapView.callout.show(at: mapPoint, screenOffset: CGPoint.zero, rotateOffsetWithMap: false, animated: true)
} else { // hide the callout
self.mapView.callout.dismiss()
// User tapped on the map.
if mapView.callout.isHidden {
// Show the callout with the coordinates of the tapped location.
mapView.callout.title = "Location"
// Project the tapped location point to WGS 84 spatial reference.
let location = AGSGeometryEngine.projectGeometry(mapPoint, to: .wgs84()) as! AGSPoint
mapView.callout.detail = String(format: "x: %.2f, y: %.2f", location.x, location.y)
mapView.callout.show(at: mapPoint, screenOffset: .zero, rotateOffsetWithMap: false, animated: true)
} else {
// Hide the callout.
mapView.callout.dismiss()
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit f2f05af

Please sign in to comment.