1

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

enter image description here

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.

enter image description here

2

Do you mean underscore (which is _) and not hyphen (which is -)? :)

Anyway, I wonder if this is due to the prefix that is used when joining layers. The underscore is used as a default but we can set this to be empty by using:

joinObject.prefix = '' 

So when you are joining your layers, your code could look like:

# Set properties for the join
#...
joinObject.targetFieldName = targetField
joinObject.prefix = ''
print(targetLyr.addJoin(joinObject))
targetLyr.addJoin(joinObject)

Then you can ignore the whole "Changing column/field names" part (if setting the prefix is indeed the fix).

  • Works like a charm! Do you think you might know the reason why my original code wasn't working? (See Changing column/field names section). I thought the renameAttribute method would work if I changed the unicode headings to strings and stored them in a list – ShockDoctor Jan 12 '18 at 22:33
  • @OskiTuranoglu - Glad it worked :). Taking a quick look at your code, I think you were trying to rename joined fields. Those fields are not saved in your shapefile which means you cannot edit them. You would have needed to save your huff_model layer as a new layer (which would also save the joined fields) and then you could rename them. – Joseph Jan 15 '18 at 10:22
1

Just to build on Joseph's answer you no longer need to make any changes to original layer being joined to the target layer with regards to that layer's name. Therefore, the following code is no longer necessary, if you're following his advice.

# 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('')

and

# 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')

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.