Part 2 - Navigating the map widget

Using the map widget

The GIS object includes a map widget for displaying geographic locations, visualizing GIS content, and displaying the results of your analysis. To use the map widget, call gis.map() and assign it to a variable that you will then be able to query to bring up the widget in the notebook:

import arcgis
from arcgis.gis import GIS
# Create a GIS object, as an anonymous user for this example
gis = GIS()
# Create a map widget
map1 = gis.map('Redlands, CA') # Passing a place name to the constructor
                               # will initialize the extent of the map.
map1

Buttons

Now that we have created a map view, let us explore the default buttons enabled on the widget:

1. Zoom in

Click the "+" sign shown on the top left corner of the widget (marked as button #1 in previous map output) to zoom into details of the map. Users can either manually zoom into a desired level of detail or set the zoom levels to an assigned number, which we will elaborate on in the next section.

2. Zoom out

Click the "-" sign shown on the top left corner of the widget (marked as button #2 in previous map output) to zoom out to a rough display of the map. Users can either manually zoom out to a desired level of details, or set the zoom levels to an assigned number, which we will elaborate on in the next section.

3. Reset the compass orientation

Click the compass sign (marked as button #3 in the previous map display) to switch the map's heading to 0.0 (north) in relation to the current device, and click again to switch back to the absolute 0.0 north.

4. 2D Map to 3D Scene

Click the "Map to Scene" button (marked as #4 in the previous map display) to switch the current view from a 2D Map to a 3D Scene. Click the button again to switch back.

Properties of the map widget

Operations of the map widget

The map widget has several properties that you can query and set, such as its zoom level, basemap, height, extent, mode, heading, rotation, tilt, scale, etc.

Zoom Level and Rotation

# Create a map widget
map2 = gis.map('Redlands, CA') # Passing a place name to the constructor
                               # will initialize the extent of the map.
map2
map2.zoom
11.0

Assigning a value to the zoom property will update the widget, which is equivalent to manually clicking the "zoom in" button twice.

map2.zoom = 9

You can also set the rotation property for the 2D mode. This can similarly be achieved by right-clicking and dragging on the map.

map2.rotation = 45

Your notebook can have as many of these widgets as you wish. Let's create another map widget and modify some of its properties.

Map Center

The center property reveals the coordinates of the center of the map.

map3 = gis.map() # creating a map object with default parameters
map3
map3.center
{'spatialReference': {'latestWkid': 3857, 'wkid': 102100},
 'x': 0,
 'y': 1.30385160446167e-08}

If you know the latitude and longitude of your place of interest, you can assign it to the center property. For instance, we can now set the center to be within California.

map3.center = {'spatialReference': {'latestWkid': 3857, 'wkid': 102100},
               'x': -13044706.248636946,
               'y': 4036244.856763349}

Extent

You can use geocoding to get the coordinates of different places and use those coordinates to drive the extent property. Geocoding converts place names to coordinates and can be implemented by using the arcgis.geocoding.geocode() function.

Let's geocode "Disneyland, CA" and set the map's extent to the geocoded location's extent:

location = arcgis.geocoding.geocode('Disneyland, CA', max_locations=1)[0]
map3.extent = location['extent']

Basemap

ArcGIS Living Atlas of the World is an evolving collection of authoritative, curated, ready-to-use global geographic information from Esri and the GIS user community. One of the most used types of content from the Living Atlas is basemaps. Basemaps are layers on your map over which all other operational layers that you add are displayed. Basemaps typically span the full extent of the world and provide context to your GIS layers. It helps viewers understand where each feature is located as they pan and zoom to various extents.

When you create a new map or scene, you can choose which basemap you want from the basemap gallery in the Map Viewer. By default, the basemap gallery for your organization is a pre-configured collection from Esri using the Living Atlas. There are many more Living Atlas basemaps to choose from, and you can create your own custom basemap gallery with the ones you like. Learn more on this here.

As an administrator of your organization, you can change which basemaps your organization uses by creating a custom basemap gallery. The custom gallery can include a combination of your own basemaps, plus Living Atlas basemaps. In a nutshell, the steps to create a custom basemap gallery are as follows:

  • Createa group for your custom basemap gallery.
  • Add maps you want to use as basemaps to the group.
  • Set the group as your organization’s basemap gallery.

These steps are detailed in the Create a custom basemap gallery for your organization blog. After step one is done, users can move forward to :

When your gis connection is created anonymously, the basemaps and gallery_basemaps properties of the created MapView object displays the default themes. While signing onto your own organization, the two properties would display your own customized options if set.

Your map can have a number of different basemaps. To see what basemaps are included with the widget, query the basemaps property:

map3.basemap # the current basemap being used
'default'
map3.basemaps # the basemap galleries
['dark-gray',
 'dark-gray-vector',
 'gray',
 'gray-vector',
 'hybrid',
 'national-geographic',
 'oceans',
 'osm',
 'satellite',
 'streets',
 'streets-navigation-vector',
 'streets-night-vector',
 'streets-relief-vector',
 'streets-vector',
 'terrain',
 'topo',
 'topo-vector']

You can assign any one of the supported basemaps to the basemap property to change the basemap. For instance, you can change the basemap to the satellite basemap as below:

map3.basemap = 'satellite'

Mode

The map widget also includes support for a 3D mode! You can specify the mode parameter either through gis.map(mode="foo") or by setting the mode property of any initiated map object. Run the following cell:

usa_map = gis.map('USA', zoomlevel=4, mode="3D") # Notice `mode="3D"`
usa_map
# And you can set the mode separately
usa_map.mode = "2D"

Heading, tilt and scale

Just like the 2D mode, you can pan by clicking-and-dragging with the left mouse button, and you can zoom with the mouse wheel. In 3D mode, clicking-and-dragging with the right mouse button modifies the tilt field and the heading field.

tilt is a number from 0-90, with 0 representing a top-down 'birds-eye' view, while 90 represents being completely parallel to the ground, facing the horizon.

usa_map.tilt
0.2263099051855245
usa_map.tilt = 22.63

It's important to note that 2D mode uses rotation to specify the number of angles clockwise from due north, while 3D mode uses heading to specify the number of degrees counterclockwise of due north. See the API reference for more information.

usa_map.heading
0.0
usa_map.heading = 60

Using multiple map widgets

Demo: creating multiple widgets in the same notebook

Stacking maps using HBox and VBox

One commonly adopted workflow for creating multiple widgets in the same notebook is to embed Python API map widgets within HBox and VBox. First, let's walk through an example of displaying Landsat imagery of two different dates side by side using an HBox structure:

# search for the landsat multispectral imagery layer
landsat_item = gis.content.search("Landsat Multispectral tags:'Landsat on AWS','landsat 8', 'Multispectral', 'Multitemporal', 'imagery', 'temporal', 'MS'", 'Imagery Layer', outside_org=True)[0]
landsat = landsat_item.layers[0]
landsat
<ImageryLayer url:"https://landsat2.arcgis.com/arcgis/rest/services/Landsat/MS/ImageServer">
import pandas as pd
from datetime import datetime
from ipywidgets import *
from arcgis import geocode
aoi = {'xmin': -117.58051663099998,
       'ymin': 33.43943880400006,
       'xmax': -114.77651663099998,
       'ymax': 36.243438804000064,
       'spatialReference': {'latestWkid': 4326, 'wkid': 102100},}
selected1 = landsat.filter_by(where="(Category = 1) AND (CloudCover <=0.10)", 
                              time=[datetime(2017, 11, 15), datetime(2018, 1, 1)],
                              geometry=arcgis.geometry.filters.intersects(aoi))

df = selected1.query(out_fields="AcquisitionDate, GroupName, CloudCover, DayOfYear", 
                     order_by_fields="AcquisitionDate").sdf
df['AcquisitionDate'] = pd.to_datetime(df['AcquisitionDate'], unit='ms')
df.tail(5)
OBJECTIDAcquisitionDateGroupNameCloudCoverDayOfYearSHAPE
1720772762017-12-27 18:21:56LC08_L1TP_040035_20171227_20200902_02_T1_MTL0.003335{"rings": [[[-12818204.659699999, 4395823.9407...
1820772992017-12-27 18:22:20LC08_L1TP_040036_20171227_20200902_02_T1_MTL0.002336{"rings": [[[-12866539.864599999, 4199117.8993...
1920773222017-12-27 18:22:44LC08_L1TP_040037_20171227_20200902_02_T1_MTL0.002537{"rings": [[[-12913258.506099999, 4005782.8035...
2020724192017-12-29 18:09:58LC08_L1TP_038036_20171229_20200902_02_T1_MTL0.000436{"rings": [[[-12522415.1111, 4199148.978100002...
2120724422017-12-29 18:10:21LC08_L1TP_038037_20171229_20200902_02_T1_MTL0.001437{"rings": [[[-12569056.4399, 4005554.541000001...
selected2 = landsat.filter_by(where="(Category = 1) AND (CloudCover <=0.10)", 
                              time=[datetime(2021, 11, 15), datetime(2022, 1, 1)],
                              geometry=arcgis.geometry.filters.intersects(aoi))

df = selected2.query(out_fields="AcquisitionDate, GroupName, CloudCover, DayOfYear", 
                     order_by_fields="AcquisitionDate").sdf
df['AcquisitionDate'] = pd.to_datetime(df['AcquisitionDate'], unit='ms')
df.tail(5)
OBJECTIDAcquisitionDateGroupNameCloudCoverDayOfYearSHAPE
533125822021-11-27 18:29:05LC08_L1TP_041037_20211127_20211208_02_T1_MTL0.044437{"rings": [[[-13082602.7775, 4005564.120899997...
633125572021-11-29 18:15:56LC08_L1TP_039035_20211129_20211209_02_T1_MTL0.002735{"rings": [[[-12643840.9522, 4395757.556199998...
733125582021-11-29 18:16:20LC08_L1TP_039036_20211129_20211209_02_T1_MTL0.001436{"rings": [[[-12691989.560800001, 4198998.6582...
833125592021-11-29 18:16:44LC08_L1TP_039037_20211129_20211209_02_T1_MTL0.001337{"rings": [[[-12738717.156100001, 4005622.7378...
933290772021-12-15 18:16:43LC08_L1TP_039037_20211215_20211223_02_T1_MTL0.005237{"rings": [[[-12739407.4636, 4005732.446599997...
def side_by_side(address, layer1, layer2):
    location = geocode(address)[0]

    satmap1 = gis.map(location)
    satmap1.add_layer(layer1)

    satmap2 = gis.map(location)
    satmap2.add_layer(layer2)

    satmap1.layout=Layout(flex='1 1', padding='6px', height='450px')
    satmap2.layout=Layout(flex='1 1', padding='6px', height='450px')

    box = HBox([satmap1, satmap2])
    return box
side_by_side("San Bernadino County, CA", selected1, selected2)

Synchronizing nav between multiple widgets

A side-by-side display of two maps is great for users wanting to explore the differences between two maps. However, if one map gets dragged or panned, the other map is not following the movements automatically. The methods sync_navigation and unsync_navigation can be introduced to resolve this. With these methods, we can modify the previous example to have the maps in sync.

def side_by_side2(address, layer1, label1, layer2, label2):
    location = geocode(address)[0]

    satmap1 = gis.map(location)
    satmap1.add_layer(layer1)

    satmap2 = gis.map(location)
    satmap2.add_layer(layer2)
 
    # create 2 hbox - one for title, another for maps
    hb1 = HBox([Label(label1), Label(label2)])
    hb2 = HBox([satmap1, satmap2])
    
    # set hbox layout preferences
    hbox_layout = Layout()
    hbox_layout.justify_content = 'space-around'
    hb1.layout, hb2.layout = hbox_layout, hbox_layout
    
    # sync all maps
    satmap1.sync_navigation(satmap2)

    return VBox([hb1,hb2])
side_by_side2("San Bernadino County, CA", selected1, 'Dec 2017', selected2, 'Dec 2021')
<IPython.core.display.Image object>

The synced display of 4 maps

Now let's sync the display of 4 maps:

selected1r = landsat.filter_by(where="(Category = 1) AND (CloudCover <=0.10)", 
                               time=[datetime(2018, 11, 15), datetime(2019, 1, 1)],
                               geometry=arcgis.geometry.filters.intersects(aoi))

df = selected1r.query(out_fields="AcquisitionDate, GroupName, CloudCover, DayOfYear", 
                     order_by_fields="AcquisitionDate").sdf
df['AcquisitionDate'] = pd.to_datetime(df['AcquisitionDate'], unit='ms')
df.tail(5)
OBJECTIDAcquisitionDateGroupNameCloudCoverDayOfYearSHAPE
416838912018-12-16 18:09:40LC08_L1TP_038036_20181216_20200830_02_T1_MTL0.031136{"rings": [[[-12518740.9842, 4199058.405000001...
516877392018-12-23 18:15:27LC08_L1TP_039035_20181223_20200830_02_T1_MTL0.045335{"rings": [[[-12642295.304499999, 4395699.8553...
616909552018-12-30 18:21:38LC08_L1TP_040035_20181230_20200829_02_T1_MTL0.054835{"rings": [[[-12814406.3816, 4395749.071999997...
716909782018-12-30 18:22:02LC08_L1TP_040036_20181230_20200830_02_T1_MTL0.008536{"rings": [[[-12862669.9624, 4198971.424900003...
816910012018-12-30 18:22:26LC08_L1TP_040037_20181230_20200830_02_T1_MTL0.001137{"rings": [[[-12909449.0056, 4005655.638700001...
selected2l = landsat.filter_by(where="(Category = 1) AND (CloudCover <=0.10)", 
                              time=[datetime(2020, 11, 15), datetime(2021, 1, 1)],
                              geometry=arcgis.geometry.filters.intersects(aoi))

df = selected2l.query(out_fields="AcquisitionDate, GroupName, CloudCover, DayOfYear", 
                     order_by_fields="AcquisitionDate").sdf
df['AcquisitionDate'] = pd.to_datetime(df['AcquisitionDate'], unit='ms')
df.tail(5)
OBJECTIDAcquisitionDateGroupNameCloudCoverDayOfYearSHAPE
143381392020-12-05 18:10:33LC08_L1TP_038037_20201205_20210313_02_T1_MTL0.001037{"rings": [[[-12590665.892099999, 3909761.2709...
1529526452020-12-10 18:28:17LC08_L1TP_041035_20201210_20210313_02_T1_MTL0.096335{"rings": [[[-12988587.3908, 4395753.784199998...
163566042020-12-19 18:22:05LC08_L1TP_040035_20201219_20210310_02_T1_MTL0.006035{"rings": [[[-12816854.251699999, 4395799.5128...
173566262020-12-19 18:22:29LC08_L1TP_040036_20201219_20210310_02_T1_MTL0.002636{"rings": [[[-12865073.5445, 4199098.650499999...
183566482020-12-19 18:22:53LC08_L1TP_040037_20201219_20210310_02_T1_MTL0.000437{"rings": [[[-12911955.1563, 4005769.613499999...
def side_by_side3(address, layers_list, labels_list):
    [layer1, layer2, layer3, layer4] = layers_list
    [label1, label2, label3, label4] = labels_list
    location = geocode(address)[0]

    satmap1 = gis.map(location)
    satmap1.add_layer(layer1)

    satmap2 = gis.map(location)
    satmap2.add_layer(layer2)
    
    satmap3 = gis.map(location)
    satmap3.add_layer(layer3)

    satmap4 = gis.map(location)
    satmap4.add_layer(layer4)
 
    # create 2 hbox - one for title, another for maps
    hb1 = HBox([Label(label1), Label(label2)])
    hb2 = HBox([satmap1, satmap2])
    hb3 = HBox([Label(label3), Label(label4)])
    hb4 = HBox([satmap3, satmap4])
    
    # set hbox layout preferences
    hbox_layout = Layout()
    hbox_layout.justify_content = 'space-around'
    hb1.layout, hb2.layout, hb3.layout, hb4.layout = hbox_layout, hbox_layout, hbox_layout, hbox_layout
    
    # sync all maps
    satmap1.sync_navigation(satmap2)
    satmap1.sync_navigation(satmap3)
    satmap1.sync_navigation(satmap4)

    return VBox([hb1,hb2,hb3,hb4])
side_by_side3("San Bernadino County, CA", 
              [selected1,selected1r,selected2l,selected2], 
              ['Dec 2017', 'Dec 2018', 'Dec 2020', 'Dec 2021'])

Dragging and panning onto any of these 4 maps will lead to synchronous movements of the other three maps, which means that the map center, zoom levels, and extent of these maps will always stay the same. This is one of the biggest advantages of sync_navigation().

Conclusion

In Part 2 of the guide series, we have talked about the use of map widgets, including buttons, features, and properties, and have seen three examples of displaying multiple map widgets in a group view using HBox and VBox. In the next chapter, we will discuss how to visualize spatial data on the map widget.

Back to Top

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