Impacchettare poligoni all'interno di un poligono usando ArcGIS Desktop?


25

Ho un raster booleano.

Nelle aree grigie del raster vorrei adattare un poligono di una determinata dimensione in una misura contigua.

Fondamentalmente, ho un poligono irregolare e vorrei "adattare" un poligono noto all'interno dell'estensione del poligono irregolare il maggior numero di volte possibile.

La direzione del poligono non ha importanza e potrebbe essere un quadrato. Vorrei che si adattasse graficamente, ma se collegasse un numero al poligono (# che si adatta) funzionerebbe anche.

Sto usando ArcGIS Desktop 10.


8
Questo è un problema molto difficile Ad esempio, ci vuole molto lavoro solo per adattare il maggior numero possibile di cerchi in un quadrato. Quando il poligono originale è complicato, come nell'illustrazione, sono necessarie alcune potenti procedure di ottimizzazione. Il metodo migliore che ho trovato per questo problema è la ricottura simulata, ma non sarà disponibile in ArcGIS e sarebbe necessario uno script estremamente furbo per copiarlo (ArcGIS è troppo lento). Potresti forse rilassare un po 'le tue esigenze, ad esempio adattando il poligono più piccolo un numero di volte sufficiente , anziché quante più volte possibile?
whuber

1
@whuber Grazie per aver modificato il mio post. Sì, un numero sufficiente di volte funzionerebbe. Oppure, che ne dici di un determinato orientamento angolare. ex. nell'immagine sopra, ho adattato il poligono tutte le volte che avrei potuto avere con quell'orientamento, se li avessi ruotati di 90 gradi ne potresti inserire uno in più ...
Thad

1
Sì, ma è anche pieno di insidie. Alcuni sono elementari. Ad esempio, il testo scritto e pubblicato dall'ESRI, "Conoscere ArcView GIS" (per la versione 3) includeva un esercizio in cui un rettangolo che rappresentava un campo da calcio veniva posizionato in modo interattivo all'interno di un poligono. Il problema era che la risposta dell'esercizio era sbagliata perché l'autore non era in grado di proiettare i dati e gli errori nell'uso delle coordinate geografiche erano abbastanza grandi da influenzare il risultato. La risposta sembrava buona nel GIS, ma se qualcuno avesse tentato di costruire quel campo, avrebbe scoperto che non c'era abbastanza spazio per farlo :-).
whuber

6
@whuber Immagino che abbiano pensato che fosse sufficiente una figura "ball park".
Kirk Kuykendall,

2
Nel caso generale del poligono irregolare all'interno del poligono irregolare, questo è un problema computazionalmente intrattabile: trovare una soluzione ottimale non è un obiettivo plausibile in tutti i casi, ed è probabilmente NP-completo da un punto di vista tecnico: quali casi non possono essere predeterminati. Se si vincola il problema in modo significativo, è probabile che alcuni algoritmi iterativi di adattamento casuale forniscano numeri ragionevolmente alti . La mia sensazione se questo è un compito è che non stanno cercando la risposta corretta , stanno cercando approcci creativi.
Mappatura domani

Risposte:


22

Esistono molti modi per affrontare questo problema. Il formato raster dei dati suggerisce un approccio basato su raster; nel rivedere questi approcci, una formulazione del problema come programma lineare intero binario sembra promettente, perché è molto nello spirito di molte analisi di selezione del sito GIS e può essere prontamente adattata a loro.

In questa formulazione, enumeriamo tutte le possibili posizioni e orientamenti dei poligoni di riempimento, che chiamerò "tessere". Associato a ciascuna piastrella è una misura della sua "bontà". L'obiettivo è quello di trovare una collezione di piastrelle non sovrapposte la cui bontà totale sia il più grande possibile. Qui, possiamo prendere la bontà di ogni tessera per essere l'area che copre. (In ambienti decisionali più ricchi di dati e sofisticati, potremmo calcolare la bontà come una combinazione di proprietà delle celle incluse in ogni riquadro, proprietà forse legate alla visibilità, alla vicinanza ad altre cose e così via.)

I vincoli a questo problema sono semplicemente che non possono sovrapporsi due riquadri all'interno di una soluzione.

Questo può essere inquadrata un po 'più astrattamente, in modo favorevole al calcolo efficiente, elencando le cellule del poligono da riempire ( "regione") 1, 2, ..., M . Qualsiasi posizionamento delle tessere può essere codificato con un vettore indicatore di zeri e di uno, consentendo a quelli di corrispondere alle celle coperte dalla tessera e di zeri altrove. In questa codifica, tutte le informazioni necessarie su una raccolta di tessere possono essere trovate sommando i loro vettori indicatore (componente per componente, come al solito): la somma sarà diversa da zero esattamente dove almeno una tessera copre una cella e la somma sarà maggiore più di una dovunque due o più tessere si sovrappongono. (La somma conta efficacemente la quantità di sovrapposizione.)

Un altro po 'di astrazione: l'insieme dei possibili tirocini piastrelle può essa stessa essere enumerato, diciamo 1, 2, ..., N . La selezione di qualsiasi serie di posizionamenti di tessere stessa corrisponde a un vettore indicatore in cui quelli designano le tessere da posizionare.

Ecco una piccola illustrazione per correggere le idee . È accompagnato dal codice Mathematica utilizzato per eseguire i calcoli, in modo che la difficoltà di programmazione (o la sua mancanza) possa essere evidente.

Innanzitutto, descriviamo una regione da piastrellare:

region =  {{0, 0, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}};

Figura 1: regione

Se numeriamo le sue celle da sinistra a destra, iniziando dall'alto, il vettore indicatore per la regione ha 16 voci:

Flatten[region]

{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}

Usiamo la seguente tessera, insieme a tutte le rotazioni per multipli di 90 gradi:

tileSet = {{{1, 1}, {1, 0}}};

Figura 2: piastrella

Codice per generare rotazioni (e riflessioni):

apply[s_List, alpha] := Reverse /@ s;
apply[s_List, beta] := Transpose[s];
apply[s_List, g_List] := Fold[apply, s, g];
group = FoldList[Append, {}, Riffle[ConstantArray[alpha, 4], beta]];
tiles = Union[Flatten[Outer[apply[#1, #2] &, tileSet, group, 1], 1]];

(Questo calcolo un po 'opaco è spiegato in una risposta su /math//a/159159 , che mostra che produce semplicemente tutte le possibili rotazioni e riflessioni di una piastrella e quindi rimuove qualsiasi risultato duplicato.)

Supponiamo di posizionare la tessera come mostrato qui:

Figura 3: posizionamento delle piastrelle

Le celle 3, 6 e 7 sono coperte in questo posizionamento. Questo è indicato dal vettore indicatore

{0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}

Se spostiamo questa piastrella di una colonna a destra, sarebbe invece quel vettore indicatore

{0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}

La combinazione del tentativo di posizionare contemporaneamente le tessere in entrambe queste posizioni è determinata dalla somma di questi indicatori,

{0, 0, 1, 1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0}

Il 2 nella settima posizione mostra queste sovrapposizioni in una cella (seconda riga in basso, terza colonna da sinistra). Poiché non vogliamo sovrapposizioni, richiederemo che la somma dei vettori in qualsiasi soluzione valida non abbia voci superiori a 1.

Si scopre che per questo problema sono possibili 29 combinazioni di orientamento e posizione per le piastrelle. (Questo è stato trovato con una semplice codifica che implica una ricerca esaustiva.) Possiamo rappresentare tutte le 29 possibilità disegnando i loro indicatori come vettori di colonna . (L'uso delle colonne anziché delle righe è convenzionale.) Ecco un'immagine dell'array risultante, che avrà 16 righe (una per ogni cella possibile nel rettangolo) e 29 colonne:

makeAllTiles[tile_, {n_Integer, m_Integer}] := 
  With[{ m0 = Length[tile], n0 = Length[First[tile]]},
   Flatten[
    Table[ArrayPad[tile, {{i, m - m0 - i}, {j, n - n0 - j}}],  {i, 0, m - m0}, {j, 0, n - n0}], 1]];
allTiles = Flatten[ParallelMap[makeAllTiles[#, ImageDimensions[regionImage]] & , tiles], 1];
allTiles = Parallelize[
   Select[allTiles, (regionVector . Flatten[#]) >= (Plus @@ (Flatten[#])) &]];
options = Transpose[Flatten /@ allTiles];

Figura 4: array di opzioni

(I due vettori indicatore precedenti appaiono come le prime due colonne a sinistra.) Il lettore dagli occhi acuti potrebbe aver notato diverse opportunità per l'elaborazione parallela: questi calcoli possono richiedere alcuni secondi.

Tutto quanto sopra può essere riformulato in modo compatto usando la notazione matriciale:

  • F è questo array di opzioni, con M righe e N colonne.

  • X è l'indicatore di una serie di posizionamenti piastrelle, di lunghezza N .

  • b è un vettore N di quelli.

  • R è l'indicatore per la regione; è un vettore M.

La "bontà" totale associata a qualsiasi possibile soluzione X è uguale a RFX , poiché FX è l'indicatore delle celle coperte da X e il prodotto con R somma questi valori. (Potremmo ponderare R se desiderassimo che le soluzioni favorissero o evitassero determinate aree della regione.) Questo deve essere massimizzato. Perché possiamo scriverlo come ( RF ). X , è una funzione lineare di X : questo è importante. (Nel codice seguente, la variabile ccontiene RF .)

I vincoli sono quelli

  1. Tutti gli elementi di X devono essere non negativi;

  2. Tutti gli elementi di X devono essere inferiori a 1 (che è la voce corrispondente in b );

  3. Tutti gli elementi di X devono essere integrali.

I vincoli (1) e (2) rendono questo un programma lineare , mentre il terzo requisito lo trasforma in un programma lineare intero .

Esistono molti pacchetti per risolvere programmi lineari interi espressi esattamente in questa forma. Sono in grado di gestire i valori di M e N in decine o addirittura centinaia di migliaia. Questo è probabilmente abbastanza buono per alcune applicazioni del mondo reale.


Come prima illustrazione, ho calcolato una soluzione per l'esempio precedente usando il comando di Mathematica 8 LinearProgramming. (Ciò minimizzerà una funzione oggettiva lineare. La minimizzazione si trasforma facilmente in massimizzazione negando la funzione obiettivo.) Restituiva una soluzione (come un elenco di tessere e le loro posizioni) in 0,011 secondi:

b = ConstantArray[-1, Length[options]];
c = -Flatten[region].options;
lu = ConstantArray[{0, 1}, Length[First[options]]];
x = LinearProgramming[c, -options, b, lu, Integers, Tolerance -> 0.05];
If[! ListQ[x] || Max[options.x] > 1, x = {}];
solution = allTiles[[Select[x Range[Length[x]], # > 0 &]]];

Figura 5: soluzione

Le celle grigie non si trovano affatto nella regione; i globuli bianchi non erano coperti da questa soluzione.

Puoi elaborare (a mano) molti altri limiti uguali a questo, ma non riesci a trovarne di migliori. Questo è un potenziale limitazione di questo approccio: ti dà una soluzione migliore, anche quando non v'è più di uno. (Esistono alcune soluzioni alternative: se riordiniamo le colonne di X , il problema rimane invariato, ma il software spesso sceglie una soluzione diversa di conseguenza. Tuttavia, questo comportamento è imprevedibile.)

Come seconda illustrazione , per essere più realistici, consideriamo la regione nella domanda. Importando l'immagine e ricampionandola, l'ho rappresentata con una griglia 69 per 81:

Figura 6: Regione

La regione comprende 2156 celle di questa griglia.

Per rendere le cose interessanti e illustrare la generalità della configurazione della programmazione lineare, proviamo a coprire il più possibile di questa regione con due tipi di rettangoli:

Figura 7: tessere

Uno è 17 per 9 (153 cellule) e l'altro è 15 per 11 (165 cellule). Potremmo preferire utilizzare il secondo, perché è più grande, ma il primo è più magro e può adattarsi in luoghi più stretti. Vediamo!

Il programma ora prevede N = 5589 possibili posizionamenti delle tessere. È abbastanza grande! Dopo 6,3 secondi di calcolo, Mathematica ha trovato questa soluzione a dieci tessere:

Figura 8: soluzione

A causa di un po 'di allentamento ( .eg, potremmo spostare la piastrella in basso a sinistra fino a quattro colonne alla sua sinistra), ci sono ovviamente alcune altre soluzioni che differiscono leggermente da questa.


1
Una versione precedente di questa soluzione (ma non altrettanto buona) appare sul sito di Mathematica all'indirizzo matematica.stackexchange.com/a/6888 . Potrebbe anche valere la pena notare che una piccola variazione della formulazione può essere utilizzata per risolvere il problema di coprire completamente la regione con il minor numero possibile di tessere (consentendo alcune sovrapposizioni, ovviamente): ciò risolverebbe il "patchhole" problema.
whuber

1
Nell'interesse dello spazio, questa risposta non descrive alcuni miglioramenti potenzialmente utili. Ad esempio, dopo aver trovato tutte le possibili posizioni delle tessere (come vettori indicatori), è possibile aggiungerle tutte per trovare quali celle possono essere effettivamente coperte da alcune tessere. L'insieme di tali celle si divide in due componenti connesse separate nel secondo esempio. Ciò significa che il problema può essere risolto in modo indipendente nei due componenti, riducendo sostanzialmente le sue dimensioni (e quindi i tempi di calcolo). Tali semplificazioni iniziali tendono ad essere importanti per affrontare i problemi del mondo reale.
whuber

Grande sforzo e risposta. Anche la risposta di Chris è stata utile. Grazie a tutti per l'aiuto! Funziona e mi ha fatto spostare di nuovo nella giusta direzione.
Thad

Wow! Ero interessato a un problema simile e questo post mi ha dato una nuova prospettiva. Grazie. Cosa succede se R è più grande (ad es. 140x140≈20000), ci sono modi per ridurre i costi di calcolo? Conosci qualche documento relativo a questo problema? Le parole chiave di ricerca non mi conducono nel modo giusto (fino ad ora).
nimcap,

@nimcap Questa è una classe importante di problemi, così tante ricerche continuano. Le parole chiave da cercare inizierebbero con "programma lineare intero misto" e si diramerebbero da lì in base a ciò che trovi.
whuber

5

Il collegamento a Al genetic Algorithms for the Packing of Polygons , fornito nella mia risposta a una domanda simile in Algoritmo di ricerca per posizionare il numero massimo di punti all'interno dell'area vincolata alla distanza minima? , potrebbe essere utile. Sembra che il metodo possa essere generalizzato per funzionare con forme di contenitore arbitrarie (e non solo rettangoli).


Quel documento ha delle belle idee (+1), ma tutti i suoi algoritmi si concentrano, in modo fondamentale, sull'imballaggio dei poligoni all'interno di regioni rettangolari . Questo perché rappresenta imballaggi con una struttura dati discreta (una sequenza di poligoni insieme ai loro orientamenti) che rappresenta un insieme di procedure in cui i poligoni sono fatti scorrere , paralleli ai lati del quadrato, verso un angolo designato. Sembra che una codifica discreta così semplice sarebbe meno efficace per le regioni più complicate. Forse una semplificazione iniziale delle regioni nella griglia sarebbe di aiuto.
whuber

2

Per il sottoinsieme fortemente vincolato che hai citato (piastrellatura quadrata / triangolare in una buca), assumendo le ottimizzazioni esplicite sopra, questo pseudocodice dovrebbe arrivare a una risposta approssimativa semplicemente guidandoti attraverso le possibilità con un'alta risoluzione, costringendo il problema. Non funzionerà correttamente in situazioni in cui la rotazione di singole tessere può vedere guadagni, come tessere rettangolari o un contenitore altamente irregolare. Si tratta di 1 milione di iterazioni, puoi provare di più se necessario.

Assumi un quadrato con i lati di lunghezza L

Crea un motivo a scacchiera di quadrati, che è almeno delle dimensioni dell'estensione del contenitore, più almeno 1L in ciascuna direzione.

N = 0

DX = 0

DY = 0

DR = 0

Ripristina la posizione della scacchiera sul centroide originale

Per (R = 1: 100)

Per (Y = 1: 100)

Per (X = 1: 100)

M = Conta il numero di quadrati completamente all'interno del contenitore

If (M> N)

DR = R

DY = Y

DX = X

N = M

Sposta la scacchiera verso est di L / 100

Ripristina la scacchiera verso est

Sposta la scacchiera verso nord di L / 100

Ripristina il nord della scacchiera

Ruota la scacchiera di 3,6 gradi CW attorno al suo centroide

DY = DY * L

DX = DX * L

Ripristina la scacchiera nella posizione e rotazione originali

Stampa DR & "," & DX & "e" & DY & "sono la matrice di traduzione / rotazione finale"

Ruota la scacchiera di DR

Traduci scacchiera di DX, DY

Seleziona i quadrati che sono completamente all'interno del contenitore

Esporta quadrati


Se provi questa procedura su una regione 2 per 5 con una cella mancante lungo il centro di un lato lungo, scoprirai che puoi inserirvi solo un quadrato 2 per 2. Tuttavia, due di questi quadrati si adattano facilmente. Il problema è che non fanno parte di un normale schema a "scacchiera". Questa difficoltà è una delle cose che rende questo problema abbastanza difficile.
whuber

1
Sì. Se la forma del contenitore è abbastanza irregolare da poter supportare più schemi regolari non contigui nell'ordine di alcune celle ciascuno, questo risulta molto lontano dall'ottimale. Aggiungendo cose del genere alla possibilità, lo spazio aumenta i tempi di elaborazione molto rapidamente e richiede un certo grado di pianificazione per il caso specifico che si sta prendendo di mira.
Mappatura domani
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.