I'm having an issue where when I try to run my script through user scripts in processing tools, I am unable to properly join two attribute tables from two separate layers when the layer that I want to join has the same name in subsequent runs of the same script.
When the script is run the first time, the tables are joined properly. However, unless the layer name of the first layer that I want the second table to be joined to is changed before the script is run the second time, the join does not work despite getting no errors.
- I did not include all of my code, only the parts that are relevant to the join (The blocks that actually control the layers in the layer panel, removal of the layer of the same name, and the join itself). I can provide more code if required.
- I have not refactored the code or focused on efficiency as of yet, I simply want to get the functionality down first. That being said, I welcome any constructive suggestions (within the scope of my question).
- The idea is that a user can use the same name for the layer as many times as they want with a successful join. This is currently not happening. If a user uses a different name for the layer, there are no problems.
EDIT:
- I included my entire code, however for convenience I broke it into three distinct sections so that someone read and understand where the join is happening without searching. I tried my best with the comments to be as clear as I can for what the code is doing, please let me know if something is unclear.
- I know the code is fairly ugly and needs improving. I'm still fairly new to QGIS, so I'm really just focused on functionality right now.
CODE
Creating the layer
##Huff_Model_Layer=vector
##Mall=field Huff_Model_Layer
##Census_Layer=vector
##Mall_Layer_Name= string test
##PDF_file=output file pdf
from qgis.core import *
from qgis.gui import *
from qgis.utils import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import os
# Load the huff model layer
ori_huff_model = processing.getObject(Huff_Model_Layer)
# Load the census layer
census_layer = processing.getObject(Census_Layer)
# Choose the mall (field in table) to find the primary and secondary market
# areas for
mall = ori_huff_model.fieldNameIndex(Mall)
# Make a copy of the huff model layer in memory to work with so that all
# changes made to copy only keeping original data intact
huff_model = QgsVectorLayer("Polygon?crs=epsg:4326", Mall_Layer_Name, "memory")
huff_model_data = huff_model.dataProvider()
huff_model.startEditing()
attr = ori_huff_model.dataProvider().fields().toList()
huff_model_data.addAttributes(attr)
huff_model.updateFields()
feat = QgsFeature()
for elem in ori_huff_model.getFeatures():
feat.setGeometry(elem.geometry())
feat.setAttributes(elem.attributes())
huff_model.addFeatures([feat])
huff_model.updateExtents()
# Find the index of the mall(field) that will be used to find the correct
# probabilities within that field
for field in huff_model.fields():
field_id = huff_model.fieldNameIndex(field.name())
if mall == field.name():
mall = huff_model.fieldNameIndex(field.name())
break
col_num = int(mall)
# Create two new fields that will only show the primary and secondary market
# areas based on what is chosen as primary and secondary marketer
# probabilities
primary = QgsField('Primary', QVariant.Double)
huff_model.addAttribute(primary)
index_pri = huff_model.fieldNameIndex('Primary')
secondary = QgsField('Secondary', QVariant.Double)
huff_model.addAttribute(secondary)
index_sec = huff_model.fieldNameIndex('Secondary')
# Find the relevant features for the chosen mall
for feature in huff_model.getFeatures():
probability = feature.attributes()
ctuid = feature["CTUID"]
if not probability[col_num]:
print ("This mall does not exist")
else:
# Primary market values will go into the "Primary" field
if probability[col_num] >= .6:
huff_model.changeAttributeValue(feature.id(), index_pri, probability[col_num])
# Secondary market values will go into the "Secondary" field
if .4 <= probability[col_num] < .6:
huff_model.changeAttributeValue(feature.id(), index_sec, probability[col_num])
# Delete all other fields except for the CTUID, Primary, Secondary columns
fields = []
fieldnames = {'CTUID', 'Primary', 'Secondary'}
for field in huff_model.fields():
if field.name() not in fieldnames:
fields.append(huff_model.fieldNameIndex(field.name()))
#print(fields.append(huff_model.fieldNameIndex(field.name())))
huff_model.deleteAttributes(fields)
# Delete all other features (rows) that do not have a probability in the
# primary and the secondary fields
expr = QgsExpression("\"Primary\" is NULL and \"Secondary\" is NULL")
for f in huff_model.getFeatures(QgsFeatureRequest(expr)):
huff_model.deleteFeature(f.id())
huff_model.commitChanges()
Setting up the Join
# Remove the previously created layer from when the script is run. I only
# want one joined layer to show in the panel at any given time. That is, if
# someone ran the script multiple times, I want to avoid stacking new
# layers as a PDF output is created each time for the attribute table of the
# joined layer.
root = QgsProject.instance().layerTreeRoot()
ids = root.findLayerIds()
last_layer = root.findLayer(ids[0])
print last_layer.name()
root.removeChildNode(last_layer)
# Add newly created layer to map. A layer I have created (using code not
# included here) is added to layer panel so that a join is possible.
reg = QgsMapLayerRegistry.instance()
reg.addMapLayer(huff_model)
# Set the appropriate layers for the join. Mall_Layer_Name is the name for
# the huff_model layer
targetLyr = QgsMapLayerRegistry.instance()
targetLyr.mapLayersByName(Mall_Layer_Name)[0] # Mall_Layer_Name is a string
censusLyr = QgsMapLayerRegistry.instance()
censusLyr.mapLayersByName(census_layer.name())[0]
# Set properties for the join
targetField = 'CTUID'
inField = 'CTUID'
joinObject = QgsVectorJoinInfo()
joinObject.joinLayerId = censusLyr.id()
joinObject.joinFieldName = inField
joinObject.targetFieldName = targetField
joinObject.memoryCache = True
print(targetLyr.addJoin(joinObject)) # You should get True as response.
targetLyr.addJoin(joinObject)
Creating the PDF output
# Make sure that the newly created and joined layer is the active layer.
huff_model = iface.activeLayer()
# Create the attribute table using the map composer in QGIS and show the
# output in PDF format.
mapRenderer = iface.mapCanvas().mapRenderer()
c = QgsComposition(mapRenderer)
c.setPlotStyle(QgsComposition.Print)
x, y = 0, 0
w, h = c.paperWidth(), c.paperHeight()
composerMap = QgsComposerMap(c, x, y, w, h)
table = QgsComposerAttributeTable(c)
table.setComposerMap(composerMap)
table.setVectorLayer(huff_model)
table.setMaximumNumberOfFeatures(huff_model.featureCount())
c.addItem(table)
# Checks for the existence of a file with the same name and removes it.
if os.path.isfile(PDF_file):
os.remove(PDF_file)
# Set printer properties
printer = QPrinter()
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName(PDF_file)
printer.setPaperSize(QSizeF(c.paperWidth(), c.paperHeight()), QPrinter.Millimeter)
printer.setFullPage(True)
# Create the PDF output and close printer
pdfPainter = QPainter(printer)
paperRectMM = printer.pageRect(QPrinter.Millimeter)
paperRectPixel = printer.pageRect(QPrinter.DevicePixel)
c.render(pdfPainter, paperRectPixel, paperRectMM)
pdfPainter.end()
The image below illustrates the final joined tabled, the script parameters, and layer panel. This image is for the first run and is successful.
First Run: Successful join
The image below is exactly the same as the first image except for the fact that the seen test layer is a new layer despite having the same name. Every other parameter is exactly the same. Also notice that the join did not work.Subsequent runs after the second run have the same result if the layer name is not changed.