功能:基于矢量图层或基于数据框范围+当前比例尺或选择影像分辨率来生成与范围相交的瓦片格网图层
效果:
代码:
# -*- 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