I'm trying to update all of my field/column names to change them into their non-joined attribute table names, as when you join attribute tables on QGIS the names are altered into something undesirable (see images below)
The issue that I'm having is that the field names will not update despite my code running and no errors being brought up.
Faruthermore, it seems like strings that are put into variables and not explicitly stated in "layer.renameAttribute(oldName, newName)" as a string do not have any affect, even though the code runs with no errors. For example, layer.renameAttribute("oldstring", "newstring" works, but not layer.renameAttribute(old_variable, new_variable) where the variables hold strings.
Finally, I tested the code (stating strings explicitly) with non-joined attribute tables using the code from the hyperlink with other vector layers and the code worked fine. Please see my code and images for more clarification.
- My code for changing the columns names is based off the answer already given here: Renaming columns in a QGIS attribute table with Python. This code works flawlessly, however my alterations to it do not, even though syntactically I have no errors
- The remainder of the code works as intended, however I feel that it could be that I may be calling upon certain parameters in the wrong order or setting up some variables incorrectly which may the reason why my code isn't working. I'm not sure of the problem however.
- The column heading names of the joined attribute tables are in unicode. As such I changed it to string in order to use the renameAttribute method. Made no difference however, even though there is no error.
Below is my full code including the creation of the layer and the joining of the layer.
CODE
Creating the layer
# Original huff_model layer
ori_huff_model = QgsVectorLayer("path", "polygon", "ogr")
# 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", "Huff_Model", "memory")
huff_model_data = huff_model.dataProvider()
# Change the census layer name to blank before joining to newly created layer so that only a '_' remains after the join before each column name in the format '_<column name>'
rename = QgsMapLayerRegistry.instance().mapLayersByName("TorontoCMA_2006census_region")[0]
if rename.name() == "TorontoCMA_2006census_region":
rename.setLayerName('')
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])
# 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())
print ">>> Field name: {} Field ID: {}".format(field.name(), field_id)
if "HiSC1009" == field.name():
mall = huff_model.fieldNameIndex(field.name())
print "<<< mall ID: {}".format(mall)
break
# This is where you choose the mall that you want the market areas for.
# Currently accepts an index number, which that corresponds to the column
# in the attribute table. I.e 1 for the first column aka first mall, 2 for
# the second column aka second mall and so forth.
qid = QInputDialog()
title = "Which mall would you like to find the market area for?"
label = "Name: "
mode = QLineEdit.Normal
default = "<mall name here>"
text, ok = QInputDialog.getText(qid, title, label, mode, default)
col_num = int(text)
print col_num
# 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, 'double', 2, 2)
huff_model.addAttribute(primary)
index_pri = huff_model.fieldNameIndex('Primary')
secondary = QgsField('Secondary', QVariant.Double, 'double', 2, 2)
huff_model.addAttribute(secondary)
index_sec = huff_model.fieldNameIndex('Secondary')
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 this column
if probability[col_num] >= .6:
huff_model.changeAttributeValue(feature.id(), index_pri, probability[col_num])
# Secondary market values will go into this column
if .4 <= probability[col_num] < .6:
huff_model.changeAttributeValue(feature.id(), index_sec, probability[col_num])
# Delete all other columns 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()))
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.updateExtents()
huff_model.commitChanges()
Setting up the join
# Making sure that only one copy layer exists at a time, if running the
script multiple times with different malls
layers = QgsMapLayerRegistry.instance().mapLayers()
for name, layer in layers.iteritems():
if layer.name() == "Huff_Model":
print "It exists"
if "Huff_Model" in name:
QgsMapLayerRegistry.instance().removeMapLayer(layer)
else:
print "Does not exist"
# Add newly formatted layer to map
reg = QgsMapLayerRegistry.instance()
reg.addMapLayer(huff_model)
# Join the census layer to the newly created layer to show specific demographic data.
targetLyr = QgsMapLayerRegistry.instance().mapLayersByName("Huff_Model")[0]
censusLyr = QgsMapLayerRegistry.instance().mapLayersByName('')[0]
root = QgsProject.instance().layerTreeRoot()
mytargetLyr = root.findLayer(targetLyr.id())
targetClone = mytargetLyr.clone()
parent = mytargetLyr.parent()
parent.insertChildNode(0, targetClone)
parent.removeChildNode(mytargetLyr)
mycensusLyr = root.findLayer(censusLyr.id())
censusClone = mycensusLyr.clone()
parent = mycensusLyr.parent()
parent.insertChildNode(1, censusClone)
parent.removeChildNode(mycensusLyr)
# Set properties for the join
targetField = 'CTUID'
inField = 'CTUID'
joinObject = QgsVectorJoinInfo()
joinObject.joinLayerId = censusLyr.id()
joinObject.joinFieldName = inField
joinObject.targetFieldName = targetField
print(targetLyr.addJoin(joinObject)) # You should get True as response.
targetLyr.addJoin(joinObject)
Changing column/field names
# Make the created layer that now has a joined attribute table active.
huff_model = iface.activeLayer()
# Set up variables to be able to perform a loop to change all layer names
field_names = [] # This will hold all the field names in a string
idx = 0
startCol = 0
endCol = len(huff_model.pendingFields())
countCol = (range(startCol, endCol, 1))
for field in huff_model.fields():
name = str(field.name()).replace('_', '') # Turn all field names from unicode to a string
field_names.append(name) # Add to the list above
print ">>> {}".format(field_names)
# Change all the field names in a loop. This currently does not work. Print statements are for debugging and testing. I want to remove the underscore (see image) but it doesn't change despite the rest of the code running.
for colNumber in countCol:
with edit(huff_model):
print "<<< {}".format(type(field_names[idx]))
newName = field_names[idx]
print "~~~ {}".format(newName)
huff_model.renameAttribute(colNumber, newName)
idx = idx + 1
# Turn the census layer back into it's original name on the layer panel
rename = QgsMapLayerRegistry.instance().mapLayersByName("")[0]
if rename.name() == "":
rename.setLayerName('TorontoCMA_2006census_region')
Original Census Layer's Attribute Table
Below is the original census layer I joined to the layer I created. I want the column headings in my target layer to look like this without the underscore
Target Layer's Attribute Table After Script Is Run and Join is performed.
Below is the attribute table of the target layer after I joined the census layer to it. The first three columns are fields that I created, but the remainder are fields from the census layer. The names are not altered from the join, despite the code being run as evident by the preceding underscore before each demographic name.