利用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()

下载源码

转载自:https://blog.csdn.net/dahongdahong/article/details/51447680

You may also like...