Manage operational layers

View inAndroidFormsUWPWPFWinUIiOSView on GitHub

Add, remove, and reorder operational layers in a map.

Image of manage operational layers

Use case

Operational layers display the primary content of the map and usually provide dynamic content for the user to interact with (as opposed to basemap layers that provide context).

The order of operational layers in a map determines the visual hierarchy of layers in the view. You can bring attention to a specific layer by rendering above other layers.

How to use the sample

When the app starts, a list displays the operational layers that are currently displayed in the map. Right-tap on the list item to remove the layer, or left-tap to move it to the top. The map will be updated automatically.

The second list shows layers that have been removed from the map. Tap one to add it to the map.

How it works

  1. Get the operational layers from the map using map.OperationalLayers.
  2. Add or remove layers using layerList.Add(layer) and layerList.Remove(layer) respectively. The last layer in the list will be rendered on top.

Relevant API

  • ArcGISMapImageLayer
  • Map
  • MapView
  • MapView.OperationalLayers

Additional information

You cannot add the same layer to the map multiple times or add the same layer to multiple maps. Instead, create a new layer using the FeatureTable.

Tags

add, delete, layer, map, remove

Sample Code

ManageOperationalLayers.cs
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
// Copyright 2019 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: http://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.

using ArcGISRuntime;
using CoreGraphics;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.UI.Controls;
using Foundation;
using System;
using UIKit;

namespace ArcGISRuntimeXamarin.Samples.ManageOperationalLayers
{
    [Register("ManageOperationalLayers")]
    [ArcGISRuntime.Samples.Shared.Attributes.Sample(
        name: "Manage operational layers",
        category: "Map",
        description: "Add, remove, and reorder operational layers in a map.",
        instructions: "When the app starts, a list displays the operational layers that are currently displayed in the map. Right-tap on the list item to remove the layer, or left-tap to move it to the top. The map will be updated automatically.",
        tags: new[] { "add", "delete", "layer", "map", "remove" })]
    public class ManageOperationalLayers : UIViewController
    {
        // Hold references to UI controls.
        private MapView _myMapView;
        private UITableViewController _tableController;
        private MapViewModel _viewModel;
        private UIBarButtonItem _manageLayersButton;

        // Some URLs of layers to add to the map.
        private readonly string[] _layerUrls = new[]
        {
            "https://sampleserver5.arcgisonline.com/arcgis/rest/services/Elevation/WorldElevations/MapServer",
            "https://sampleserver5.arcgisonline.com/arcgis/rest/services/Census/MapServer",
            "https://sampleserver5.arcgisonline.com/arcgis/rest/services/DamageAssessment/MapServer"
        };

        public ManageOperationalLayers()
        {
            Title = "Manage operational layers";
        }

        private void Initialize()
        {
            _viewModel = new MapViewModel(new Map(BasemapStyle.ArcGISStreets));
            _myMapView.Map = _viewModel.Map;

            // Add the layers.
            foreach (string layerUrl in _layerUrls)
            {
                _viewModel.AddLayerFromUrl(layerUrl);
            }

            // Create the table view controller.
            _tableController = new UITableViewController(UITableViewStyle.Plain);

            // The table view content is managed by the view model.
            _tableController.TableView.Source = _viewModel;
        }

        private void ManageLayers_Clicked(object sender, EventArgs e)
        {
            // Show the layer list popover. Note: most behavior is managed by the table view & its source. See MapViewModel.
            var controller = new UINavigationController(_tableController);
            // Show an edit button in the top left of the popover.
            controller.NavigationBar.Items[0].SetLeftBarButtonItem(_tableController.EditButtonItem, false);
            // Show a close button in the top right.
            var closeButton = new UIBarButtonItem("Close", UIBarButtonItemStyle.Plain, (o, ea) => controller.DismissViewController(true, null));
            controller.NavigationBar.Items[0].SetRightBarButtonItem(closeButton, false);
            // Show the table view in a popover.
            controller.ModalPresentationStyle = UIModalPresentationStyle.Popover;
            controller.PreferredContentSize = new CGSize(300, 250);
            UIPopoverPresentationController pc = controller.PopoverPresentationController;
            if (pc != null)
            {
                pc.BarButtonItem = (UIBarButtonItem) sender;
                pc.PermittedArrowDirections = UIPopoverArrowDirection.Down;
                pc.Delegate = new ppDelegate();
            }

            PresentViewController(controller, true, null);
        }

        // Force popover to display on iPhone.
        private class ppDelegate : UIPopoverPresentationControllerDelegate
        {
            public override UIModalPresentationStyle GetAdaptivePresentationStyle(
                UIPresentationController forPresentationController) => UIModalPresentationStyle.None;

            public override UIModalPresentationStyle GetAdaptivePresentationStyle(UIPresentationController controller,
                UITraitCollection traitCollection) => UIModalPresentationStyle.None;
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            Initialize();
        }

        public override void LoadView()
        {
            // Create the views.
            View = new UIView {BackgroundColor = ApplicationTheme.BackgroundColor};

            _myMapView = new MapView();
            _myMapView.TranslatesAutoresizingMaskIntoConstraints = false;

            _manageLayersButton = new UIBarButtonItem();
            _manageLayersButton.Title = "Manage layers";

            UIToolbar toolbar = new UIToolbar();
            toolbar.TranslatesAutoresizingMaskIntoConstraints = false;
            toolbar.Items = new[]
            {
                new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace),
                _manageLayersButton
            };

            // Add the views.
            View.AddSubviews(_myMapView, toolbar);

            // Lay out the views.
            NSLayoutConstraint.ActivateConstraints(new NSLayoutConstraint[]
            {
                _myMapView.TopAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.TopAnchor),
                _myMapView.LeadingAnchor.ConstraintEqualTo(View.LeadingAnchor),
                _myMapView.TrailingAnchor.ConstraintEqualTo(View.TrailingAnchor),
                _myMapView.BottomAnchor.ConstraintEqualTo(toolbar.TopAnchor),
                toolbar.LeadingAnchor.ConstraintEqualTo(View.LeadingAnchor),
                toolbar.TrailingAnchor.ConstraintEqualTo(View.TrailingAnchor),
                toolbar.BottomAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.BottomAnchor)
            });
        }

        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);

            // Subscribe to events.
            _manageLayersButton.Clicked += ManageLayers_Clicked;
        }

        public override void ViewDidDisappear(bool animated)
        {
            base.ViewDidDisappear(animated);

            // Unsubscribe from events, per best practice.
            _manageLayersButton.Clicked -= ManageLayers_Clicked;
        }
    }

    class MapViewModel : UITableViewSource
    {
        public Map Map { get; }
        public LayerCollection IncludedLayers => Map.OperationalLayers;
        public LayerCollection ExcludedLayers { get; } = new LayerCollection();
        private const string CellIdentifier = "LayerTableCell";

        public MapViewModel(Map map)
        {
            Map = map;
        }

        public void AddLayerFromUrl(string layerUrl)
        {
            ArcGISMapImageLayer layer = new ArcGISMapImageLayer(new Uri(layerUrl));
            Map.OperationalLayers.Add(layer);
        }

        public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
        {
            // Gets a cell for the specified section and row within that section.
            var cell = new UITableViewCell(UITableViewCellStyle.Default, CellIdentifier);
            // The first section is Map.OperationalLayers, the other section is excluded layers.
            switch (indexPath.Section)
            {
                case 0:
                    cell.TextLabel.Text = IncludedLayers[indexPath.Row].Name;
                    break;
                case 1:
                    cell.TextLabel.Text = ExcludedLayers[indexPath.Row].Name;
                    break;
            }

            return cell;
        }

        public override nint RowsInSection(UITableView tableview, nint section)
        {
            // Get the number of layers in each section - sections are how the two lists are represented.
            switch (section)
            {
                case 0:
                    return IncludedLayers.Count;
                case 1:
                    return ExcludedLayers.Count;
                default:
                    return 0;
            }
        }

        public override nint NumberOfSections(UITableView tableView)
        {
            // Two sections - layers in the map and layers not in the map.
            return 2;
        }

        public override string TitleForHeader(UITableView tableView, nint section)
        {
            // The first section is for included layers, the second section is excluded layers.
            return section == 0 ? "Layers in map" : "Layers not in map";
        }

        public override bool CanEditRow(UITableView tableView, NSIndexPath indexPath)
        {
            // All rows are editable - they can be reordered or moved to another list via insertion/deletion.
            return true;
        }

        public override string TitleForDeleteConfirmation(UITableView tableView, NSIndexPath indexPath)
        {
            // The message shown when you drag to the side when editing.
            switch (indexPath.Section)
            {
                case 0:
                    return "Remove from map";
                case 1:
                    return "Add to map";
            }

            return "This shouldn't happen.";
        }

        public override void CommitEditingStyle(UITableView tableView, UITableViewCellEditingStyle editingStyle, NSIndexPath indexPath)
        {
            // Commit editing - in this case, editing means insertion or deletion.

            Layer selectedLayer;
            switch (indexPath.Section)
            {
                case 0:
                    selectedLayer = IncludedLayers[indexPath.Row];
                    IncludedLayers.Remove(selectedLayer);
                    ExcludedLayers.Add(selectedLayer);
                    break;
                case 1:
                    selectedLayer = ExcludedLayers[indexPath.Row];
                    ExcludedLayers.Remove(selectedLayer);
                    IncludedLayers.Add(selectedLayer);
                    break;
            }

            // Force the view to reload its data.
            tableView.ReloadData();
        }

        public override UITableViewCellEditingStyle EditingStyleForRow(UITableView tableView, NSIndexPath indexPath)
        {
            // Layers in the first section can be removed from the map (looks like deletion).
            // Layers in the second section can be added to the map (looks like insertion).
            switch (indexPath.Section)
            {
                case 0:
                    return UITableViewCellEditingStyle.Delete;
                default:
                    return UITableViewCellEditingStyle.Insert;
            }
        }

        public override bool CanMoveRow(UITableView tableView, NSIndexPath indexPath)
        {
            // All rows can be moved.
            return true;
        }

        public override void MoveRow(UITableView tableView, NSIndexPath sourceIndexPath, NSIndexPath destinationIndexPath)
        {
            // Find the source and destination lists (based on the section of the source and destination index).
            LayerCollection source = sourceIndexPath.Section == 0 ? IncludedLayers : ExcludedLayers;
            LayerCollection destination = destinationIndexPath.Section == 0 ? IncludedLayers : ExcludedLayers;

            // Find the layer that is being moved.
            Layer movedLayer = source[sourceIndexPath.Row];

            // Remove the layer from the source list and insert into the destination list.
            source.RemoveAt(sourceIndexPath.Row);
            destination.Insert(destinationIndexPath.Row, movedLayer);

            // Reload the table now that the data has changed.
            tableView.ReloadData();
        }
    }
}

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.