Add Google Street View Link to a Popup in ArcGIS Online with the ArcGIS API for Python

Table of Contents

Introduction

The ArcGIS API for Python is a powerful Python library that allows users to interact with and automate tasks in ArcGIS Online (or Portal). The API is excellent for programmatically creating, maintaining, and updating components of ArcGIS Online such as WebMap layers. In this post we will focus on manipulating the Popups for a point feature layer in a WebMap and add a field to the Popup that opens up a new browser window in Google Street View at that point location (if Street View is available at that location). The workflow below is advanced in nature where we manipulate the JSON representation of a layer in a WebMap. 

Geospatial Professionals, GIS Analysts, and enthusiasts will discover the power of automation and script-based operations to efficiently interact and update WebMaps in ArcGIS Online. Throughout this course, you will gain practical, hands-on experience in leveraging the ArcGIS API for Python to perform a wide range of WebMap tasks with ease.

We will dissect WebMaps for all that they are and by the end of this course you will be comfortable with manipulating the JSON, which is the behind the scenes configuration of a WebMap, to produce scripts for workflows where there is currently no API Python method, such as grouping layers.

arcgis modules

The API provides access to your organisations ArcGIS Online via the GIS class in the gis module. This GIS class is the gateway to ArcGIS Online. We will need to import the WebMap class to create a WebMap object. The WebMap class is a part of the mapping module. This mapping module provides components for working with 2D and 3D maps and scenes, and also includes classes for map layers such as the MapFeatureLayerMapImageLayer, and VectorTileLayer.

				
					## import GIS which provides the gateway entry to your AGOL
from arcgis.gis import GIS

## import the WebMap class to provide for interacting with WebMap layers
from arcgis.mapping import WebMap

				
			

Accessing ArcGIS Online

Our first port of call is to access your ArcGIS Online via the GIS class. There are a handful of ways to achieve access, if you are logged into your ArcGIS Online in ArcGIS Pro you can simply use "home", otherwise, another common way is to provide the ArcGIS Online URL, followed by your username and password.
				
					## Access AGOL
agol = GIS("home")
				
			
				
					## Access AGOL
agol = GIS(
    url = "https://your_organisation.maps.arcgis.com/",
    username = "Your_Username",
    password = "Your_Password"
)
				
			

The WebMap Layer JSON

The JSON configuration for the WebMap layer of interest is extremely important. In the snippet below we are creating an Item object using the ContentManager get() method and supplying in a WebMap Item ID. We then supply this Item object as a parameter to instantiate a WebMap object. Once we have our WebMap object we can use the get_layer() method and supply the title of the layer of interest to return the JSON configuration for that layer.

See ArcGIS API for Python – WebMap Object versus Item Object for more information the difference between a WebMap Item object and a WebMap object.

				
					## Access the WebMap Item
wm_item = agol.content.get("WM_ITEM_ID")

## Create a WebMap object from WebMp Item
webmap = WebMap(wm_item)

## Get a Feature Layer as a dictionary using WebMap object get_layer() method
lyr = webmap.get_layer(title="NIAH Locations")

print(lyr)

##{
##  "id": "1918390d9f8-layer-5",
##  "title": "NIAH Locations",
##  "url": "https://services-eu1.arcgis.com/xImpnbbhc3YDkwTJ/arcgis/rest/services/NIAH Locations/FeatureServer/0",
##  "itemId": "907fb391a6e94628b296c22cafc339aa",
##  "layerType": "ArcGISFeatureLayer",
##  "popupInfo": {
##    "popupElements": [
##      {
##        "type": "fields"
##      },
##      {
##        "type": "attachments",
##        "displayType": "auto"
##      }
##    ],
##    "showAttachments": true,
##    "fieldInfos": [
##      {
##        "fieldName": "FID",
##        "isEditable": false,
##        "label": "FID",
##        "visible": false
##      },
##      {
##        "fieldName": "Date_Field",
##        "isEditable": true,
##        "label": "Date_Field",
##        "visible": true
##      },
##      {
##        "fieldName": "REG_NO",
##        "format": {
##          "digitSeparator": true,
##          "places": 0
##        },
##        "isEditable": true,
##        "label": "REG_NO",
##        "visible": true
##      }
##    ],
##    "title": "NIAH Locations"
##  }
##}
				
			

The JSON above represents the NIAH Locations layer from the WebMap below.

Manipulate The Layer JSON

Take note that there is currently no “expressionInfos” property in our “popupInfo”. We will add the property later and give the expressionInfos the value of the expression variable below. The expressionInfos value must be a list of dictionaries. 

The dictionary below describes the Arcade expression for our Street View link. the “name” is simply the name of the expression, the “title” is how the field will be named in the popup, the “expression” is the Arcade code to generate the link. This code is available at the Harvard University Sharepoint site where access can be hit and miss so it is also below the Python code snippet.

				
					## This gets added to the expressionInfos property
expression = [
    {
        "name": "expr0",
        "title": "Google Streetview",
        "expression": "var PointGeometry = Centroid(Geometry($feature));\nvar ArcadeX = PointGeometry.x;\nvar ArcadeY = PointGeometry.y;\nvar ArcadeSr = PointGeometry.spatialReference.wkid;\nvar Latitude, Longitude;\n\nfunction AuxSphereToLatLon(x, y) {\n  Console(\"Converting...\");\n  // Conversion based on http://dotnetfollower.com/wordpress/2011/07/javascript-how-to-convert-mercator-sphere-coordinates-to-latitude-and-longitude/\n  var rMajor = 6378137;\n  var shift = PI * rMajor;\n  Longitude = x / shift * 180.0;\n  Latitude = y / shift * 180.0;\n  Latitude = 180 / PI * (2 * Atan(Exp(Latitude * PI / 180.0)) - PI / 2.0);\n}\n\nif (ArcadeSr == 4326) {\n  Console(\"4326 Spatial Reference - No Conversion Necessary\");\n  Latitude = ArcadeY;\n  Longitude = ArcadeX;\n} else if (ArcadeSr == 102100) {\n  Console(\"102100 Spatial Reference - Conversion Necessary\");\n  AuxSphereToLatLon(ArcadeX, ArcadeY);\n} else {\n  Console(ArcadeSr + \" Spatial Reference is not supported - currently works with Web Maps where the basemap is in WGS84 (4326) or Web Mercator Auxiliary Sphere 102100\");}\n  \n  var url = \"http://maps.google.com/maps?q=&layer=c&cbll=\" + text(Latitude) + \",\" + text(Longitude);\n  \n  return url;",
        "returnType": "string"
    }
]
				
			
				
					var PointGeometry = Geometry($feature);
var ArcadeX = PointGeometry.x;
var ArcadeY = PointGeometry.y;
var ArcadeSr = PointGeometry.spatialReference.wkid;
var Latitude, Longitude;

function AuxSphereToLatLon(x, y) 
{  Console("Converting...");

// Conversion based on http://dotnetfollower.com/wordpress/2011/07/javascript-how-to-convert-mercator-sphere-coordinates-to-latitude-and-longitude/ 

var rMajor = 6378137;
var shift = PI * rMajor;
Longitude = x / shift * 180.0;
Latitude = y / shift * 180.0;
Latitude = 180 / PI * (2 * Atan(Exp(Latitude * PI / 180.0)) - PI / 2.0);
}

if (ArcadeSr == 4326) {  Console("4326 Spatial Reference - No Conversion Necessary");  Latitude = ArcadeY;  Longitude = ArcadeX;} else if (ArcadeSr == 102100) {  Console("102100 Spatial Reference - Conversion Necessary");  AuxSphereToLatLon(ArcadeX, ArcadeY);} else {  Console(ArcadeSr + " Spatial Reference is not supported - currently works with Web Maps where the basemap is in WGS84 (4326) or Web Mercator Auxiliary Sphere 102100");}
var url = "http://www.google.com/maps/@?api=1&map_action=pano&viewpoint=" + text(Latitude) + "," + text(Longitude);
return url;
				
			

The little code snippet below needs to be added to the “fieldInfos” at the root of the “popupInfo” and also the “fieldInfos” in the “popupElements” property of the “popupInfo”. Ahh, but look, there is currently no fieldInfos in the “popupElements” property of the “popupInfo”, we’ll have to handle that!

				
					## This gets added to the fieldInfos in popupElement
exp_fld = {
    "fieldName": "expression/expr0",
    "isEditable": True,
    "visible": True
}
				
			

So let’s add the above code snippet to the “fieldInfos” at the root of the “popupInfo” property for the layer.

				
					## Add the field expression to the expressionInfos property
lyr.popupInfo.fieldInfos = lyr.popupInfo.fieldInfos + [exp_fld]
				
			

Next, we want to add the same to the “fieldInfos” in the “popupElements” property of the “popupInfo”. Depending on how mature your popups are, the “fieldInfos” might already exist in the “popupElements”, but if not, we will add and grab the details from the “fieldInfos” at the root of the “popupInfo”.

The popupElements represents the different components of the popup such as fields, images, and charts for examples. We are accessing [0] below, because the fields are the first element in our “popupElements” list (see the JSON)

				
					## update the popupElements fieldInfos
try:
    lyr.popupInfo.popupElements[0].fieldInfos = lyr.popupInfo.popupElements[0].fieldInfos + [exp_fld]
except AttributeError:
    lyr.popupInfo.popupElements[0]["fieldInfos"] = lyr.popupInfo.fieldInfos
				
			

Our last JSON manipulation is to update the “expressionInfos”. The below handles whether the “expressionInfos” property exists in the “popupInfo” or not.

				
					## update the popupInfo expressionInfos
try:
    lyr.popupInfo.expressionInfos = lyr.popupInfo.expressionInfos + [expression]
except AttributeError:
    lyr.popupInfo["expressionInfos"] = expression
				
			

Update and Save the WebMap

And finally we apply the updates, refresh our WebMap and try out the cool Street View link.

				
					status = webmap.update()

print(status)
				
			

Tip!

Perform Popup manipulations manually and print the layer definition to screen as we saw in the WebMap Layer JSON section, this will help you to figure out what elements of the JSON need to be manipulated to perform your updates programmatically.

Geospatial Professionals, GIS Analysts, and enthusiasts will discover the power of automation and script-based operations to efficiently interact and update WebMaps in ArcGIS Online. Throughout this course, you will gain practical, hands-on experience in leveraging the ArcGIS API for Python to perform a wide range of WebMap tasks with ease.

We will dissect WebMaps for all that they are and by the end of this course you will be comfortable with manipulating the JSON, which is the behind the scenes configuration of a WebMap, to produce scripts for workflows where there is currently no API Python method, such as grouping layers.

All the code in one place

You can find the entire code workflow below with links to important components in the documentation that were used.

				
					from arcgis.gis import GIS
from arcgis.mapping import WebMap

################################################################################
## API Reference Links:
##  https://developers.arcgis.com/python/api-reference/arcgis.mapping.toc.html#webmap
##  https://developers.arcgis.com/python/api-reference/arcgis.mapping.toc.html#arcgis.mapping.WebMap.get_layer
##  https://developers.arcgis.com/python/api-reference/arcgis.mapping.toc.html#arcgis.mapping.WebMap.update
##
## Description:
##  Add google streetview to point layer
##
## Notes:
##
##
## API Version: 2.2.0.1, 2.3.0
##
################################################################################

"""Access AGOL"""
agol = GIS("home")

## Access the WebMap Item
wm_item = agol.content.get("WM_ITEM_ID")

## Create a WebMap object from WebMp Item
webmap = WebMap(wm_item)

## the name of teh point layer
lyr = webmap.get_layer(title="PT_LAYER_NAME")

## This gets added to the expressionInfos property
expression = [
    {
        "name": "expr0",
        "title": "Google Streetview",
        "expression": "var PointGeometry = Centroid(Geometry($feature));\nvar ArcadeX = PointGeometry.x;\nvar ArcadeY = PointGeometry.y;\nvar ArcadeSr = PointGeometry.spatialReference.wkid;\nvar Latitude, Longitude;\n\nfunction AuxSphereToLatLon(x, y) {\n  Console(\"Converting...\");\n  // Conversion based on http://dotnetfollower.com/wordpress/2011/07/javascript-how-to-convert-mercator-sphere-coordinates-to-latitude-and-longitude/\n  var rMajor = 6378137;\n  var shift = PI * rMajor;\n  Longitude = x / shift * 180.0;\n  Latitude = y / shift * 180.0;\n  Latitude = 180 / PI * (2 * Atan(Exp(Latitude * PI / 180.0)) - PI / 2.0);\n}\n\nif (ArcadeSr == 4326) {\n  Console(\"4326 Spatial Reference - No Conversion Necessary\");\n  Latitude = ArcadeY;\n  Longitude = ArcadeX;\n} else if (ArcadeSr == 102100) {\n  Console(\"102100 Spatial Reference - Conversion Necessary\");\n  AuxSphereToLatLon(ArcadeX, ArcadeY);\n} else {\n  Console(ArcadeSr + \" Spatial Reference is not supported - currently works with Web Maps where the basemap is in WGS84 (4326) or Web Mercator Auxiliary Sphere 102100\");}\n  \n  var url = \"http://maps.google.com/maps?q=&layer=c&cbll=\" + text(Latitude) + \",\" + text(Longitude);\n  \n  return url;",
        "returnType": "string"
    }
]

## This gets added to the both fieldInfos in popupElement and fieldInfos"""
exp_fld = {
    "fieldName": "expression/expr0",
    "isEditable": True,
    "visible": True
}

## update fieldInfos
lyr.popupInfo.fieldInfos = lyr.popupInfo.fieldInfos + [exp_fld]

## update popupInfo popupElements fieldInfos
try:
    lyr.popupInfo.popupElements[0].fieldInfos = lyr.popupInfo.popupElements[0].fieldInfos + [exp_fld]
except AttributeError:
    lyr.popupInfo.popupElements[0]["fieldInfos"] = lyr.popupInfo.fieldInfos

## Add to the expressionInfos property
try:
    lyr.popupInfo.expressionInfos = lyr.popupInfo.expressionInfos + [expression]
except AttributeError:
    lyr.popupInfo["expressionInfos"] = expression


## Update and save the WebMap
status = webmap.update()

print(status)

################################################################################
print("\nSCRIPT COMPLETE")
				
			

Leave a Comment

Your email address will not be published. Required fields are marked *