Skip to content

Commit 0390d7a

Browse files
authored
Merge pull request #293 from Esri/Caleb/New-FindRouteInMobileMapPackage
[New] Find route in mobile map package
2 parents 382c8c9 + 3da9b57 commit 0390d7a

File tree

8 files changed

+693
-0
lines changed

8 files changed

+693
-0
lines changed

Samples.xcodeproj/project.pbxproj

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@
187187
D7337C602ABD142D00A5D865 /* ShowMobileMapPackageExpirationDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7337C5F2ABD142D00A5D865 /* ShowMobileMapPackageExpirationDateView.swift */; };
188188
D7337C612ABD166A00A5D865 /* ShowMobileMapPackageExpirationDateView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7337C5F2ABD142D00A5D865 /* ShowMobileMapPackageExpirationDateView.swift */; };
189189
D734FA0C2A183A5B00246D7E /* SetMaxExtentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D734FA092A183A5B00246D7E /* SetMaxExtentView.swift */; };
190+
D73723762AF5877500846884 /* FindRouteInMobileMapPackageView.Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73723742AF5877500846884 /* FindRouteInMobileMapPackageView.Models.swift */; };
191+
D73723792AF5ADD800846884 /* FindRouteInMobileMapPackageView.MobileMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73723782AF5ADD700846884 /* FindRouteInMobileMapPackageView.MobileMapView.swift */; };
192+
D737237A2AF5AE1600846884 /* FindRouteInMobileMapPackageView.MobileMapView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D73723782AF5ADD700846884 /* FindRouteInMobileMapPackageView.MobileMapView.swift */; };
193+
D737237B2AF5AE1A00846884 /* FindRouteInMobileMapPackageView.Models.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D73723742AF5877500846884 /* FindRouteInMobileMapPackageView.Models.swift */; };
190194
D73F8CF42AB1089900CD39DA /* Restaurant.stylx in Resources */ = {isa = PBXBuildFile; fileRef = D73F8CF32AB1089900CD39DA /* Restaurant.stylx */; settings = {ASSET_TAGS = (StyleFeaturesWithCustomDictionary, ); }; };
191195
D73FC0FD2AD4A18D0067A19B /* CreateMobileGeodatabaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73FC0FC2AD4A18D0067A19B /* CreateMobileGeodatabaseView.swift */; };
192196
D73FC0FE2AD4A19A0067A19B /* CreateMobileGeodatabaseView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D73FC0FC2AD4A18D0067A19B /* CreateMobileGeodatabaseView.swift */; };
@@ -223,6 +227,9 @@
223227
D75B58522AAFB37C0038B3B4 /* StyleFeaturesWithCustomDictionaryView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D75B58502AAFB3030038B3B4 /* StyleFeaturesWithCustomDictionaryView.swift */; };
224228
D75C35672AB50338003CD55F /* GroupLayersTogetherView.GroupLayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75C35662AB50338003CD55F /* GroupLayersTogetherView.GroupLayerListView.swift */; };
225229
D76000A22AF18BAB00B3084D /* FindRouteInTransportNetworkView.Model.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7749AD52AF08BF50086632F /* FindRouteInTransportNetworkView.Model.swift */; };
230+
D76000AE2AF19C2300B3084D /* FindRouteInMobileMapPackageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76000AB2AF19C2300B3084D /* FindRouteInMobileMapPackageView.swift */; };
231+
D76000B12AF19C4600B3084D /* FindRouteInMobileMapPackageView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D76000AB2AF19C2300B3084D /* FindRouteInMobileMapPackageView.swift */; };
232+
D76000B72AF19FCA00B3084D /* SanFrancisco.mmpk in Resources */ = {isa = PBXBuildFile; fileRef = D76000B62AF19FCA00B3084D /* SanFrancisco.mmpk */; settings = {ASSET_TAGS = (FindRouteInMobileMapPackage, ); }; };
226233
D7634FAF2A43B7AC00F8AEFB /* CreateConvexHullAroundGeometriesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7634FAE2A43B7AC00F8AEFB /* CreateConvexHullAroundGeometriesView.swift */; };
227234
D7634FB02A43B8B000F8AEFB /* CreateConvexHullAroundGeometriesView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7634FAE2A43B7AC00F8AEFB /* CreateConvexHullAroundGeometriesView.swift */; };
228235
D769C2122A29019B00030F61 /* SetUpLocationDrivenGeotriggersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D769C2112A29019B00030F61 /* SetUpLocationDrivenGeotriggersView.swift */; };
@@ -367,6 +374,9 @@
367374
dstPath = "";
368375
dstSubfolderSpec = 7;
369376
files = (
377+
D737237B2AF5AE1A00846884 /* FindRouteInMobileMapPackageView.Models.swift in Copy Source Code Files */,
378+
D737237A2AF5AE1600846884 /* FindRouteInMobileMapPackageView.MobileMapView.swift in Copy Source Code Files */,
379+
D76000B12AF19C4600B3084D /* FindRouteInMobileMapPackageView.swift in Copy Source Code Files */,
370380
D7705D662AFC575000CC0335 /* FindClosestFacilityFromPointView.swift in Copy Source Code Files */,
371381
D73FD0002B02C9610006360D /* FindRouteAroundBarriersView.Views.swift in Copy Source Code Files */,
372382
D76EE6082AF9AFEC00DA0325 /* FindRouteAroundBarriersView.Model.swift in Copy Source Code Files */,
@@ -604,6 +614,8 @@
604614
D7337C592ABCFDB100A5D865 /* StyleSymbolsFromMobileStyleFileView.SymbolOptionsListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyleSymbolsFromMobileStyleFileView.SymbolOptionsListView.swift; sourceTree = "<group>"; };
605615
D7337C5F2ABD142D00A5D865 /* ShowMobileMapPackageExpirationDateView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShowMobileMapPackageExpirationDateView.swift; sourceTree = "<group>"; };
606616
D734FA092A183A5B00246D7E /* SetMaxExtentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetMaxExtentView.swift; sourceTree = "<group>"; };
617+
D73723742AF5877500846884 /* FindRouteInMobileMapPackageView.Models.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindRouteInMobileMapPackageView.Models.swift; sourceTree = "<group>"; };
618+
D73723782AF5ADD700846884 /* FindRouteInMobileMapPackageView.MobileMapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindRouteInMobileMapPackageView.MobileMapView.swift; sourceTree = "<group>"; };
607619
D73F8CF32AB1089900CD39DA /* Restaurant.stylx */ = {isa = PBXFileReference; lastKnownFileType = file; path = Restaurant.stylx; sourceTree = "<group>"; };
608620
D73FC0FC2AD4A18D0067A19B /* CreateMobileGeodatabaseView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateMobileGeodatabaseView.swift; sourceTree = "<group>"; };
609621
D73FCFFE2B02C7630006360D /* FindRouteAroundBarriersView.Views.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindRouteAroundBarriersView.Views.swift; sourceTree = "<group>"; };
@@ -624,6 +636,8 @@
624636
D7553CD82AE2DFEC00DC2A70 /* GeocodeOfflineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeocodeOfflineView.swift; sourceTree = "<group>"; };
625637
D75B58502AAFB3030038B3B4 /* StyleFeaturesWithCustomDictionaryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyleFeaturesWithCustomDictionaryView.swift; sourceTree = "<group>"; };
626638
D75C35662AB50338003CD55F /* GroupLayersTogetherView.GroupLayerListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupLayersTogetherView.GroupLayerListView.swift; sourceTree = "<group>"; };
639+
D76000AB2AF19C2300B3084D /* FindRouteInMobileMapPackageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindRouteInMobileMapPackageView.swift; sourceTree = "<group>"; };
640+
D76000B62AF19FCA00B3084D /* SanFrancisco.mmpk */ = {isa = PBXFileReference; lastKnownFileType = file; path = SanFrancisco.mmpk; sourceTree = "<group>"; };
627641
D7634FAE2A43B7AC00F8AEFB /* CreateConvexHullAroundGeometriesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateConvexHullAroundGeometriesView.swift; sourceTree = "<group>"; };
628642
D769C2112A29019B00030F61 /* SetUpLocationDrivenGeotriggersView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetUpLocationDrivenGeotriggersView.swift; sourceTree = "<group>"; };
629643
D76EE6062AF9AFE100DA0325 /* FindRouteAroundBarriersView.Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindRouteAroundBarriersView.Model.swift; sourceTree = "<group>"; };
@@ -819,6 +833,7 @@
819833
D78666A92A21616D00C60110 /* Find nearest vertex */,
820834
E066DD33285CF3A0004D3D5B /* Find route */,
821835
D7DDF84F2AF47C6C004352D9 /* Find route around barriers */,
836+
D76000AA2AF19C2300B3084D /* Find route in mobile map package */,
822837
D7E7D0792AEB39BF003AAD02 /* Find route in transport network */,
823838
E088E1722863B5E600413100 /* Generate offline map */,
824839
D7553CD62AE2DFEC00DC2A70 /* Geocode offline */,
@@ -959,6 +974,7 @@
959974
00D4EF8328638BF100B9CC30 /* 15a7cbd3af1e47cfa5d2c6b93dc44fc2 */,
960975
D7CE9F992AE2F575008F7A5F /* 22c3083d4fa74e3e9b25adfc9f8c0496 */,
961976
00D4EF8E28638BF100B9CC30 /* 68ec42517cdd439e81b036210483e8e7 */,
977+
D76000B52AF19FC900B3084D /* 260eb6535c824209964cf281766ebe43 */,
962978
D7C16D202AC5FE9800689E89 /* 290f0c571c394461a8b58b6775d0bd63 */,
963979
1C965C4629DBA879002F8536 /* 681d6f7694644709a7c830ec57a2d72b */,
964980
D7CE9F9C2AE2F585008F7A5F /* 3424d442ebe54f3cbf34462382d3aebe */,
@@ -1508,6 +1524,24 @@
15081524
path = "Style features with custom dictionary";
15091525
sourceTree = "<group>";
15101526
};
1527+
D76000AA2AF19C2300B3084D /* Find route in mobile map package */ = {
1528+
isa = PBXGroup;
1529+
children = (
1530+
D73723782AF5ADD700846884 /* FindRouteInMobileMapPackageView.MobileMapView.swift */,
1531+
D73723742AF5877500846884 /* FindRouteInMobileMapPackageView.Models.swift */,
1532+
D76000AB2AF19C2300B3084D /* FindRouteInMobileMapPackageView.swift */,
1533+
);
1534+
path = "Find route in mobile map package";
1535+
sourceTree = "<group>";
1536+
};
1537+
D76000B52AF19FC900B3084D /* 260eb6535c824209964cf281766ebe43 */ = {
1538+
isa = PBXGroup;
1539+
children = (
1540+
D76000B62AF19FCA00B3084D /* SanFrancisco.mmpk */,
1541+
);
1542+
path = 260eb6535c824209964cf281766ebe43;
1543+
sourceTree = "<group>";
1544+
};
15111545
D769C20D2A28FF8600030F61 /* Set up location-driven geotriggers */ = {
15121546
isa = PBXGroup;
15131547
children = (
@@ -1974,6 +2008,7 @@
19742008
ChangeCameraController,
19752009
DisplayDimensions,
19762010
DisplayMapFromMobileMapPackage,
2011+
FindRouteInMobileMapPackage,
19772012
FindRouteInTransportNetwork,
19782013
GeocodeOffline,
19792014
IdentifyRasterCell,
@@ -2033,6 +2068,7 @@
20332068
D73F8CF42AB1089900CD39DA /* Restaurant.stylx in Resources */,
20342069
D74C8C022ABA6202007C76B8 /* emoji-mobile.stylx in Resources */,
20352070
D7CE9FA32AE2F595008F7A5F /* san-diego-eagle-locator in Resources */,
2071+
D76000B72AF19FCA00B3084D /* SanFrancisco.mmpk in Resources */,
20362072
792222DD2A81AA5D00619FFE /* AIS_MarineCadastre_SelectedVessels_CustomDataSource.jsonl in Resources */,
20372073
E041AC20288077B90056009B /* xcode.css in Resources */,
20382074
00D4EF9028638BF100B9CC30 /* LA_Trails.geodatabase in Resources */,
@@ -2153,6 +2189,7 @@
21532189
1C3B7DC82A5F64FC00907443 /* AnalyzeNetworkWithSubnetworkTraceView.Model.swift in Sources */,
21542190
D752D9402A39154C003EB25E /* ManageOperationalLayersView.swift in Sources */,
21552191
D7ABA2F92A32579C0021822B /* MeasureDistanceInSceneView.swift in Sources */,
2192+
D73723792AF5ADD800846884 /* FindRouteInMobileMapPackageView.MobileMapView.swift in Sources */,
21562193
E004A6E028466279002A1FE6 /* ShowCalloutView.swift in Sources */,
21572194
E000E763286A0B18005D87C5 /* CutGeometryView.swift in Sources */,
21582195
D7705D582AFC244E00CC0335 /* FindClosestFacilityToMultiplePointsView.swift in Sources */,
@@ -2270,10 +2307,12 @@
22702307
0042E24528E4F82C001F33D6 /* ShowViewshedFromPointInSceneView.ViewshedSettingsView.swift in Sources */,
22712308
D7DDF8532AF47C6C004352D9 /* FindRouteAroundBarriersView.swift in Sources */,
22722309
1C9B74D929DB54560038B06F /* ChangeCameraControllerView.swift in Sources */,
2310+
D76000AE2AF19C2300B3084D /* FindRouteInMobileMapPackageView.swift in Sources */,
22732311
00273CF42A82AB5900A7A77D /* SamplesSearchView.swift in Sources */,
22742312
D78666AD2A2161F100C60110 /* FindNearestVertexView.swift in Sources */,
22752313
D7C16D1B2AC5F95300689E89 /* Animate3DGraphicView.swift in Sources */,
22762314
D744FD172A2112D90084A66C /* CreateConvexHullAroundPointsView.swift in Sources */,
2315+
D73723762AF5877500846884 /* FindRouteInMobileMapPackageView.Models.swift in Sources */,
22772316
00CB9138284814A4005C2C5D /* SearchWithGeocodeView.swift in Sources */,
22782317
1C43BC7F2A43781200509BF8 /* SetVisibilityOfSubtypeSublayerView.Views.swift in Sources */,
22792318
D754E3232A1D66820006C5F1 /* StylePointWithPictureMarkerSymbolsView.swift in Sources */,
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Copyright 2023 Esri
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import ArcGIS
16+
import SwiftUI
17+
18+
extension FindRouteInMobileMapPackageView {
19+
/// The map view for a mobile map package map.
20+
struct MobileMapView: View {
21+
/// The view model for the view.
22+
@StateObject private var model: Model
23+
24+
/// The placement of the address callout on the map.
25+
@State private var calloutPlacement: CalloutPlacement?
26+
27+
/// The text address shown in the callout.
28+
@State private var calloutText: String = ""
29+
30+
/// The point on the screen where the user tapped.
31+
@State private var tapScreenPoint: CGPoint?
32+
33+
/// A Boolean value indicating whether the reset button is disabled.
34+
@State private var resetDisabled = true
35+
36+
/// A Boolean value indicating whether the error alert is showing.
37+
@State private var errorAlertIsShowing = false
38+
39+
/// The error shown in the error alert.
40+
@State private var error: Error? {
41+
didSet { errorAlertIsShowing = error != nil }
42+
}
43+
44+
init(map: Map, locatorTask: LocatorTask) {
45+
let model = Model(map: map, locatorTask: locatorTask)
46+
_model = StateObject(wrappedValue: model)
47+
}
48+
49+
var body: some View {
50+
MapViewReader { mapViewProxy in
51+
MapView(map: model.map, graphicsOverlays: model.graphicsOverlays)
52+
.callout(placement: $calloutPlacement) { _ in
53+
Text(calloutText)
54+
.font(.callout)
55+
.padding(8)
56+
}
57+
.onSingleTapGesture { screenPoint, _ in
58+
tapScreenPoint = screenPoint
59+
}
60+
.task(id: tapScreenPoint) {
61+
guard let tapScreenPoint else { return }
62+
63+
do {
64+
// Check to see if the tap was on a marker.
65+
let identifyResult = try await mapViewProxy.identify(
66+
on: model.markerGraphicsOverlay,
67+
screenPoint: tapScreenPoint,
68+
tolerance: 12
69+
)
70+
71+
if let graphic = identifyResult.graphics.first,
72+
let graphicPoint = graphic.geometry as? Point {
73+
// Update the callout to the identified marker.
74+
await updateCallout(point: graphicPoint, graphic: graphic)
75+
} else {
76+
// Add a graphic at the tapped map point.
77+
guard let location = mapViewProxy.location(
78+
fromScreenPoint: tapScreenPoint
79+
) else { return }
80+
await addGraphic(at: location)
81+
}
82+
} catch {
83+
self.error = error
84+
}
85+
}
86+
}
87+
.toolbar {
88+
ToolbarItemGroup(placement: .bottomBar) {
89+
Spacer()
90+
Button("Reset") {
91+
resetGraphics()
92+
}
93+
.disabled(resetDisabled)
94+
}
95+
}
96+
.task {
97+
// Load the route parameters sample loads.
98+
do {
99+
try await model.loadRouteParameters()
100+
} catch {
101+
self.error = error
102+
}
103+
}
104+
.alert(isPresented: $errorAlertIsShowing, presentingError: error)
105+
}
106+
107+
/// Updates the placement and text of the callout using a given point and graphic.
108+
/// - Parameters:
109+
/// - point: The point to reverse geocode and set the callout placement to.
110+
/// - graphic: The graphic at the point.
111+
private func updateCallout(point: Point, graphic: Graphic) async {
112+
// Update the callout text with the address from a reverse geocode.
113+
do {
114+
calloutText = try await model.reverseGeocode(point: point)
115+
} catch {
116+
self.error = error
117+
calloutText = "No address found"
118+
}
119+
120+
// Update the callout placement with the graphic and point.
121+
calloutPlacement = .geoElement(graphic, tapLocation: point)
122+
}
123+
124+
/// Adds a marker or route stop with a callout at a given point.
125+
/// - Parameter point: The point to add the graphic at.
126+
private func addGraphic(at point: Point) async {
127+
// Normalize the tap location.
128+
guard let point = GeometryEngine.normalizeCentralMeridian(of: point) as? Point else { return }
129+
130+
// Add a route stop if the map has routing. Otherwise, update the marker.
131+
if model.routeTask != nil {
132+
do {
133+
try await model.addRouteStop(at: point)
134+
} catch {
135+
self.error = error
136+
}
137+
} else {
138+
model.updateMarker(to: point)
139+
}
140+
141+
// Update the callout with the last marker.
142+
guard let lastMarker = model.lastMarker else { return }
143+
await updateCallout(point: point, graphic: lastMarker)
144+
145+
resetDisabled = false
146+
}
147+
148+
/// Resets the graphics on the map view.
149+
private func resetGraphics() {
150+
// Reset the view properties.
151+
calloutPlacement = nil
152+
resetDisabled = true
153+
154+
// Reset the graphics.
155+
if model.routeTask != nil {
156+
model.markerGraphicsOverlay.removeAllGraphics()
157+
model.routeGraphicsOverlay?.removeAllGraphics()
158+
} else {
159+
model.lastMarker?.geometry = nil
160+
}
161+
}
162+
}
163+
}

0 commit comments

Comments
 (0)