1. 程式人生 > >利用Arcpy釋出地圖服務,製作切片

利用Arcpy釋出地圖服務,製作切片

  因為工作需求,我需要把管理員上傳的圖片進行切片,供使用者下載切片離線瀏覽。後臺切片功能主要分為以下幾個步驟:

1. 製作地圖文件(*.mxd);
2. 釋出地圖文件;
3. 製作伺服器快取;
4. 生成切片;
5. 打包成zip

  ArcGIS版本是10.2,下面這些程式碼應該在10.0上無法執行。生成mxd文件時不支援柵格資料,我的圖片大都是jpeg格式的,所以最終我更換了ArcGIS版本,妥協了…

Arcpy製作地圖文件

  我沒有找打直接生成地圖文件的示例程式碼,因此我使用了SaveAs的方法,就是利用已有的空白文件(我稱之為“模板”),載入上jpg圖片後再“另存為”。這樣的一個好處就是我可以直接在模板中定義地理座標系,後面再發布為地圖服務時會用到的,否則報錯。

    # 建立map document
    def CreateMxd(imagepath,mxdpath):
        dirname=os.path.dirname(imagepath)
        imagename=os.path.basename(imagepath)
        dotindex=imagename.index('.')
        name=imagename[0:dotindex]
        new_mxd=os.path.abspath(dirname+"/"+name+".mxd")
        rasterLayer="raster"
temp_mxd = arcpy.mapping.MapDocument(mxdpath) df=arcpy.mapping.ListDataFrames(temp_mxd,"Layers")[0] arcpy.MakeRasterLayer_management(imagepath,rasterLayer,"","","") addLayer=arcpy.mapping.Layer(rasterLayer) arcpy.mapping.AddLayer(df,addLayer,"TOP") temp_mxd.saveACopy
(new_mxd) del temp_mxd return new_mxd

  這個函式的引數是“圖片的位置”和“模板的位置”,然後將圖片的名稱提取出來作為新mxd檔案的名稱。

  需要注意的是,直接新增jpg格式的圖層在ArcGIS10.2中是允許的,但生成的mxd文件裡面的圖層樣式是採用“拉伸(Stretched)”方式展示的,圖片的樣式直接成為灰色調了,而我需要的是“RGB合成(RGB Composite)”。因此我使用了這種“先建立臨時柵格圖層,再新增到mxd”的方式。
  使用RGB合成

釋出地圖文件

  我們都知道,在ArcGIS10.1之後都是先將mxd文件轉為sd文件,再進行釋出的。這樣做也是有好處的(聽說是),不再贅述。官網文件給出兩種方式釋出地圖,一是需要ArcGIS Server伺服器名、使用者名稱和密碼方式驗證登入,二是採用arcgis server連線檔案ags的方式直接驗證。我採用了後者,比如我的連線檔案位置在“C:\Users\Administrator\AppData\Roaming\ESRI\Desktop10.2\ArcCatalog\arcgis on WIN-20150327EEH_6080 (admin).ags”。直接引用這個地址作為引數傳入下面這個函式就行。

    # 釋出服務
    def PublishService(mxdpath,agspath):
        new_mxd = arcpy.mapping.MapDocument(mxdpath)
        dirname=os.path.dirname(mxdpath)
        mxdname=os.path.basename(mxdpath)
        dotindex=mxdname.index('.')
        servicename=mxdname[0:dotindex]

        sddraft = os.path.abspath(servicename + '.sddraft')
        sd = os.path.abspath(servicename + '.sd')
        if os.path.exists(sd):
        os.remove(sd)
        #建立服務定義草稿draft
        arcpy.CreateImageSDDraft(new_mxd, sddraft, servicename, 'ARCGIS_SERVER', agspath,False,None, "Ortho Images", "ortho images,image service")

        #分析草稿draft
        analysis = arcpy.mapping.AnalyzeForSD(sddraft)
        #列印分析結果
        print "The following information was returned during analysis of the MXD:"
        for key in ('messages', 'warnings', 'errors'):
        print '----' + key.upper() + '---'
        vars = analysis[key]
        for ((message, code), layerlist) in vars.iteritems():
        print '', message, ' (CODE %i)' % code
        print '   applies to:',
        for layer in layerlist:
        print layer.name,
        print

        # 傳送草稿至伺服器
        if analysis['errors'] == {}:
        # Execute StageService. This creates the service definition.
        arcpy.StageService_server(sddraft, sd)
        # Execute UploadServiceDefinition. This uploads the service definition and publishes the service.
        arcpy.UploadServiceDefinition_server(sd, agspath)
        print "Service successfully published"
        else: 
        print "Service could not be published because errors were found during analysis."

        print arcpy.GetMessages()
        return agspath.replace(".ags","/"+servicename+".MapServer")

  同樣兩個引數,這個函式需要mxd文件路徑和ags檔案路徑作為引數。中間需要先生成服務定義檔案(.sd)和服務定義草稿檔案(.sddraft),然後對sddraft檔案進行分析,若中間沒有出現錯誤,現將其傳送至伺服器。然後再將sd檔案,通過ags檔案傳送至伺服器,更新服務。

  這段程式碼最後我返回了剛剛釋出的服務地址,後面將會用到。

製作伺服器快取

  注意喲,這裡是定義伺服器快取,不是生成切片地方。這裡是定義,下一步才是生成。就好比“開啟存有《名偵探柯南》的雲盤資料夾”,下一步才是“點選播放按鈕”。

    #製作地圖伺服器快取
    def CreateCache(inputService,cachepath):
        # List of input variables for map service properties
        tilingSchemeType = "NEW"
        scalesType = "CUSTOM"
        numOfScales = "5"#此引數無效
        scales = [500000000,250000000,125000000,64000000,32000000]
        dotsPerInch = "96"
        tileOrigin = "0 0"
        tileSize = "256 x 256"
        cacheTileFormat = "PNG8"
        tileCompressionQuality = ""
        storageFormat = "COMPACT"
        predefinedTilingScheme = ""

        try:
            starttime = time.clock()
            result = arcpy.CreateMapServerCache_server(inputService,cachepath,tilingSchemeType, scalesType, numOfScales, dotsPerInch,tileSize, predefinedTilingScheme,tileOrigin, scales,cacheTileFormat,tileCompressionQuality,storageFormat)

            # print messages to a file
            while result.status < 4:
                time.sleep(0.2)
            resultValue = result.getMessages()
            print "completed " + str(resultValue)

        except Exception, e:
            # If an error occurred, print line number and error message
            tb = sys.exc_info()[2]
            print "Failed at step 1 \n" "Line %i" % tb.tb_lineno
            print e.message

        print "Executed creation of Map server Cache schema "

  這裡主要用了CreateMapServerCache_server這個函式,具體函式的意義這裡就不再多說了,完全可以自己去檢視ArcGIS文件。我是使用了自定義的方式切片,可以採用STANDARD方式切片,然後設定numOfScales的值就可以了。

生成切片

  這裡才是生成切片的地方,用到的是Arcpy的ManageMapServerCacheTiles_server函式,同樣具體的引數還是需要自己去查官方文件。幫助文件上面說引數waitForJobCompletion的值為“WAIT”時,可以從其他地方查到具體的切片進度,這裡我還沒去做,看以後的需求吧。

    #生成瓦片
    def CreateTiles(inputService):
        scales = ""
        numOfCachingServiceInstances = 2
        updateMode = "RECREATE_ALL_TILES"
        areaOfInterest = ""
        waitForJobCompletion = "WAIT"
        updateExtents = ""

        try:
            result = arcpy.ManageMapServerCacheTiles_server(inputService, scales,updateMode,numOfCachingServiceInstances,areaOfInterest, updateExtents,waitForJobCompletion)
            #print messages to a file
            while result.status < 4:
                time.sleep(0.2)
            resultValue = result.getMessages()
            print "completed " + str(resultValue)
            print "Created cache tiles for given schema successfully"
        except Exception, e:
            # If an error occurred, print line number and error message
            tb = sys.exc_info()[2]
            print "Failed at step 1 \n" "Line %i" % tb.tb_lineno
            print e.message

        print "Created Map server Cache Tiles "

  這個函式需要上一步返回的引數,就是那個以“*.MapServer”結尾的全路徑。

壓縮切片檔案

  將切片打包主要是為了便於使用者下載,線上預覽的話就不需要這一步了。下面的程式碼是參考了linda1000的博文,有興趣的可以自己前去看看。

  這裡的兩個引數分別是要壓縮的切片路徑,然後是壓縮後的輸出路徑(注意這裡要以*.zip結尾,包括壓縮檔名).

    # MakeZipFile
    def MakeZipFile(filepath,zippath):
        filelist = []
        #Check input ...
        fulldirname = os.path.abspath(filepath)
        fullzipfilename = os.path.abspath(zippath)
        print "Start to zip %s to %s ..." % (fulldirname, fullzipfilename)
        if not os.path.exists(fulldirname):
            print "Dir/File %s is not exist" % fulldirname
            return
        if os.path.isdir(fullzipfilename):
            tmpbasename = os.path.basename(filepath)
            fullzipfilename = os.path.normpath(os.path.join(fullzipfilename, tmpbasename))

        #Get file(s) to zip ...
        if os.path.isfile(filepath):
            filelist.append(filepath)
            filepath = os.path.dirname(filepath)
        else:
            #get all file in directory
            for root, dirlist, files in os.walk(filepath):
                for filename in files:
                    filelist.append(os.path.join(root,filename))

        #Start to zip file ...
        destZip = zipfile.ZipFile(fullzipfilename, "w")
        for eachfile in filelist:
            destfile = eachfile[len(filepath):]
            print "Zip file %s..." % destfile
            destZip.write(eachfile, destfile)
        destZip.close()
        print "Zip folder succeed!"

呼叫

  這段程式碼我是使用Java runtime直接呼叫的,可以使用這種方式一套流程走下來。但也有不大合適的地方,比如瓦片被刪掉了,我只想生成一下切片等等,這樣就不合適了。 

    #********************************************
    #           主      函      數              *
    #********************************************
    # 程式執行所需引數:
    # 1.圖片所在位置(filepath+filename)
    # 2.mxd模板位置(filepath+filename)
    # 3.arcgis server連線檔案(ags)位置
    # 4.生成切片快取位置(out_path)
    # 5.生成壓縮檔案位置(out_path+zipname)
    if __name__ == '__main__':
        #驗證路徑是否正確
        imagepath=sys.argv[1]
        mxdpath=sys.argv[2]
        agspath=sys.argv[3]
        cachepath=sys.argv[4]
        zippath=sys.argv[5]
        if not os.path.isfile(imagepath):
            print "圖片檔案不存在"
            sys.exit()
        if not os.path.isfile(mxdpath):
            print "mxd模板不存在"
            sys.exit()
        if not os.path.isfile(agspath):
            print "arcgis server連線檔案不存在"
            sys.exit()
        if not os.path.isdir(cachepath):
            print "快取目錄不存在"
            sys.exit()
        # 建立mxd
        new_mxd=CreateMxd(imagepath,mxdpath)
        print "Finished CreateMxd"
        # 釋出服務
        inputService=PublishService(new_mxd,agspath)
        print "Finished PublishService"
        # 製作伺服器快取
        CreateCache(inputService,cachepath)
        print "Finished CreateCache"
        # 生成瓦片
        CreateTiles(inputService)
        print "Finished CreateTiles"
        # 壓縮檔案
        tcachepath=os.path.abspath(cachepath+"/"+new_mxd[new_mxd.rindex("\\")+1:new_mxd.rindex(".")])
        print tcachepath
        MakeZipFile(tcachepath,zippath)
        print "Finished MakeZipFile"

更新

  使用過程中,發現無法使得切出更小比例尺的切片,最小的切片也是1:500000000。但當用戶上傳的圖片過大時,切出的圖片無法在檢視內完整的看到全貌。這顯然不合理,最後終於找到了解決方法。草稿檔案上傳至伺服器前對其進行修改,sddraft檔案是指上是一個符號xml標準的檔案。使用python直接可以進行操作,將裡面的minScale改為更大

    #修改草稿draft
    # read sddraft xml
    doc = DOM.parse(sddraft)

    # turn on caching in the configuration properties
    configProps = doc.getElementsByTagName('ConfigurationProperties')[0]
    propArray = configProps.firstChild
    propSets = propArray.childNodes
    for propSet in propSets:
        keyValues = propSet.childNodes
        for keyValue in keyValues:
            if keyValue.tagName == 'Key':
                if keyValue.firstChild.data == "minScale":
                    # turn on caching
                    keyValue.nextSibling.firstChild.data = "32000000000"

    # output to a new sddraft
    if os.path.exists(sddraft): os.remove(sddraft)
    f = open(sddraft, 'w')
    doc.writexml( f )
    f.close()

下載原始碼