Python
ArcGIS
Arcpy

ArcPyコード集

面倒なのでArcGIS ServerのセットアップをArcPyで自動化した.
使える部品を紹介してみる.

ArcPyの環境構築

ArcGIS Desktopを入れる.

ArcPyの実行

例えばArcGIS Desktop 10.5をインストールし, Pythonをデフォルト設定でインストールした場合はこんな感じ.

C:\Python27\ArcGIS10.5\python.exe -B E:\Scripts\sample.py

-Bで.cpyファイルを作らないようにした.

Arc Catalogにデータベース接続を追加する

  • out_dir, file_nameで接続ファイルのパスとファイル名を指定する.
  • db_platformでデータベースの種類を指定する(SQLServer, Oracle, ...).
  • account_authenticationで認証方式を指定する.
    • DATABASE_AUTH: DB認証
    • OPERATING_SYSTEM_AUTH: OS認証
  • save_user_passでユーザー名とパスワードを接続ファイルに保存するかどうかを指定する.
    • SAVE_USERNAME: 保存する
    • DO_NOT_SAVE_USERNAME: 保存しない
import arcpy

out_dir = r'C:\Users\someone\AppData\Roaming\ESRI\Desktop10.5\ArcCatalog'
file_name = 'db.sde'
db_platform = 'SQL_SERVER'
instance = 'SAMPLE'
account_authentication = 'DATABASE_AUTH'
user_name = 'user'
user_pass = 'pass'
save_user_pass = 'SAVE_USERNAME'
db_name = 'TEST_DB'

arcpy.CreateDatabaseConnection_management(out_dir,
                                          file_name,
                                          db_platform,
                                          instance,
                                          account_authentication,
                                          user_name,
                                          user_pass,
                                          save_user_pass,
                                          db_name)

参考

http://desktop.arcgis.com/ja/arcmap/10.3/tools/data-management-toolbox/create-database-connection.htm

Arc CatalogにArcGIS Serverの接続を追加する

  • gisserver_connection_typeで接続の設定をする.
    • USER_GIS_SERVICES: GISサービスを使用する
    • PUBLISH_GIS_SERVICES: GISサービスを公開する
    • ADMINISTER_GIS_SERVICES: GISサービスを管理する
  • out_dir, file_nameで接続ファイルのパスとファイル名を指定する.
  • save_user_passでユーザー名とパスワードを接続ファイルに保存するかどうかを指定する.
    • SAVE_USERNAME: 保存する
    • DO_NOT_SAVE_USERNAME: 保存しない
import arcpy

gisserver_connection_type = 'ADMINISTER_GIS_SERVICES'
out_dir = r'C:\Users\someone\AppData\Roaming\ESRI\Desktop10.5\ArcCatalog'
file_name = 'arcgisserverconn.ags'
arcgisserver_url = 'http://server:6080/arcgis/admin'
server_type = 'ARCGIS_SERVER'
use_arcgis_desktop_staging_folder = False
staging_folder_path = out_dir
user_name = 'user'
user_pass = 'pass'
save_user_pass = 'SAVE_USERNAME'

arcpy.mapping.CreateGISServerConnectionFile(gisserver_connection_type,
                                            out_dir,
                                            file_name,
                                            arcgisserver_url,
                                            server_type,
                                            use_arcgis_desktop_staging_folder,
                                            staging_folder_path,
                                            user_name,
                                            user_pass,
                                            save_username_password)

参考

http://desktop.arcgis.com/ja/arcmap/10.3/analyze/arcpy-mapping/creategisserverconnectionfile.htm

ArcGIS Serverのデータストアにデータベースを追加する

  • server_connection_file_full_pathでArcGIS Serverの接続ファイル(.ags)のフルパスを指定する.
  • datastore_typeでデータストアの形式を指定する.
    • DATABASE: データベース
    • FOLDER: フォルダー
  • connection_nameで接続に名前を付ける.
  • db_connection_file_full_pathでデータベース接続ファイル(.sde)のフルパスを指定する.
import arcpy

server_connection_file_full_path = r'C:\Users\someone\AppData\Roaming\ESRI\Desktop10.5\ArcCatalog\arcgisserverconn.ags'
datastore_type = 'DATABASE'
connection_name = 'TEST_DB'
db_connection_file_full_path = r'C:\Users\someone\AppData\Roaming\ESRI\Desktop10.5\ArcCatalog\db.sde'

arcpy.AddDataStoreItem(server_connection_file_full_path, 
                       datastore_type, 
                       conn_name, 
                       db_connection_file_full_path, 
                       db_connection_file_full_path)

参考

https://pro.arcgis.com/ja/pro-app/arcpy/functions/adddatastoreitem.htm

mxdファイルのデータソースを修正する

DBサーバーのリプレースに伴い, データソースを修正する必要があった.
GUIでやるのは超めんどくさいのでArcPyで.

下のスクリプトでは念のため編集前のmxdのバックアップを取っている.

import arcpy
import os

base_dir = r'E:\MXD'
backup_dir = os.path.join(base_dir, 'backup')
modified_dir = os.path.join(base_dir, 'modified')
mxd_files = ['a.mxd', 'b.mxd', 'c.mxd', 'd.mxd', 'e.mxd']
old_sde = r'C:\Users\someone\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\old_db.sde'
new_sde = r'C:\Users\someone\AppData\Roaming\ESRI\Desktop10.5\ArcCatalog\db.sde'

for f in mxd_files:
    file_full_path = os.path.join(c.base_dir, f)
    # バックアップを取る
    shutil.copy(file_full_path, backup_dir)
    # mxdファイルを開いてデータソースを付け替える
    mxd = arcpy.mapping.MapDocument(file_full_path)
    mxd.findAndReplaceWorkspacePaths(old_sde, new_sde)
    # mxdファイルを別ファイルとして保存する
    modified_file_path = os.path.join(modified_dir, f)
    mxd.saveACopy(modified_file_path)
    del mxd

参考

http://desktop.arcgis.com/ja/arcmap/10.3/analyze/arcpy-mapping/updatingandfixingdatasources.htm

ArcGIS Serverのサービスを公開する

いちいち1個ずつmxdファイル開いてサービスを公開するのがいやになったのでArcPyで自動化した.

デフォルトで公開する

デフォルトの設定でサービスを公開する場合は以下の通り.
流れは「分析してドラフトサービスを作成する→ドラフトサービスからサービス定義を作成する→サービス定義からサービスを公開する」って感じ.

import arcpy
import os

mxd_dir = r'E:\MXD'
mxd_files = ['a.mxd', 'b.mxd', 'c.mxd', 'd.mxd', 'e.mxd']
sddraft_dir = r'E:\MXD\SDDRAFT'
sd_dir = r'E:\MXD\SD'
server_type = 'ARCGIS_SERVER'
server_connection_file_full_path = r'C:\Users\someone\AppData\Roaming\ESRI\Desktop10.5\ArcCatalog\arcgisserverconn.ags'

for mxd_file in mxd_files:
    mapDoc = arcpy.mapping.MapDocument(os.path.join(c.mxd_dir, mxd_file))
    service_name = mxd_file.split('.mxd')[0]
    sddraft = os.path.join(sddraft_dir, '{0}.sddraft'.format(service_name))
    sd = os.path.join(sd_dir, '{0}.sd'.format(service_name))
    summary = ''
    tags = ''

    # 分析してドラフトサービスを作成する.
    analysis = arcpy.mapping.CreateMapSDDraft(mapDoc, 
                                              sddraft, 
                                              service_name, 
                                              server_type,
                                              server_connection_file_full_path, 
                                              True, 
                                              None, 
                                              summary, 
                                              tags)

    # 分析結果にエラーが無ければサービスを公開する.
    if analysis['errors'] == {}:
        # ドラフトサービスからサービス定義を作成する.
        arcpy.StageService_server(sddraft, sd)
        # サービス定義からサービスを公開する.
        arcpy.UploadServiceDefinition_server(sd, server_connection_file_full_path)
    else: 
        # 分析結果にエラーがあったら表示する.
        print(analysis['errors'])

ドラフトサービスを修正してサービスを公開する

例えばKMLを無効にしてFeature Accessを有効にする場合は以下の通り.
ドラフトサービスを頑張って編集する感じ.

import arcpy
import os
import codecs
import xml.dom.minidom as DOM

mxd_dir = r'E:\MXD'
mxd_files = ['a.mxd', 'b.mxd', 'c.mxd', 'd.mxd', 'e.mxd']
sddraft_dir = r'E:\MXD\SDDRAFT'
sd_dir = r'E:\MXD\SD'
server_type = 'ARCGIS_SERVER'
server_connection_file_full_path = r'C:\Users\someone\AppData\Roaming\ESRI\Desktop10.5\ArcCatalog\arcgisserverconn.ags'

for mxd_file in mxd_files:
    mapDoc = arcpy.mapping.MapDocument(os.path.join(c.mxd_dir, mxd_file))
    service_name = mxd_file.split('.mxd')[0]
    sddraft = os.path.join(sddraft_dir, '{0}.sddraft'.format(service_name))
    sd = os.path.join(sd_dir, '{0}.sd'.format(service_name))
    summary = ''
    tags = ''

    # 分析してドラフトサービスを作成する.
    analysis = arcpy.mapping.CreateMapSDDraft(mapDoc, 
                                              sddraft, 
                                              service_name, 
                                              server_type,
                                              server_connection_file_full_path, 
                                              True, 
                                              None, 
                                              summary, 
                                              tags)

    # 分析結果にエラーが無ければサービスを公開する.
    if analysis['errors'] == {}:
        # sddraftを読み込む.
        doc = DOM.parse(sddraft)
        # kmlを無効にする.
        services___ = doc.getElementsByTagName('TypeName')
        for service__ in services___:
            if service__.firstChild.data == 'KmlServer':
                service__.parentNode.getElementsByTagName('Enabled')[0].firstChild.data = 'false'
        # Feature Accessを有効にする.
        for service__ in services___:
            if service__.firstChild.data == 'FeatureServer':
                service__.parentNode.getElementsByTagName('Enabled')[0].firstChild.data = 'true'
        # 保存する.
        if os.path.exists(sddraft):
            os.remove(sddraft)
        f = codecs.open(sddraft,"w",'utf-8')
        doc.writexml(f, '', '\t','','utf-8')
        f.close()

        # ドラフトサービスからサービス定義を作成する.
        arcpy.StageService_server(sddraft, sd)
        # サービス定義からサービスを公開する.
        arcpy.UploadServiceDefinition_server(sd, server_connection_file_full_path)
    else: 
        # 分析結果にエラーがあったら表示する.
        print(analysis['errors'])

参考

https://gis.stackexchange.com/questions/91480/specifying-map-service-parameters-when-publishing-with-arcpy