Skip to content

Instantly share code, notes, and snippets.

@gacarrillor
Created November 1, 2021 00:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gacarrillor/ab596923d676fd0c0d1c5b253a57e2e7 to your computer and use it in GitHub Desktop.
Save gacarrillor/ab596923d676fd0c0d1c5b253a57e2e7 to your computer and use it in GitHub Desktop.
Note this uses PyQt4. It was uploaded only for reference.
# -*- coding: utf-8 -*-
# ***************************************************************************
#
# Code borrowed from TableManager
#
# Copyright (C) 2008 Borys Jurgiel
# Adapted by Germán Carrillo to rename Shapefiles (2015)
#
# ***************************************************************************
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU General Public License as published by *
# * the Free Software Foundation; either version 2 of the License, or *
# * (at your option) any later version. *
# * *
# ***************************************************************************
from qgis.core import *
from qgis.gui import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
DEBUGMODE=True
########## CLASS TableManager ##############################
class RenameShapefiles():
def __init__(self, iface, layer):
self.iface = iface
self.layer = None
if type(layer) is None:
print "This doesn't seem to be a valid layer..."
return
if not layer.type() == QgsMapLayer.VectorLayer:
print layer.name() + " is not a vector layer..."
return
self.layer = layer
self.provider = self.layer.dataProvider()
self.fields = self.readFields( self.provider.fields() )
if not self.provider.storageType() == 'ESRI Shapefile': # Is provider saveable?
print self.layer.name() + " is not a Shapefile layer..."
return
self.readData()
def readFields(self, providerFields): # Populates the self.fields dictionary with providerFields
fieldsDict = {}
i=0
for field in providerFields:
fieldsDict.update({i:field})
i+=1
return fieldsDict
def formatFieldType(self, field):
if field.type() == QVariant.Date:
return field.typeName()
elif field.type() == QVariant.Double:
return "%s(%d,%d)" % (field.typeName(), field.length(), field.precision())
else:
return "%s(%d)" % (field.typeName(), field.length())
def readData(self): # Reads data from the 'provider' QgsDataProvider into the 'data' list [[column1] [column2] [column3]...]
fields = self.fields
self.data = []
for i in range(len(fields)):
self.data += [[]]
for feat in self.provider.getFeatures():
attrs = feat.attributes()
for i in range(len(attrs)):
self.data[i] += [attrs[i]]
def doSave(self): # Called when appropriate button was pressed
# I believe the procedure below is as much safe as possible.
if not self.layer:
return
layerName = self.layer.name()
srcName = layerName
encoding = self.provider.encoding()
tmpDir = QDir.tempPath()
srcPath = self.provider.dataSourceUri().split('|')[0]
srcOrigPath = QFileInfo(srcPath).path() + '/' + QFileInfo(srcPath).baseName()
originalName = QFileInfo(srcPath).baseName()
srcPath = QFileInfo(srcPath).path() + '/' + layerName
if originalName == layerName:
print "Layer "+ layerName+" has nothing to rename..."
return
# without this one line code in win xp, srcName return something wrong,see below (patch from Volkan Kepoglu - thanks!)
srcPath = srcPath.replace("\\","/")
srcOrigPath = srcOrigPath.replace("\\","/")
# write the layer to the temporary directory
if self.writeToFile(tmpDir+'/'+srcName+'.shp', encoding) != 0:
QMessageBox.warning(self, 'Table Manager', "Failed saving the changes to the temporary directory: "+tmpDir+" !\nThe layer won't be changed, please use the Save As button.")
QgsVectorFileWriter.deleteShapeFile(tmpDir+'/'+srcName+'.shp')
return
# try to remove the old .dbf~ backup
QFile(srcOrigPath+'.dbf~').remove()
QFile.remove( tmpDir+'/'+srcName+'.qml' )
self.layer.saveNamedStyle( tmpDir+'/'+srcName+'.qml' )
QgsMapLayerRegistry.instance().removeMapLayers([self.layer.id()])
#Rename files
QFile(srcOrigPath+'.shx').rename(srcPath+'.shx')
QFile(srcOrigPath+'.prj').rename(srcPath+'.prj')
QFile(srcOrigPath+'.qpj').rename(srcPath+'.qpj')
QFile(srcOrigPath+'.shp').rename(srcPath+'.shp')
###########
# rename the oryginal .dbf file to .dbf~
if not QFile(srcOrigPath+'.dbf').rename(srcPath+'.dbf~'):
QMessageBox.warning(self, 'Table Manager', 'Failed backuping the old table to '+srcPath+'.dbf~\nThe layer won\'t be changed, please use the Save As button.')
# we don't return now because layer has to be reloaded'''
# copy the .dbf from the temp directory to the target location
elif QFile(tmpDir+'/'+srcName+'.dbf').copy(srcPath+'.dbf'):
# dbf file copied. Copy also cpg if present
pass
else:
# something went wrong with copying the dbf file. Restore the dbf~ file
if not QFile(srcPath+'.dbf~').rename(srcPath+'.dbf'):
QMessageBox.warning(self, 'Table Manager', 'WARNING! I can neither save the new {0}.dbf file\nnor restore it from the '+srcName+'.dbf~ backup.\nPlease check it manually!')
QgsVectorFileWriter.deleteShapeFile(tmpDir+'/'+srcName+'.shp')
return
# dbf~ file restored :
QMessageBox.warning(self, 'Table Manager', "Failed saving the changes to "+srcPath+".dbf\nThe layer will not be changed, please use the Save As button.")
'''
QgsVectorFileWriter.deleteShapeFile(tmpDir+'/'+srcName+'.shp')
return
we don't return now because layer has to be reloaded
'''
# clean the temp directory
QgsVectorFileWriter.deleteShapeFile(tmpDir+'/'+srcName+'.shp')
# create the new layer
'''layerName = self.layer.name()'''
newLayer = QgsVectorLayer(srcPath+'.shp', layerName, 'ogr')
if not newLayer.isValid():
QMessageBox.warning(self, 'Table Manager', "WARNING! The changes seem to be commited, but I can't reload the layer!\nPlease check it out!\nThe old table is backuped as "+srcName+".dbf~.")
return
# copy the style (it's possible only if the clasyfying field was not renamed!)
'''#newLayer.copySymbologySettings(self.layer)'''
resp = newLayer.loadNamedStyle( tmpDir+'/'+srcName+'.qml' )
if not resp[1]:
QMessageBox.warning(self, 'Table Manager', "This layer will be reloaded without its previous style (loading style failed)")
QFile.remove( tmpDir+'/'+srcName+'.qml' )
# set encoding
newLayer.setProviderEncoding( encoding )
# reload the layer
QgsMapLayerRegistry.instance().addMapLayers([newLayer])
# point the self.layer to the new one.
self.layer = newLayer
self.provider = self.layer.dataProvider()
self.fields = self.readFields( self.provider.fields() )
def writeToFile(self, filePath, encoding, driverName = 'ESRI Shapefile'): # write data to a shapefile
if QFile(filePath).exists():
try:
QgsVectorFileWriter.deleteShapeFile(filePath)
except:
QMessageBox.warning(self, 'Table Manager', 'Cannot overwrite an existing shapefile.\nPlease remove it manually.')
return 1
# create destination layer
fields = QgsFields()
keys = self.fields.keys()
keys.sort()
for key in keys:
fields.append(self.fields[key])
if driverName == 'ESRI Shapefile':
#lco = ["ENCODING="+encoding , "SHAPE_ENCODING="+encoding]
lco = ["ENCODING="+encoding]
settings = QSettings()
else:
lco = []
writer = QgsVectorFileWriter(filePath, encoding, fields, self.provider.geometryType(), self.provider.crs(), driverName, [], lco)
if writer.hasError():
QMessageBox.warning(self, 'Table Manager', "Error creating file. The errror message was:" + "<br/>" + writer.errorMessage() )
return 2
geom = QgsGeometry()
n = 0
for feat in self.provider.getFeatures():
geom = feat.geometry()
outFeat = QgsFeature()
if geom:
outFeat.setGeometry(geom)
attrs = []
for i in range(len(self.fields)):
try:
attrs += [self.data[i][n]]
except:
attrs += ['']
outFeat.initAttributes(len(attrs))
outFeat.setAttributes(attrs)
writer.addFeature(outFeat)
n += 1
del writer
return 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment