示例:准备在离线工作流中使用的 Esri 底图
Portal for ArcGIS 预先配置了来自 ArcGIS Online 的底图集合。不支持在离线工作流中使用这些底图。例如,您无法在 Collector for ArcGIS 中离线使用预配置底图。
以下 python 脚本采用默认 ArcGIS Online 底图并将其作为地图项目添加到门户中。可离线使用底图地图项目并将其用于离线工作流中。为运行脚本,您需要提供 ArcGIS Online 帐户凭据。
运行此脚本时,您需要指定以下信息:
- 用于访问 Portal for ArcGIS 的 URL。它可以是 ArcGIS Web Adaptor URL,例如 https://webadaptor.domain.com/arcgis 或托管门户的计算机的 URL,例如 https://portal.domain.com:7443/arcgis。指定 URL 时,必须包括计算机的完全限定的域名。
- 门户中的帐户用户名和密码。
- 将在其中创建新地图项目的文件夹的名称。如果未提供此文件夹,则将在您的根文件夹中创建地图项目。
- ArcGIS Online 帐户的用户名和密码。此脚本会将您的 ArcGIS Online 凭据嵌入到新地图项目中,这将启用底图的离线功能。
以下示例将在您的根文件夹中创建底图集合。这些底图中包括您的 ArcGIS Online 凭据,这样诸如 Collector for ArcGIS 等应用程序便可在离线工作流中使用这些底图。
python prepareEsriBasemapsForOfflineUse.py https://webadaptor.domain.com/arcgis portalUser portalPassword arcGISOnlineUser arcGISOnlinePassword
#!/usr/bin/env python
# Requires Python 2.7+
# Sample Usage:
# python addOfflineBasemaps.py -u <destinationPortal> -o <portalAdmin>
# -s <portalPassword> -f <destFolder>
# -a <agoUsername> -p <agoPassword>
import urllib
import json
import argparse
def generateToken(username, password, portalUrl):
'''Retrieves a token to be used with API requests.'''
parameters = urllib.urlencode({'username' : username,
'password' : password,
'client' : 'referer',
'referer': portalUrl,
'expiration': 60,
'f' : 'json'})
response = urllib.urlopen(portalUrl + '/sharing/rest/generateToken?',
parameters).read()
try:
jsonResponse = json.loads(response)
if 'token' in jsonResponse:
return jsonResponse['token']
elif 'error' in jsonResponse:
print jsonResponse['error']['message']
for detail in jsonResponse['error']['details']:
print detail
except ValueError, e:
print 'An unspecified error occurred.'
print e
def searchPortal(portal, query=None, totalResults=None, sortField='numviews',
sortOrder='desc', token=''):
'''
Search the portal using the specified query and search parameters.
Optionally provide a token to return results visible to that user.
'''
# Default results are returned by highest
# number of views in descending order.
allResults = []
if not totalResults or totalResults > 100:
numResults = 100
else:
numResults = totalResults
results = __search__(portal, query, numResults, sortField, sortOrder, 0,
token)
if not 'error' in results.keys():
if not totalResults:
totalResults = results['total'] # Return all of the results.
allResults.extend(results['results'])
while (results['nextStart'] > 0 and
results['nextStart'] < totalResults):
# Do some math to ensure it only
# returns the total results requested.
numResults = min(totalResults - results['nextStart'] + 1, 100)
results = __search__(portal=portal, query=query,
numResults=numResults, sortField=sortField,
sortOrder=sortOrder, token=token,
start=results['nextStart'])
allResults.extend(results['results'])
return allResults
else:
print results['error']['message']
return results
def __search__(portal, query=None, numResults=100, sortField='numviews',
sortOrder='desc', start=0, token=None):
'''Retrieve a single page of search results.'''
params = {
'q': query,
'num': numResults,
'sortField': sortField,
'sortOrder': sortOrder,
'f': 'json',
'start': start
}
if token:
# Adding a token provides an authenticated search.
params['token'] = token
request = portal + '/sharing/rest/search?' + urllib.urlencode(params)
results = json.loads(urllib.urlopen(request).read())
return results
def groupSearch(query, portalUrl, token=''):
'''Search for groups matching the specified query.'''
# Example 1: query all groups owned by a user.
# 'owner:johndoe'
# Example 2: query groups with Operations in the name.
# 'Operations'
# Example 3: query all groups with public access.
# 'access:public'
parameters = urllib.urlencode({'q': query, 'token': token, 'f': 'json'})
request = (portalUrl + '/sharing/rest/community/groups?' + parameters)
groups = json.loads(urllib.urlopen(request).read())
return groups['results']
def getUserContent(username, portalUrl, token):
''''''
parameters = urllib.urlencode({'token': token, 'f': 'json'})
request = (portalUrl + '/sharing/rest/content/users/' + username +
'?' + parameters)
print request
content = urllib.urlopen(request).read()
return json.loads(content)
def getItemDescription(itemId, portalUrl, token=''):
'''Returns the description for a Portal for ArcGIS item.'''
parameters = urllib.urlencode({'token' : token,
'f' : 'json'})
response = urllib.urlopen(portalUrl + "/sharing/rest/content/items/" +
itemId + "?" + parameters).read()
return json.loads(unicode(response, 'utf-8'))
def createFolder(username, title, portalUrl, token):
'''Creates a new folder in a user's content.'''
parameters = urllib.urlencode({'title': title,
'token' : token,
'f' : 'json'})
response = urllib.urlopen(portalUrl + '/sharing/rest/content/users/' +
username + '/createFolder?', parameters).read()
return json.loads(response)
def addServiceItem(username, folder, description, serviceUrl, portalUrl,
token, thumbnailUrl='', serviceUsername=None,
servicePassword=None):
'''Creates a new item in a user's content.'''
# Update the description with unicode safe values.
descriptionJSON = __decodeDict__(json.loads(description))
parameters = {'item': descriptionJSON['title'],
'url': serviceUrl,
'thumbnailurl': thumbnailUrl,
'overwrite': 'false',
'token' : token,
'f' : 'json'}
if serviceUsername and servicePassword:
# Store the credentials with the service.
parameters.update({'serviceUsername': serviceUsername,
'servicePassword': servicePassword})
# Add the item's description (with safe values for unicode).
parameters.update(descriptionJSON)
# Encode and post the item.
postParameters = urllib.urlencode(parameters)
response = urllib.urlopen(portalUrl + '/sharing/rest/content/users/' +
username + '/' + folder + '/addItem?',
postParameters).read()
return json.loads(response)
# Helper functions for decoding the unicode values in the response json.
def __decodeDict__(dct):
newdict = {}
for k, v in dct.iteritems():
k = __safeValue__(k)
v = __safeValue__(v)
newdict[k] = v
return newdict
def __safeValue__(inVal):
outVal = inVal
if isinstance(inVal, unicode):
outVal = inVal.encode('utf-8')
elif isinstance(inVal, list):
outVal = __decode_list__(inVal)
return outVal
def __decode_list__(lst):
newList = []
for i in lst:
i = __safeValue__(i)
newList.append(i)
return newList
# Run the script.
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--portal', required=True,
help=('url of the Portal (e.g. '
'https://portal.domain.com:7443/arcgis)'))
parser.add_argument('-o', '--portalAdmin', required=True,
help='Portal admin username')
parser.add_argument('-s', '--portalPassword', required=True,
help='Portal admin password')
parser.add_argument('-a', '--agoAdmin', required=True,
help='ArcGIS Online admin username')
parser.add_argument('-p', '--agoPassword', required=True,
help='ArcGIS Online admin password')
parser.add_argument('-f', '--folder', required=False,
help='Optional destination folder')
# Read the command line arguments.
args = parser.parse_args()
agoAdmin = args.agoAdmin
agoPassword = args.agoPassword
portal = args.portal
portalAdmin = args.portalAdmin
portalPassword = args.portalPassword
folderTitle = args.folder
# Get a token for the Portal for ArcGIS.
print 'Getting token for ' + portal
token = generateToken(username=portalAdmin, password=portalPassword,
portalUrl=portal)
# Get the destination folder ID.
folderId = ''
if folderTitle == None:
# No folder specified. Folder is root.
print 'Using the root folder...'
folderId = '/'
else:
# Check if the folder already exists.
userContent = getUserContent(portalAdmin, portal, token)
for folder in userContent['folders']:
if folder['title'] == folderTitle:
folderId = folder['id']
# Create the folder if it was not found.
if folderId == '':
print 'Creating folder ' + args.folder + '...'
newFolder = createFolder(portalAdmin, folderTitle, portal, token)
folderId = newFolder['folder']['id']
print 'Using folder ' + folderTitle + ' (id:' + folderId + ')'
# Get the ArcGIS Online group ID.
query = 'owner:esri title:Tiled Basemaps'
# Search for the public ArcGIS Online group (no token needed).
sourceGroup = groupSearch(query, 'https://www.arcgis.com')[0]['id']
# Get the items in the ArcGIS Online group specified above.
basemaps = searchPortal('https://www.arcgis.com', 'group:' + sourceGroup)
# Add the basemaps as new items in the Portal.
for basemap in basemaps:
# Get the item description.
description = getItemDescription(basemap['id'],
'https://www.arcgis.com')
serviceUrl = description['url']
thumbUrl = ('https://www.arcgis.com' +
'/sharing/rest/content/items/' + description['id'] +
'/info/' + description['thumbnail'])
newDescription = json.dumps(
{'title': description['title'],
'type': description['type'],
'snippet': description['snippet'],
'description': description['description'],
'licenseInfo': description['licenseInfo'],
'tags': ','.join(description['tags']),
'typeKeywords': ','.join(description['typeKeywords']),
'accessInformation': description['accessInformation']}
)
try:
result = addServiceItem(portalAdmin, folderId, newDescription,
serviceUrl, portal, token, thumbUrl,
agoAdmin, agoPassword)
if 'success' in result:
print 'Successfully added ' + basemap['title']
elif 'error' in result:
print 'Error copying ' + basemap['title']
print result['error']['message']
for detail in result['error']['details']:
print detail
else:
print 'Error copying ' + basemap['title']
print 'An unhandled error occurred.'
except:
print 'Error copying ' + basemap['title']
print 'An unhandled exception occurred.'
print 'Copying complete.'