Strumenti modello Gravity / Huff


26

Sto cercando un modo per simulare un modello di gravità usando uno strato basato su punti.

A tutti i miei punti viene assegnato un valore z e maggiore è questo valore, maggiore è la loro "sfera di influenza". Questa influenza è inversamente proporzionale alla distanza dal centro.

È un tipico modello di Huff, ogni punto è un massimo locale e le valli tra loro indicano i limiti della zona di influenza tra di loro.

Ho provato diversi algoritmi di Arcgis (IDW, allocazione dei costi, interpolazione polinomiale) e QGIS (plug-in Heatmap), ma non ho trovato nulla che potesse aiutarmi. Ho anche trovato questo thread , ma non è molto utile per me.

In alternativa, potrei anche essere soddisfatto di un modo per generare diagrammi Voronoi se c'è un modo per influenzare la dimensione di ciascuna cella dal valore z del punto corrispondente.

Risposte:


13

Ecco una piccola funzione Python QGIS che implementa questo. Richiede il plug-in rasterlang (il repository deve essere aggiunto manualmente a QGIS).

Prevede tre parametri obbligatori: il livello punti, un livello raster (per determinare le dimensioni e la risoluzione dell'output) e un nome file per il livello output. È inoltre possibile fornire un argomento facoltativo per determinare l'esponente della funzione di decadimento della distanza.

I pesi per i punti devono trovarsi nella prima colonna degli attributi del livello punti.

Il raster risultante viene automaticamente aggiunto all'area di disegno.

Ecco un esempio di come eseguire lo script. I punti hanno pesi compresi tra 20 e 90 e la griglia ha dimensioni di 60 per 50 unità della mappa.

points = qgis.utils.iface.mapCanvas().layer(0)
raster = qgis.utils.iface.mapCanvas().layer(1)
huff(points,raster,"output.tiff",2)

from rasterlang.layers import layerAsArray
from rasterlang.layers import writeGeoTiff
import numpy as np

def huff(points, raster, outputfile, decay=1):
    if points.type() != QgsMapLayer.VectorLayer:
        print "Error: First argument is not a vector layer (but it has to be)"
        return
    if raster.type() != QgsMapLayer.RasterLayer:
        print "Error: Second argument is not a raster layer (but it has to be)"
        return
    b = layerAsArray(raster)
    e = raster.extent()
    provider = points.dataProvider()
    extent = [e.xMinimum(),e.yMinimum(),e.xMaximum(),e.yMaximum()]
    xcols = np.size(layerAsArray(raster),1)
    ycols = np.size(layerAsArray(raster),0)
    xvec = np.linspace(extent[0], extent[2], xcols, endpoint=False)
    xvec = xvec + (xvec[1]-xvec[0])/2
    yvec = np.linspace(extent[3], extent[1], ycols, endpoint=False)
    yvec = yvec + (yvec[1]-yvec[0])/2
    coordArray = np.meshgrid(xvec,yvec)
    gravity = b
    point = QgsFeature()
    provider.select( provider.attributeIndexes() )
    while provider.nextFeature(point):
      coord = point.geometry().asPoint()
      weight = point.attributeMap()[0].toFloat()[0]
      curGravity = weight * ( (coordArray[0]-coord[0])**2 + (coordArray[1]-coord[1])**2)**(-decay/2)
      gravity = np.dstack((gravity, curGravity))
    gravitySum = np.sum(gravity,2)
    huff = np.max(gravity,2)/gravitySum
    np.shape(huff) 
    writeGeoTiff(huff,extent,outputfile)
    rlayer = QgsRasterLayer(outputfile)
    QgsMapLayerRegistry.instance().addMapLayer(rlayer)

3
(+1) L'approccio sembra buono. Ma perché prendi la radice quadrata e poi ri-quadrala nel calcolo curGravity? È uno spreco di tempo computazionale. Un altro insieme sprecato di calcoli comporta la normalizzazione di tutte le griglie di "gravità" prima di trovare il massimo: invece, trova il loro massimo e normalizza quello dalla somma.
whuber

Non quadrerà l'intera frazione?
Lynxlynxlynx,

1
Jake, non hai ancora bisogno della radice quadrata: dimenticala del tutto e usa metà dell'esponente previsto. In altre parole, se z è la somma dei quadrati delle differenze di coordinate, invece di calcolare (sqrt (z)) ^ p, che è due operazioni moderatamente costose, basta calcolare z ^ (p / 2), che (perché p / 2 è un numero pre-calcolato ) è solo un'operazione raster e porta anche a un codice più chiaro. Questa idea viene alla luce quando si applicano i modelli gravitazionali come erano originariamente intesi: viaggiare nei tempi. Non esiste più alcuna formula con radice quadrata, quindi aumenti il ​​tempo di viaggio alla potenza di -p / 2.
whuber

Grazie mille, sembra proprio quello di cui ho bisogno. Solo un problema, non sono così abituato a Python e non ho mai usato l'estensione Rasterlang. L'ho installato sulla mia versione di QGIS, ma sono bloccato con un "errore di sintassi". La tua funzione è già implementata nell'estensione rasterlang? Se no, come posso farlo? Grazie per il tuo aiuto! http://i.imgur.com/NhiAe9p.png
Damien,

1
@Jake: Ok, penso di iniziare a capire come funziona la console. Ho fatto come hai detto e il codice sembra essere compreso correttamente. Ora ho un altro errore che riguarda un pacchetto Python "shape_base.py". La mia installazione di QGIS non ha alcune funzionalità? http://i.imgur.com/TT0i2Cl.png
Damien,
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.