凡例がArcPyを使用してデータフレーム内のフィーチャと重複する場合は、凡例を移動します


8

凡例がデータフレーム内の機能をインターセプトする場合、プログラムで(アークピー)凡例を移動する方法を見つけようとします。以下のシナリオで、凡例がAOIのビューを覆い隠している場合、別のコーナーに移動して、問題。これは、データフレームを小さくして横に置くのではなく、データフレームの上に配置する必要があります。

ここに画像の説明を入力してください


1
データドリブンページを使用している場合は、gis.stackexchange.com / questions / 167975 / でいくつかの支援が見つかるかもしれません。より一般的には、「データドリブンページで凡例を移動する」などの方法でGoogleを検索すると、さらにいくつかの提案が得られます。固定伝説で、私は、画像にそれらを変換し、周りにそれらを移動するために、次を使用しました:support.esri.com/en/technical-article/000011951 これらのどれも答え、ただの回避策はありません。
ジョンズ2017

はいイムは、現在、データドリブンページ、リンクジョンズに感謝使って
Slevy

回答:


7

入力: ここに画像の説明を入力してください スクリプト:

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には引き続きレイヤー全体が表示されますが、ポイントに定義クエリ(一致)を適用しました。


このFelixについてのすばらしい記事をありがとうございました。このソリューションを実装して、例のように流動的に機能するように問題を抱えていますが、マップドキュメントや凡例のアンカーポイントなどを作成するときに注意すべき点があります。 ?
Slevy 2017

1
アンカーポイントは、凡例とデータフレームの両方で左下にあります。これをどうやって忘れたの?
FelixIP

はい、間違いなくここでテストに違いをもたらしました。アンカーポイントを中央(データフレーム用)に切り替えたい場合、ロジック全体が正常に機能していないと思いますか?どの部分を構成する必要がありますか。33行目から44行目までですか?
Slevy 2017

1
幅と位置xを通じてxminとxmaxを計算します。y軸と同様。なぜ必要なのかわからない...
FelixIP '18

別のワークフローの一部、Felixに感謝します。ここまで大きく前進しました
Slevy

3

これを行う方法は、凡例要素をそれらのフィーチャと同じ座標系で表す「凡例要素」フィーチャクラスを作成することです。

これにより、[レイヤーを場所で選択]を使用して、凡例要素がフィーチャと重複しているかどうかをテストし、重複している場合は移動できます。

その重要なことではありませんが、非常に実行可能であり、このサイトにQ&Aがあります(ポイントXYをarcpyを使用してページ単位XYに変換しますか?)。


1
最も難しいのは、凡例ボックスに収まるほど大きなギャップを見つけることです。
FelixIP 2017

1
@FelixIPなぜそうなのですか?質問者は、データフレームの4つのコーナーをテストするだけに制限しているようです。私は彼らが適切なコーナーがない場合に何が起こるかについてのルールを持っていると思います。
PolyGeo

伝説のギャップはおそらく私の問題の中で最も少ないでしょうが、私はこれが進むべき道だと思います。理想的には、凡例が対象のポリゴンを遮らないまで、マップの縮尺は変化し続けます。人々が試みたいくつかの実用的な例を聞いたり見たりしたいのですが!
Slevy 2017

2

以下は、データを不明瞭にしないように、凡例とインセットマップを移動するために使用したコードです。別のスレッドでの交差チェック機能について質問しました。これは他の誰かのコードの私の実装です。どこから来たのか正確には思い出せません。私が思うに、ニューイングランドのある州の差し込みマップを移動するスクリプトでした。

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

1
この例では、要素は左下にアンカーされています。
CSB 2017

CSBに感謝します。私のケースでは、データフレームを中央に固定する必要があるので、Page Originのエクステント数式をカスタマイズしている最中なので、そこに到達したらサンプルを投稿します。それ以外の場合は、初期テストで非常に有望に見えます。また、「unobscured_lyr」への参照がありますが、これは回避すべきレイヤーとしてスクリプトの外部で参照されていると想定していますか?
Slevy 2017

正解です。「unobscured_lyr」は、カバーしないようにしています。もちろん、複数のレイヤーで機能させることもできます。
CSB 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.