Outils de modèle de gravité / Huff

26

Je cherche un moyen de simuler un modèle de gravité en utilisant une couche basée sur des points.

Tous mes points reçoivent une valeur z et plus cette valeur est élevée, plus leur "sphère d'influence" est grande. Cette influence est inversement proportionnelle à la distance au centre.

C'est un modèle typique de Huff, chaque point est un maximum local et les vallées entre elles indiquent les limites de la zone d'influence entre elles.

J'ai essayé plusieurs algorithmes d'Arcgis (IDW, allocation des coûts, interpolation polynomiale) et QGIS (plugin heatmap), mais je n'ai rien trouvé qui pourrait m'aider. J'ai également trouvé ce fil , mais il n'est pas très utile pour moi.

Comme alternative, je pourrais également être satisfait par un moyen de générer des diagrammes de Voronoi s'il existe un moyen d'influencer la taille de chaque cellule par la valeur z du point correspondant.

Damien
la source

Réponses:

13

Voici une petite fonction python QGIS qui implémente cela. Il nécessite le plugin rasterlang (le référentiel doit être ajouté à QGIS manuellement).

Il attend trois paramètres obligatoires: la couche de points, une couche raster (pour déterminer la taille et la résolution de la sortie) et un nom de fichier pour la couche de sortie. Vous pouvez également fournir un argument facultatif pour déterminer l'exposant de la fonction de décroissance de la distance.

Les pondérations des points doivent figurer dans la première colonne d'attributs de la couche de points.

Le raster résultant est automatiquement ajouté au canevas.

Voici un exemple de la façon d'exécuter le script. Les points ont des poids compris entre 20 et 90, et la grille a une taille de 60 par 50 unités cartographiques.

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)
Jake
la source
3
(+1) L'approche semble bonne. Mais pourquoi prenez-vous la racine carrée, puis la remettez au carré dans l'informatique curGravity? C'est une perte de temps de calcul. Un autre ensemble de calculs inutiles consiste à normaliser toutes les grilles de "gravité" avant de trouver le maximum: au lieu de cela, trouvez leur maximum et normalisez cela par la somme.
whuber
Cela ne correspond-il pas à la fraction entière?
lynxlynxlynx
1
Jake, vous n'avez toujours jamais besoin de la racine carrée: oubliez-la complètement et utilisez la moitié de l'exposant prévu. En d'autres termes, si z est la somme des carrés des différences de coordonnées, au lieu de calculer (sqrt (z)) ^ p, qui est deux opérations modérément coûteuses, il suffit de calculer z ^ (p / 2), qui (parce que p / 2 est un nombre précalculé ) n'est qu'une opération raster - et conduit également à un code plus clair. Cette idée vient à l'esprit lorsque vous appliquez des modèles gravitationnels tels qu'ils étaient initialement destinés: aux temps de trajet. Il n'y a plus de formule racine carrée, vous augmentez donc le temps de déplacement jusqu'à la puissance -p / 2.
whuber
Merci beaucoup, cela ressemble à ce dont j'ai besoin. Juste un problème, je ne suis pas tellement habitué à python et je n'ai jamais utilisé l'extension Rasterlang. Je l'ai installé sur ma version QGIS, mais je suis coincé avec une "erreur de syntaxe". Votre fonction est-elle déjà implémentée dans l'extension rasterlang? Si non, comment dois-je procéder? Merci pour ton aide! http://i.imgur.com/NhiAe9p.png
Damien
1
@Jake: Ok, je pense que je commence à comprendre comment fonctionne la console. J'ai fait comme vous l'avez dit et le code semble être bien compris. Maintenant, j'ai une autre erreur qui concerne un package python "shape_base.py". Mon installation QGIS manque-t-elle de certaines fonctionnalités? http://i.imgur.com/TT0i2Cl.png
Damien