2022-04-22 Arcpy开发のArcMap插件开发

功能:基于矢量图层或基于数据框范围+当前比例尺或选择影像分辨率来生成与范围相交的瓦片格网图层
效果:


image.png

代码:


# -*- coding: utf-8 -*
import arcpy
import math
import pythonaddins

#全局变量,地图缩放层级
level = -1
def setLevel(zoom):
    global level 
    level = zoom

def getLevel():
    global level
    return level

#全局变量,参考矢量图层
refLayer = ""
def setRefLayer(name):
    global refLayer 
    refLayer = name

def getRefLayer():
    global refLayer
    return refLayer

class Envelope(object):
    def __init__(self,minLat,maxLat,minLon,maxLon):
        self.minLat = minLat
        self.maxLat = maxLat
        self.minLon = minLon
        self.maxLon = maxLon

#经纬度坐标转瓦片坐标(瓦片坐标是平面的)
def deg2tile(lat_deg, lon_deg, zoom):
  lat_rad = math.radians(lat_deg)
  n = 2.0 ** zoom
  xtile = int((lon_deg + 180.0) / 360.0 * n)
  ytile = int((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n)
  return (xtile, ytile)

#瓦片坐标转经纬度
def tile2deg(xtile, ytile, zoom):
  n = 2.0 ** zoom
  lon_deg = xtile / n * 360.0 - 180.0
  lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
  lat_deg = math.degrees(lat_rad)
  return (lat_deg, lon_deg)

#瓦片坐标转范围
def tile2Envelope(xtile, ytile, zoom):
   maxLat,minLon = tile2deg(xtile,ytile,zoom)
   minLat,maxLon = tile2deg(xtile+1,ytile+1,zoom)
   return (minLon,maxLon,minLat,maxLat)   

#范围+缩放级别求瓦片
def envelope2XYRange(minLon,minLat,maxLon,maxLat,zoom):
    minTileX,maxTileY = deg2tile(minLat,minLon,zoom)
    maxTileX,minTileY = deg2tile(maxLat,maxLon,zoom)
    return (maxTileY,maxTileX,minTileX,minTileY)

# 基于范围(经纬度的)+ 缩放级别计算瓦片的张数
def getTileNumbers(minLon,minLat,maxLon,maxLat,zoom):
    maxTileY,maxTileX,minTileX,minTileY=envelope2XYRange(minLon,minLat,maxLon,maxLat,zoom)
    count = 0
    for y in range(minTileY,maxTileY+1):
        for x in range(minTileX,maxTileX+1):
            count = count+1
    return count

# 基于范围(经纬度)+ 缩放级别生成瓦片格网shp文件
def envelope2TileGridLayer(envelope,zoom):
    refLayer = getRefLayer()
    if refLayer == "":
        return 0
    if envelope.minLon < -180.0:
        return 0
    if envelope.maxLon > 180.0:
        return 0
    if envelope.minLat < -87.0:
        return 0
    if envelope.maxLat > 87.0:
        return 0
    maxTileY,maxTileX,minTileX,minTileY=envelope2XYRange(envelope.minLon,envelope.minLat,
    envelope.maxLon,envelope.maxLat,zoom)
    if arcpy.Exists(r'in_memory\grid'):
            arcpy.Delete_management(r'in_memory\grid')
    srf = arcpy.SpatialReference(4326)
    arcpy.CreateFeatureclass_management("in_memory","grid","POLYLINE","","","",srf)
    arcpy.AddField_management("grid","name","TEXT",35)
    cursor = arcpy.InsertCursor("grid")
    index = 0
    for y in range(minTileY,maxTileY+1):
        for x in range(minTileX,maxTileX+1):
            if index > 200000:
              pythonaddins.MessageBox("the number of tiles is greater than 20000, please reduce the layer extent or zoom value!","Tile Numbers",0)
              return index
            index = index+1
    index = 0
    for y in range(minTileY,maxTileY+1):
        for x in range(minTileX,maxTileX+1):
            index = index + 1
            minLon,maxLon,minLat,maxLat = tile2Envelope(x,y,zoom)
            coordinates = arcpy.Array()
            # 左上
            leftTopPoint = arcpy.Point()
            leftTopPoint.X = minLon
            leftTopPoint.Y = maxLat
            coordinates.add(leftTopPoint)
            # 左下
            leftBottomPoint = arcpy.Point()
            leftBottomPoint.X = minLon 
            leftBottomPoint.Y = minLat
            coordinates.add(leftBottomPoint)
            # 右下
            rightBottomPoint = arcpy.Point()
            rightBottomPoint.X = maxLon 
            rightBottomPoint.Y = minLat
            coordinates.add(rightBottomPoint)
            # 右上
            rightTopPoint = arcpy.Point()
            rightTopPoint.X = maxLon
            rightTopPoint.Y = maxLat
            coordinates.add(rightTopPoint)
            # 最后回到起点
            coordinates.add(leftTopPoint)
            # 构建闭合线
            polyline = arcpy.Polyline(coordinates)
            row = cursor.newRow()
            # 将线(几何)对下付给图层中的单个要素
            row.shape = polyline
            row.name = str(x)+"_"+str(y)+"_"+str(zoom)+"_XYZ"
            # 插入该要素
            cursor.insertRow(row)              
    #上面循环完后,完整的瓦片格网图层是ok的,下面求相交的部分的图层单独copy出来      
    selection = arcpy.SelectLayerByLocation_management("grid","INTERSECT",refLayer)
    #如果相交部分不为空的话
    if selection:
        #如果图层存在的话,删除他
        if arcpy.Exists(r'in_memory\grid_intersect'):
           arcpy.Delete_management(r'in_memory\grid_intersect')
        #创建一个线的矢量图层
        arcpy.CreateFeatureclass_management("in_memory","grid_intersect","POLYLINE","","","",srf)
        #添加字段
        arcpy.AddField_management("grid_intersect","name","TEXT",35)
        #获取新建图层的插入游标
        newCursor = arcpy.InsertCursor("grid_intersect")
        #获取相交部分图层的查询游标
        cursor = arcpy.SearchCursor(selection)
        for row in cursor:
            newRow = newCursor.newRow()
            newRow.name = row.name
            newRow.shape = row.shape
            newCursor.insertRow(newRow)
        #将相交部分的要素复制到新图层上去
        # arcpy.CopyFeatures_management(selection,r'in_memory\grid_intersect')   
    # grid图层关闭
    mxd = arcpy.mapping.MapDocument("current")
    allLayers = arcpy.mapping.ListLayers(mxd)
    for ly in allLayers:
        if ly.name == 'grid':
            ly.visible = False
    arcpy.RefreshTOC()
    arcpy.RefreshActiveView()       
    return index

# arcmap比例尺转换成对应的地图缩放层级
def scale2Zoom(scale):
    zoom = 1
    if  147914677.73 < scale <= 295829355.45:
        zoom = 1
    elif 73957338.86 < scale <= 147914677.73:
        zoom = 2
    elif 36978669.43 < scale <= 73957338.86:
        zoom = 3
    elif 18489334.72 < scale <= 36978669.43:
        zoom = 4
    elif 9244667.36 < scale <= 18489334.72:
        zoom = 5
    elif 4622333.68 < scale <= 9244667.36:
        zoom = 6
    elif 2311166.84 < scale <= 4622333.68:
        zoom = 7
    elif 1155583.42 < scale <= 2311166.84:
        zoom = 9
    elif 288895.85 < scale <= 1155583.42:
        zoom = 10
    elif 144447.93 < scale <= 288895.85:
        zoom = 11
    elif 72223.96 < scale <= 144447.93:
        zoom = 12
    elif 36111.98 < scale <= 72223.96:
        zoom = 13
    elif 18055.99 < scale <= 36111.98:
        zoom = 14
    elif 9028.0 < scale <= 18055.99:
        zoom = 15
    elif 4514.0 < scale <= 9028.0:
        zoom = 16
    elif 2257.0 < scale <= 4514.0:
        zoom = 17
    elif 1128.50 < scale <= 2257.0:
        zoom = 18
    elif 564.25 < scale <= 1128.50:
        zoom = 19
    elif scale <= 564.25:
        zoom = 20
    else:
        zoom = 0
    return zoom

#瓦片构建规则
class BuildRule(object):
    """Implementation for tilegrid_addin.combobox_1 (ComboBox)"""
    def __init__(self):
       self.items = ["none","16m","2m", "0.8m"]
       self.editable = True
       self.enabled = True
       self.dropdownWidth = 'WWWWWWWWWW'
       self.width = 'WWWWWW'
    def onSelChange(self, selection):
        pass
    def onEditChange(self, text):
        if text == "16m":
            setLevel(8)
        elif text == "2m":
            setLevel(14)
        elif text == "0.8m":
            setLevel(17)
        elif text == "none":
            setLevel(-1)
    def refresh(self):
        pass

#生成有效的瓦片格网
class Generate(object):
    """Implementation for tilegrid_addin.button (Button)"""
    def __init__(self):
        self.enabled = True
        self.checked = False
    def onClick(self):
        refLayer = getRefLayer()
        if refLayer == "":
            pythonaddins.MessageBox("Please select a vector layer for tile's extent update!","Layer Select",0)
            return
        mxd = arcpy.mapping.MapDocument("current")
        # 获取当前地图文档(工程)中的第一个数据框
        df = arcpy.mapping.ListDataFrames(mxd)[0]
        zoom = -1
        dfExtent = None
        if df:
            scale = df.scale
            zoom = scale2Zoom(scale)
            dfExtent = df.extent
        if zoom > -1 and dfExtent:
            minY = dfExtent.YMin
            maxY = dfExtent.YMax
            minX = dfExtent.XMin
            maxX = dfExtent.XMax
            envelope = Envelope(minY,maxY,minX,maxX)
            level = getLevel()
            layers =arcpy.mapping.ListLayers(mxd)
            for layer in layers:
                name = layer.name
                if name == refLayer and level > 0:
                    layerExtent = layer.getExtent()
                    minY = layerExtent.YMin
                    maxY = layerExtent.YMax
                    minX = layerExtent.XMin
                    maxX = layerExtent.XMax
                    envelope = Envelope(minY,maxY,minX,maxX)
                    #如果选择指定层级,则生成的瓦片范围按选择的矢量图层计算
                    return envelope2TileGridLayer(envelope,level)
            #如果没有选择指定层级,则生成的瓦片范围按当前数据框视口范围定
            envelope2TileGridLayer(envelope,zoom)   
            

#选择切片范围
class SelectBoundary(object):
    """Implementation for tilegrid_addin.combobox (ComboBox)"""
    def __init__(self):
        self.editable = True
        self.enabled = True
        #下拉框的宽度
        self.dropdownWidth = 'wwwwwwwwwwwwwwwww'
        #框的宽度
        self.width = 'wwwwwwwwwwww'
    def onSelChange(self, selection):
        pass
    def onEditChange(self, text):
        setRefLayer(text)
    def onFocus(self, focused):
        if focused:
            self.mxd = arcpy.mapping.MapDocument('current')
            layers = arcpy.mapping.ListLayers(self.mxd)
            self.items=[]
            for layer in layers:
                layerName = layer.name
                if layerName == "grid" or layerName == "grid_intersect":
                    continue
                if layer.isFeatureLayer:
                    if(arcpy.Describe(layer).shapeType == "Polygon" or arcpy.Describe(layer).shapeType == "Polyline"):
                        self.items.append(layerName)
    def onEnter(self):
        pass
    def refresh(self):
        pass

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容