凡例がデータフレーム内の機能をインターセプトする場合、プログラムで(アークピー)凡例を移動する方法を見つけようとします。以下のシナリオで、凡例がAOIのビューを覆い隠している場合、別のコーナーに移動して、問題。これは、データフレームを小さくして横に置くのではなく、データフレームの上に配置する必要があります。
凡例がデータフレーム内の機能をインターセプトする場合、プログラムで(アークピー)凡例を移動する方法を見つけようとします。以下のシナリオで、凡例がAOIのビューを覆い隠している場合、別のコーナーに移動して、問題。これは、データフレームを小さくして横に置くのではなく、データフレームの上に配置する必要があります。
回答:
import arcpy, traceback, os, sys, time
from arcpy import env
import numpy as np
env.overwriteOutput = True
outFolder=arcpy.GetParameterAsText(0)
env.workspace = outFolder
dpi=2000
tempf=r'in_memory\many'
sj=r'in_memory\sj'
## ERROR HANDLING
def showPyMessage():
arcpy.AddMessage(str(time.ctime()) + " - " + message)
try:
mxd = arcpy.mapping.MapDocument("CURRENT")
allLayers=arcpy.mapping.ListLayers(mxd,"*")
ddp = mxd.dataDrivenPages
df = arcpy.mapping.ListDataFrames(mxd)[0]
SR = df.spatialReference
## GET LEGEND ELEMENT
legendElm = arcpy.mapping.ListLayoutElements(mxd, "LEGEND_ELEMENT", "myLegend")[0]
# GET PAGES INFO
thePagesLayer = arcpy.mapping.ListLayers(mxd,ddp.indexLayer.name)[0]
fld = ddp.pageNameField.name
# SHUFFLE THROUGH PAGES
for pageID in range(1, ddp.pageCount+1):
ddp.currentPageID = pageID
aPage=ddp.pageRow.getValue(fld)
arcpy.RefreshActiveView()
## DEFINE WIDTH OF legend IN MAP UNITS..
E=df.extent
xmin=df.elementPositionX;xmax=xmin+df.elementWidth
x=[xmin,xmax];y=[E.XMin,E.XMax]
aX,bX=np.polyfit(x, y, 1)
w=aX*legendElm.elementWidth
## and COMPUTE NUMBER OF ROWS FOR FISHNET
nRows=(E.XMax-E.XMin)//w
## DEFINE HEIGHT OF legend IN MAP UNITS
ymin=df.elementPositionY;ymax=ymin+df.elementHeight
x=[ymin,ymax];y=[E.YMin,E.YMax]
aY,bY=np.polyfit(x, y, 1)
h=aY*legendElm.elementHeight
## and COMPUTE NUMBER OF COLUMNS FOR FISHNET
nCols=(E.YMax-E.YMin)//h
## CREATE FISHNET WITH SLIGHTLY BIGGER CELLS (due to different aspect ratio between legend and dataframe)
origPoint='%s %s' %(E.XMin,E.YMin)
yPoint='%s %s' %(E.XMin,E.YMax)
endPoint='%s %s' %(E.XMax,E.YMax)
arcpy.CreateFishnet_management(tempf, origPoint,yPoint,
"0", "0", nCols, nRows,endPoint,
"NO_LABELS", "", "POLYGON")
arcpy.DefineProjection_management(tempf, SR)
## CHECK CORNER CELLS ONLY
arcpy.SpatialJoin_analysis(tempf, tempf, sj, "JOIN_ONE_TO_ONE",
match_option="SHARE_A_LINE_SEGMENT_WITH")
nCorners=0
with arcpy.da.SearchCursor(sj, ("Shape@","Join_Count")) as cursor:
for shp, neighbours in cursor:
if neighbours!=3:continue
nCorners+=1; N=0
for lyr in allLayers:
if not lyr.visible:continue
if lyr.isGroupLayer:continue
if not lyr.isFeatureLayer:continue
## CHECK IF THERE ARE FEATURES INSIDE CORNER CELL
arcpy.Clip_analysis(lyr, shp, tempf)
result=arcpy.GetCount_management(tempf)
n=int(result.getOutput(0))
N+=n
if n>0: break
## IF NONE, CELL FOUND; COMPUTE PAGE COORDINATES FOR LEGEND AND BREAK
if N==0:
tempRaster=outFolder+os.sep+aPage+".png"
e=shp.extent;X=e.XMin;Y=e.YMin
x=(X-bX)/aX;y=(Y-bY)/aY
break
if nCorners==0: N=1
## IF NO CELL FOUND PLACE LEGEND OUTSIDE DATAFRAME
if N>0:
x=df.elementPositionX+df.elementWidth
y=df.elementPositionY
legendElm.elementPositionY=y
legendElm.elementPositionX=x
outFile=outFolder+os.sep+aPage+".png"
arcpy.AddMessage(outFile)
arcpy.mapping.ExportToPNG(mxd,outFile)
except:
message = "\n*** PYTHON ERRORS *** "; showPyMessage()
message = "Python Traceback Info: " + traceback.format_tb(sys.exc_info()[2])[0]; showPyMessage()
message = "Python Error Info: " + str(sys.exc_type)+ ": " + str(sys.exc_value) + "\n"; showPyMessage()
注:データドリブンページの各ページについて、スクリプトはデータフレームコーナーに十分なスペースを見つけて、表示されているフィーチャレイヤーを覆わずに凡例(myLegendと呼ばれます)を配置しようとします。スクリプトはフィッシュネットを使用してコーナーセルを識別します。セルの寸法は、データビュー単位での凡例の寸法よりわずかに大きくなっています。コーナーセルは、3つの隣接セルと境界を共有するセルです。コーナーや部屋が見つからない場合、凡例はレイアウトページのデータフレームの外側に配置されました。
残念ながら、ページ定義クエリの管理方法がわかりません。示されているポイントは、元々はRECTANGLE範囲全体に散らばっていましたが、ページと関連付けられていないものもありました。Arcpyには引き続きレイヤー全体が表示されますが、ポイントに定義クエリ(一致)を適用しました。
これを行う方法は、凡例要素をそれらのフィーチャと同じ座標系で表す「凡例要素」フィーチャクラスを作成することです。
これにより、[レイヤーを場所で選択]を使用して、凡例要素がフィーチャと重複しているかどうかをテストし、重複している場合は移動できます。
その重要なことではありませんが、非常に実行可能であり、このサイトにQ&Aがあります(ポイントXYをarcpyを使用してページ単位XYに変換しますか?)。
以下は、データを不明瞭にしないように、凡例とインセットマップを移動するために使用したコードです。別のスレッドでの交差チェック機能について質問しました。これは他の誰かのコードの私の実装です。どこから来たのか正確には思い出せません。私が思うに、ニューイングランドのある州の差し込みマップを移動するスクリプトでした。
insetは、凡例またはインセットマップ要素のハンドルです。
#check intersect function
def checkIntersect(MovableObject):
#get absolute x and y disatnce of MovableObject in page units
PageOriginDistX = (inset.elementPositionX + inset.elementWidth) - DataFrame.elementPositionX #Xmax in page units
PageOriginDistY = (inset.elementPositionY + inset.elementHeight) - DataFrame.elementPositionY #absolute y disatnce of element
#Generate x/y pairs for new tempfile used to test intersection of original MovableObject placement
Xmax = DataFrame.extent.XMin + ((DataFrame.extent.XMax - DataFrame.extent.XMin) *
(PageOriginDistX / DataFrame.elementWidth))
Xmin = DataFrame.extent.XMin + ((DataFrame.extent.XMax - DataFrame.extent.XMin) *
((inset.elementPositionX - DataFrame.elementPositionX) / DataFrame.elementWidth))
Ymax = DataFrame.extent.YMin + ((DataFrame.extent.YMax - DataFrame.extent.YMin) *
(PageOriginDistY / DataFrame.elementHeight))
Ymin = DataFrame.extent.YMin + ((DataFrame.extent.YMax - DataFrame.extent.YMin) *
((inset.elementPositionY - DataFrame.elementPositionY) / DataFrame.elementHeight))
#list of coords for temp polygon
coordList = [[[Xmax,Ymax], [Xmax,Ymin], [Xmin,Ymin], [Xmin,Ymax]]]
#create empty temp poly as tempShape, give it a spatial ref, make it into a featureclass so it works
#with intersect
tempShape = os.path.join(sys.path[0], "temp.shp")
arcpy.CreateFeatureclass_management(sys.path[0], "temp.shp","POLYGON")
array = arcpy.Array()
point = arcpy.Point()
featureList = []
arcpy.env.overwriteOutput = True
for feature in coordList:
for coordPair in feature:
point.X = coordPair[0]
point.Y = coordPair[1]
array.add(point)
array.add(array.getObject(0))
polygon = arcpy.Polygon(array)
array.removeAll()
featureList.append(polygon)
arcpy.CopyFeatures_management(featureList, tempShape)
arcpy.MakeFeatureLayer_management(tempShape, "tempShape_lyr")
#check for intersect
arcpy.SelectLayerByLocation_management("unobscured_lyr", "INTERSECT", "tempShape_lyr", "", "NEW_SELECTION")
#initiate search and count
polyCursor = arcpy.SearchCursor("unobscured_lyr")
polyRow = polyCursor.next()
count = 0
#Clear Selection
arcpy.SelectLayerByAttribute_management("unobscured_lyr","CLEAR_SELECTION")
#Delete the temporary shapefile.
arcpy.Delete_management(tempShape)
#count
while polyRow:
count = count + 1
polyRow = polyCursor.next()
#Clear Selection
arcpy.SelectLayerByAttribute_management("unobscured_lyr","CLEAR_SELECTION")
#Delete the temporary shapefile.
arcpy.Delete_management(tempShape)
#Return the count value to main part of script to determine placement of locator map.
return count
次に、この投稿からの以下のコード(Movable Legend / Inset Mapを使用したデータドリブンページ)は、より理にかなっているはずです。
for pageNum in range(1, mxd.dataDrivenPages.pageCount + 1):
#setup naming and path for output maps
path = mxd.filePath
bn = os.path.basename(path)[:-4]
mxd.dataDrivenPages.currentPageID = pageNum
insetDefaultX = inset.elementPositionX
insetDefaultY = inset.elementPositionY
#check defualt position for intersect
intersect = checkIntersect(inset)
if intersect == 0: #if it doesn't intersect, print the map
arcpy.mapping.ExportToEPS(mxd, exportFolder + "\\" + bn + "_"+ str(pageNum) + ".eps", "Page_Layout",640,480,300,"BETTER","RGB",3,"ADAPTIVE","RASTERIZE_BITMAP",True,False)
else: #intersect != 0: #move inset to SE corner
inset.elementPositionX = (DataFrame.elementPositionX + DataFrame.elementWidth) - inset.elementWidth
inset.elementPositionY = DataFrame.elementPositionY