Filter by time extent

View on GitHubSample viewer app

The time slider provides controls that allow you to visualize temporal data by applying a specific time extent to a map view.

Filter by time extent

Use case

When viewing feature layers that contain a large amount of data with timestamps, you may want to filter the data to only show data relevant to a specific time range. This can help visualize changes in the data over time and aids in making better interpretations and predictions of future trends.

How to use the sample

Use the slider at the bottom of the map to customize the date range for which you want to view the data. The date for the hurricanes sample data ranges from September 1st, 2005 to December 31st, 2005. Once the start and end dates have been selected, the map view will update to only show the relevant data that falls in the time extent selected. Use the play button to step through the data one day at a time. Use the previous and next buttons to go back and forth in 2 day increments as demonstrated below.

How it works

  1. Create an AGSMapView with an AGSMap.
  2. Create a feature layer by creating an AGSPortalItem and specifying the layer.
  3. Add the feature layer that includes time-enabled data to the map's array of operational layers.
  4. Create a TimeSlider from the ArcGIS Runtime Toolkit to allow users to show data only from the given date range. This sets up all necessary calls to visualize and step through the temporal data.

Relevant API

  • AGSFeatureLayer
  • AGSMap
  • AGSMapView
  • AGSServiceFeatureTable

Additional information

This sample uses Atlantic hurricane data from the National Hurricane Center (NOAA / National Weather Service) and the TimeSlider toolkit component which requires the toolkit to be cloned and set up locally. For information about setting up the toolkit, see the repository's root README.md here.

Tags

animate, data, filter, time, time extent, time frame, toolkit

Sample Code

FilterByTimeExtentViewController.swift
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
// Copyright 2021 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
import ArcGISToolkit

class FilterByTimeExtentViewController: UIViewController {
    /// The map view.
    @IBOutlet var mapView: AGSMapView! {
        didSet {
            let map = makeMap()
            mapView.map = map
            // Set the map view's viewpoint.
            let center = AGSPoint(x: -58.495293, y: 29.979774, spatialReference: .wgs84())
            mapView.setViewpoint(AGSViewpoint(center: center, scale: 1.5e8))
        }
    }

    /// The time slider from the ArcGIS toolkit.
    let timeSlider: TimeSlider = {
        let timeSlider = TimeSlider()
        timeSlider.playbackInterval = 0.5
        return timeSlider
    }()

    func makeMap() -> AGSMap {
        let featureLayer = AGSFeatureLayer(
            item: AGSPortalItem(
                portal: .arcGISOnline(withLoginRequired: false),
                itemID: "49925d814d7e40fb8fa64864ef62d55e"
            ),
            layerID: 0
        )
        initializeTimeSlider(for: featureLayer)

        let map = AGSMap(basemapStyle: .arcGISTopographic)
        map.operationalLayers.add(featureLayer)
        return map
    }

    /// Initialize the time steps.
    func initializeTimeSlider(for featureLayer: AGSFeatureLayer) {
        // The default date and time for the starting and ending thumb.
        let currentTimeExtent: AGSTimeExtent = {
            // The date formatter.
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy/MM/dd HH:mm"
            return AGSTimeExtent(
                startTime: formatter.date(from: "2005/10/01 05:00")!,
                endTime: formatter.date(from: "2005/10/31 05:00")!
            )
        }()
        featureLayer.load { [weak self] error in
            guard error == nil, let fullTimeExtent = featureLayer.fullTimeExtent else { return }
            self?.timeSlider.initializeTimeSteps(timeStepCount: 60, fullExtent: fullTimeExtent) { _ in
                // Set the current time extent.
                self?.timeSlider.currentExtent = currentTimeExtent
            }
        }
    }

    /// Configure the time slider's attributes and position.
    func setupTimeSlider() {
        // Configure time slider.
        timeSlider.labelMode = .thumbs
        timeSlider.addTarget(self, action: #selector(timeSliderValueChanged(timeSlider:)), for: .valueChanged)
        // Add the time slider to the view.
        view.addSubview(timeSlider)

        // Add constraints to position the slider.
        timeSlider.translatesAutoresizingMaskIntoConstraints = false
        // Assign the constraints.
        let constraints = [
            timeSlider.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1),
            view.safeAreaLayoutGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: timeSlider.trailingAnchor, multiplier: 1),
            mapView.attributionTopAnchor.constraint(equalToSystemSpacingBelow: timeSlider.bottomAnchor, multiplier: 1)
        ]
        // Activate the constraints.
        NSLayoutConstraint.activate(constraints)
    }

    @objc
    func timeSliderValueChanged(timeSlider: TimeSlider) {
        if mapView.timeExtent != timeSlider.currentExtent {
            mapView.timeExtent = timeSlider.currentExtent
        }
    }

    // MARK: UIViewController

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

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