Pindahkan legenda jika tumpang tindih fitur dalam dataframe menggunakan ArcPy


8

Mencoba menemukan cara pemrograman (arcpy) memindahkan legenda jika ia memotong fitur dalam bingkai data, dalam skenario di bawah ini, jika legenda mengaburkan pandangan AOI, maka saya ingin itu pindah ke sudut yang berbeda sampai bukan isu. Ini harus di atas bingkai data yang bertentangan dengan membuat bingkai data lebih kecil dan hanya meletakkannya di samping.

masukkan deskripsi gambar di sini


1
Jika Anda menggunakan Data Driven Pages, Anda mungkin menemukan bantuan dalam hal ini: gis.stackexchange.com/questions/167975/… . Secara umum saya akan mencari di Google pada sesuatu seperti "pindahkan legenda di Data Driven Pages" untuk beberapa saran lagi. Dengan legenda tetap, saya telah mengonversikannya ke gambar dan menggunakan yang berikut untuk memindahkannya: support.esri.com/en/technical-article/000011951 Tidak satu pun dari ini adalah jawaban, hanya solusi.
hidung belang

Ya saya sedang menggunakan halaman data didorong, terima kasih untuk tautan Johns
Slevy

Jawaban:


7

Input: masukkan deskripsi gambar di sini Script:

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

KELUARAN: masukkan deskripsi gambar di sini

CATATAN: Untuk setiap halaman dalam skrip halaman yang didorong data, upaya untuk menemukan ruang yang cukup di sudut bingkai data untuk menempatkan Legend (disebut myLegend) tanpa mencakup lapisan fitur apa pun yang terlihat. Script menggunakan fishnet untuk mengidentifikasi sel sudut. Dimensi sel sedikit lebih besar dari dimensi Legenda dalam unit tampilan data. Sel sudut adalah sel yang memiliki batas dengan 3 tetangga. Jika tidak ada sudut atau ruang ditemukan, Legenda ditempatkan di luar kerangka data pada halaman tata letak.

Sayangnya saya tidak tahu bagaimana mengelola permintaan definisi halaman. Poin yang ditampilkan pada awalnya tersebar di sekitar tingkat RECTANGLE, dengan beberapa dari mereka tidak memiliki hubungan dengan halaman. Arcpy masih melihat seluruh lapisan, meskipun saya menerapkan kueri definisi (kecocokan) ke poin.


Terima kasih atas tulisan hebatnya pada Felix ini, walaupun saya mengalami masalah dalam mengimplementasikan solusi ini agar bekerja dengan lancar seperti contoh Anda, sedetail ini, apakah ada sesuatu yang harus saya waspadai saat membuat dokumen peta, titik jangkar legenda, dll. ?
Slevy

1
Titik jangkar berada di kiri bawah untuk legenda dan bingkai data. Bagaimana saya bisa melupakan ini?
FelixIP

Yap, pasti membuat perbedaan dalam tes di sini. Jika saya ingin mengganti anchor point ke tengah (untuk dataframe), saya mengasumsikan seluruh logikanya rusak? bagian mana yang harus saya konfigurasi. Hanya baris 33 hingga 44?
Slevy

1
Hitung xmin dan xmax melalui lebar dan posisi x. Mirip dengan sumbu y. Tidak yakin mengapa Anda membutuhkannya ...
FelixIP

Bagian dari alur kerja lain, terima kasih Felix, langkah maju yang bagus di sini
Slevy

3

Cara yang akan saya lakukan ini adalah dengan membuat kelas fitur "elemen legenda" yang mewakili elemen legenda Anda dalam sistem koordinat yang sama dengan fitur-fitur tersebut.

Dengan begitu Anda bisa menggunakan Select Layer By Location untuk menguji apakah elemen legenda Anda tumpang tindih dengan fitur apa pun, dan memindahkannya jika ada.

Ini non-sepele tetapi sangat bisa dilakukan dan ada T&J di situs ini ( Konversi titik XY ke unit halaman XY menggunakan arcpy? ) Yang dapat digunakan untuk mengerjakan bagian tersulit dari konversi antara koordinat halaman dan peta.


1
Bagian tersulit adalah menemukan celah yang cukup besar agar sesuai dengan kotak legenda.
FelixIP

1
@ Feliks Mengapa demikian? Sepertinya si penanya membatasi diri hanya menguji empat sudut bingkai data. Saya berasumsi mereka memiliki aturan untuk apa yang terjadi jika tidak ada sudut yang cocok.
PolyGeo

Saya pikir ini adalah cara untuk pergi, meskipun kesenjangan dalam legenda mungkin akan menjadi masalah saya yang paling sedikit. Idealnya, skala peta akan terus berubah sampai legenda tidak mencegat poligon yang menarik. Meskipun ingin mendengar atau melihat beberapa contoh praktis yang telah dicoba orang!
Slevy

2

Di bawah ini adalah kode yang saya gunakan untuk memindahkan legenda dan inset peta agar tidak mengaburkan data. Anda bertanya tentang fungsi periksa perpotongan pada utas lainnya. Ini adalah implementasi kode orang lain. Saya tidak ingat persis dari mana asalnya. Itu adalah naskah untuk memindahkan peta inset untuk negara bagian di New England, kurasa.

inset adalah pegangan untuk elemen legenda atau inset map.

#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

Kemudian, kode di bawah ini dari pos ini ( Halaman Data Didorong dengan Legenda Bergerak / Inset ) harus lebih masuk akal.

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
harus menyebutkan: dalam contoh elemen ini berlabuh di kiri bawah.
CSB

Terima kasih CSB, Ya untuk kasus saya, saya perlu bingkai data untuk berlabuh di tengah, jadi saya hanya dalam proses menyesuaikan formula luaran Page Origin Anda, saya akan memposting contoh setelah saya sampai di sana. Kalau tidak, tampak sangat menjanjikan dalam pengujian awal. Juga, ada referensi ke "unobscured_lyr", dengan asumsi ini dirujuk di luar skrip sebagai lapisan yang harus dihindari?
Slevy

benar, "unobscured_lyr" adalah yang kami coba tidak bahas. tentu saja, Anda bisa membuatnya bekerja dengan banyak layer juga.
CSB
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.