No último post foi descrito o básico da manipulação de camadas vetoriais. Como o nosso objetivo é criar um plug-in personalizado totalmente funcional com base em uma distância, gostaria de discutir a indexação espacial e a análise do vizinho mais próximo.

A figura acima ilustra a tarefa que pode ser resolvida apenas usando a API do QGIS. Imagine que você tenha uma camada de origem com um atributo preenchido com valores. Você recebe uma camada de destino também, infelizmente, os valores nesta camada estão faltando (não tão raros no mundo GIS, certo?). No entanto, você sabe que o valor ausente de cada recurso na camada de destino pode ser preenchido pelo valor de seu vizinho mais próximo da camada de origem. Como você faz isso?

1. GERANDO DADOS FICTÍCIOS

Vamos criar dois conjuntos de dados na memória com atributos id e value. Ambos terão dez recursos.

from qgis.core import QgsMapLayerRegistry, QgsVectorLayer, QgsFeature, QgsGeometry, QgsPoint, QgsSpatialIndex
from qgis.utils import iface

source_layer = QgsVectorLayer("point?crs=epsg:4326&field=id:integer&field=value:integer", "Source layer", "memory")
target_layer = QgsVectorLayer("point?crs=epsg:4326&field=id:integer&field=value:integer", "Target layer", "memory")

def create_dummy_data():

    source_layer.startEditing()
    target_layer.startEditing()

    feature = QgsFeature(source_layer.pendingFields())

    for i in range(10):
        feature.setGeometry(QgsGeometry.fromPoint(QgsPoint(i, i)))
        feature.setAttribute("id", i)
        feature.setAttribute("value", i)
        source_layer.addFeature(feature)

    feature = QgsFeature(source_layer.pendingFields())

    for i in range(10):
        feature.setGeometry(QgsGeometry.fromPoint(QgsPoint(i + i, i)))
        feature.setAttribute("id", i)
        target_layer.addFeature(feature)

    source_layer.commitChanges()
    target_layer.commitChanges()

    QgsMapLayerRegistry.instance().addMapLayer(source_layer)
    QgsMapLayerRegistry.instance().addMapLayer(target_layer)

create_dummy_data()

2. ESCREVENDO VALORES DO VIZINHO MAIS PRÓXIMO

A análise real do vizinho mais próximo pode ser feita em dez linhas de código! Primeiro, o qgis.core.QgsSpatialIndex é construído a partir de todos os recursos source_layer. Então, você itera sobre os recursos target_layer e para cada um deles, obtém apenas um ( nearestNeighbor(f.geometry().asPoint(), 1)[0]) vizinho mais próximo. Por fim, você apenas grava o valor do atributo do vizinho mais próximo na camada de destino e confirma as alterações.

def write_values_from_nn():
    source_layer_index = QgsSpatialIndex(source_layer.getFeatures())
    source_layer_features = {feature.id(): feature for (feature) in source_layer.getFeatures()}
    target_layer_features = target_layer.getFeatures()

    target_layer.startEditing()

    for f in target_layer_features:
        nearest = source_layer_index.nearestNeighbor(f.geometry().asPoint(), 1)[0]
        value = source_layer_features[nearest].attribute("value")
        target_layer.changeAttributeValue(f.id(), 1, value)

    target_layer.commitChanges()

write_values_from_nn()

3. O QUE VEM POR AI

Estamos um pouco mais perto do nosso objetivo. O que esta faltando?

  • Verificações de capacidades: a camada de destino suporta edições? Verifique os recursos do provedor de dados da camada para descobrir.
  • Registro do usuário: avisos ou erros estão completamente ausentes. Será ótimo tê-los apresentados dentro do qgis.gui.QgsMessageBar.
  • Atributos customizados: essa versão espera que as duas camadas tenham o mesmo atributo com o mesmo tipo de dados.
  • GUI : um widget PyQt transformará esse script baseado em console em um plug-in personalizado. Isso é o que vamos ver no próximo post.