@@ -17,16 +17,43 @@ import SwiftUI
1717
1818/// A view that shows how to interact with the geometry editor.
1919struct CreateAndEditGeometriesView : View {
20- /// The map to display in the view.
21- @State private var map = Map ( basemapStyle: . arcGISTopographic)
20+ /// A map with an imagery basemap.
21+ @State private var map : Map = {
22+ let map = Map ( basemapStyle: . arcGISImagery)
23+ // A viewpoint centered at the island of Inis Meáin (Aran Islands) in Ireland.
24+ map. initialViewpoint = Viewpoint (
25+ center: Point ( latitude: 53.08230 , longitude: - 9.5920 ) ,
26+ scale: 5_000
27+ )
28+ return map
29+ } ( )
2230
2331 /// The view model for this sample.
2432 @StateObject private var model = GeometryEditorModel ( )
2533
34+ /// The screen point to perform an identify operation.
35+ @State private var identifyScreenPoint : CGPoint ?
36+
2637 var body : some View {
2738 VStack {
28- MapView ( map: map, graphicsOverlays: [ model. geometryOverlay] )
29- . geometryEditor ( model. geometryEditor)
39+ MapViewReader { proxy in
40+ MapView ( map: map, graphicsOverlays: [ model. geometryOverlay] )
41+ . geometryEditor ( model. geometryEditor)
42+ . onSingleTapGesture { screenPoint, _ in
43+ identifyScreenPoint = screenPoint
44+ }
45+ . task ( id: identifyScreenPoint) {
46+ guard let identifyScreenPoint,
47+ let identifyResult = try ? await proxy. identify (
48+ on: model. geometryOverlay,
49+ screenPoint: identifyScreenPoint,
50+ tolerance: 5
51+ ) ,
52+ let graphic = identifyResult. graphics. first,
53+ !model. isStarted else { return }
54+ model. startEditing ( with: graphic)
55+ }
56+ }
3057 }
3158 . toolbar {
3259 ToolbarItem ( placement: . primaryAction) {
@@ -197,11 +224,11 @@ private extension GeometryEditorMenu {
197224 Divider ( )
198225
199226 Button ( role: . destructive) {
200- model. clearSavedSketches ( )
227+ model. deleteAllGeometries ( )
201228 } label: {
202- Label ( " Clear Saved Sketches " , systemImage: " trash " )
229+ Label ( " Delete All Geometries " , systemImage: " trash " )
203230 }
204- . disabled ( !model. canClearSavedSketches )
231+ . disabled ( !model. canClearGraphics )
205232 }
206233 }
207234
@@ -296,8 +323,8 @@ private class GeometryEditorModel: ObservableObject {
296323 /// The graphics overlay used to save geometries to.
297324 let geometryOverlay = GraphicsOverlay ( renderingMode: . dynamic)
298325
299- /// A Boolean value indicating if the saved sketches can be cleared.
300- @Published private( set) var canClearSavedSketches = false
326+ /// A Boolean value indicating if the initial graphics and saved sketches can be cleared.
327+ @Published private( set) var canClearGraphics = false
301328
302329 /// A Boolean value indicating if the geometry editor has started.
303330 @Published private( set) var isStarted = false
@@ -314,43 +341,89 @@ private class GeometryEditorModel: ObservableObject {
314341 isUniformScale ? . uniform : . stretch
315342 }
316343
344+ /// The selected graphic to edit.
345+ private var selectedGraphic : Graphic ?
346+
347+ init ( ) {
348+ let boundaryGraphic = Graphic ( geometry: . boundary( ) , symbol: . polygon)
349+
350+ let road1Graphic = Graphic ( geometry: . road1( ) , symbol: . polyline)
351+
352+ let road2Graphic = Graphic ( geometry: . road2( ) , symbol: . polyline)
353+
354+ let outbuildingsGraphic = Graphic ( geometry: . outbuildings( ) , symbol: . multipoint)
355+
356+ let houseGraphic = Graphic ( geometry: . house( ) , symbol: . point)
357+
358+ geometryOverlay. addGraphics ( [
359+ boundaryGraphic,
360+ road1Graphic,
361+ road2Graphic,
362+ outbuildingsGraphic,
363+ houseGraphic
364+ ] )
365+
366+ canClearGraphics = true
367+ }
368+
317369 /// Saves the current geometry to the graphics overlay and stops editing.
318370 /// - Precondition: Geometry's sketch must be valid.
319371 func save( ) {
320372 precondition ( geometryEditor. geometry? . sketchIsValid ?? false )
373+
374+ if selectedGraphic != nil {
375+ // Update geometry for edited graphic.
376+ updateGraphic ( )
377+ } else {
378+ // Add new graphic.
379+ addGraphic ( )
380+ }
381+ }
382+
383+ /// Updates the selected graphic with the current geometry.
384+ private func updateGraphic( ) {
385+ guard let selectedGraphic else { return }
386+ selectedGraphic. geometry = geometryEditor. stop ( )
387+ isStarted = false
388+ selectedGraphic. isVisible = true
389+ self . selectedGraphic = nil
390+ }
391+
392+ /// Adds a new graphic for the current geometry to the graphics overlay.
393+ private func addGraphic( ) {
321394 let geometry = geometryEditor. geometry!
322395 let graphic = Graphic ( geometry: geometry, symbol: symbol ( for: geometry) )
323396 geometryOverlay. addGraphic ( graphic)
324397 stop ( )
325- canClearSavedSketches = true
398+ canClearGraphics = true
326399 }
327400
328- /// Clears all the saved sketches on the graphics overlay.
329- func clearSavedSketches ( ) {
401+ /// Removes the initial graphics and saved sketches on the graphics overlay.
402+ func deleteAllGeometries ( ) {
330403 geometryOverlay. removeAllGraphics ( )
331- canClearSavedSketches = false
404+ canClearGraphics = false
332405 }
333406
334407 /// Stops editing with the geometry editor.
335408 func stop( ) {
336409 geometryEditor. stop ( )
337410 isStarted = false
411+ selectedGraphic? . isVisible = true
338412 }
339413
340414 /// Returns the symbology for graphics saved to the graphics overlay.
341415 /// - Parameter geometry: The geometry of the graphic to be saved.
342416 /// - Returns: Either a marker or fill symbol depending on the type of provided geometry.
343417 private func symbol( for geometry: Geometry ) -> Symbol {
344418 switch geometry {
345- case is Point , is Multipoint :
346- return SimpleMarkerSymbol ( style: . circle, color: . blue, size: 20 )
419+ case is Point :
420+ return . point
421+ case is Multipoint :
422+ return . multipoint
347423 case is Polyline :
348- return SimpleLineSymbol ( color : . blue , width : 2 )
424+ return . polyline
349425 case is ArcGIS . Polygon :
350- return SimpleFillSymbol (
351- color: . gray. withAlphaComponent ( 0.5 ) ,
352- outline: SimpleLineSymbol ( color: . blue, width: 2 )
353- )
426+ return . polygon
354427 default :
355428 fatalError ( " Unexpected geometry type " )
356429 }
@@ -385,6 +458,122 @@ private class GeometryEditorModel: ObservableObject {
385458 geometryEditor. start ( withType: geometryType)
386459 isStarted = true
387460 }
461+
462+ /// Starts editing a given graphic with the geometry editor.
463+ /// - Parameter graphic: The graphic to edit.
464+ func startEditing( with graphic: Graphic ) {
465+ selectedGraphic = graphic
466+ graphic. isVisible = false
467+ let geometry = graphic. geometry!
468+ geometryEditor. start ( withInitial: geometry)
469+ isStarted = true
470+ }
471+ }
472+
473+ private extension Geometry {
474+ // swiftlint:disable force_try
475+ static func house( ) -> Point {
476+ let jsonStr = """
477+ { " x " :-1067898.59,
478+ " y " :6998366.62,
479+ " spatialReference " :{ " latestWkid " :3857, " wkid " :102100}}
480+ """
481+ return try ! Point . fromJSON ( jsonStr)
482+ }
483+
484+ static func road1( ) -> Polyline {
485+ let jsonStr = """
486+ { " paths " :[[[-1068095.40,6998123.52],[-1068086.16,6998134.60],
487+ [-1068083.20,6998160.44],[-1068104.27,6998205.37],
488+ [-1068070.63,6998255.22],[-1068014.44,6998291.54],
489+ [-1067952.33,6998351.85],[-1067927.93,6998386.93],
490+ [-1067907.97,6998396.78],[-1067889.86,6998406.63],
491+ [-1067848.08,6998495.26],[-1067832.92,6998521.11]]],
492+ " spatialReference " :{ " latestWkid " :3857, " wkid " :102100}}
493+ """
494+ return try ! Polyline . fromJSON ( jsonStr)
495+ }
496+
497+ static func road2( ) -> Polyline {
498+ let jsonStr = """
499+ { " paths " :[[[-1067999.28,6998061.97],[-1067994.48,6998086.59],
500+ [-1067964.53,6998125.37],[-1067952.70,6998215.84],
501+ [-1067923.13,6998347.54],[-1067903.90,6998391.86],
502+ [-1067895.40,6998422.02],[-1067891.70,6998460.18],
503+ [-1067889.49,6998483.56],[-1067880.98,6998527.26]]],
504+ " spatialReference " :{ " latestWkid " :3857, " wkid " :102100}}
505+ """
506+ return try ! Polyline . fromJSON ( jsonStr)
507+ }
508+
509+ static func outbuildings( ) -> Multipoint {
510+ let jsonStr = """
511+ { " points " :[[-1067984.26,6998346.28],[-1067966.80,6998244.84],
512+ [-1067921.88,6998284.65],[-1067934.36,6998340.74],
513+ [-1067917.93,6998373.97],[-1067828.30,6998355.28],
514+ [-1067832.25,6998339.70],[-1067823.10,6998336.93],
515+ [-1067873.22,6998386.78],[-1067896.72,6998244.49]],
516+ " spatialReference " :{ " latestWkid " :3857, " wkid " :102100}}
517+ """
518+ return try ! Multipoint . fromJSON ( jsonStr)
519+ }
520+
521+ static func boundary( ) -> Polygon {
522+ let jsonStr = """
523+ { " rings " :[[[-1067943.67,6998403.86],[-1067938.17,6998427.60],
524+ [-1067898.77,6998415.86],[-1067888.26,6998398.80],
525+ [-1067800.85,6998372.93],[-1067799.61,6998342.81],
526+ [-1067809.38,6998330.00],[-1067817.07,6998307.85],
527+ [-1067838.07,6998285.34],[-1067849.10,6998250.38],
528+ [-1067874.02,6998256.00],[-1067879.87,6998235.95],
529+ [-1067913.41,6998245.03],[-1067934.84,6998291.34],
530+ [-1067948.41,6998251.90],[-1067961.18,6998186.68],
531+ [-1068008.59,6998199.49],[-1068052.89,6998225.45],
532+ [-1068039.37,6998261.11],[-1068064.12,6998265.26],
533+ [-1068043.32,6998299.88],[-1068036.25,6998327.93],
534+ [-1068004.43,6998409.28],[-1067943.67,6998403.86]]],
535+ " spatialReference " :{ " latestWkid " :3857, " wkid " :102100}}
536+ """
537+ return try ! Polygon . fromJSON ( jsonStr)
538+ }
539+ // swiftlint:enable force_try
540+ }
541+
542+ private extension Symbol {
543+ static var point : SimpleMarkerSymbol {
544+ SimpleMarkerSymbol (
545+ style: . square,
546+ color: . red,
547+ size: 10
548+ )
549+ }
550+
551+ static var multipoint : SimpleMarkerSymbol {
552+ SimpleMarkerSymbol (
553+ style: . circle,
554+ color: . yellow,
555+ size: 5
556+ )
557+ }
558+
559+ static var polyline : SimpleLineSymbol {
560+ SimpleLineSymbol (
561+ color: . blue,
562+ width: 2
563+ )
564+ }
565+
566+ static var polygon : SimpleFillSymbol {
567+ SimpleFillSymbol (
568+ style: . solid,
569+ color: . red. withAlphaComponent ( 0.3 ) ,
570+ outline: SimpleLineSymbol (
571+ style: . dash,
572+ color: . black,
573+ width: 1
574+ )
575+ )
576+ }
388577}
389578
390579#Preview {
0 commit comments