Try the Unique_values_saver plugin for QGIS. It allows you to save one vector layer for each unique value from a specific field.
For instance, for a layer with world countries and regions, I've generated 10 vector layers, each one corresponding to a different region.
If you need the way to do it in Python, just have a look at the source code of the plugin, specifically at the uniqueValues method:
# copyright : (C) 2014 by Giuseppe De Marco
# email : info@pienocampo.it
# Snippet taken from the Unique_values_saver QGIS plugin
def uniquevalues (self):
self.dlg.ui.txt.clear()
lay = self.choose_layer()
fie = self.choose_field()
uniquevalues = []
progress = self.dlg.ui.progressbar
progress.setValue(0)
if ((lay != "") and (fie != "")):
for name, layer in QgsMapLayerRegistry.instance().mapLayers().iteritems():
if layer.name() == lay:
uniqueprovider = layer.dataProvider()
fields = uniqueprovider.fields()
dir = self.get_dir()
if dir==None:
QMessageBox.information(None, "Shapesaver","No output directory given: aborting...")
return
else:
QMessageBox.information(None, "Shapesaver","output dir : "+str(dir))
for field in fields:
if field.name() == fie:
id = fields.indexFromName(fie)
uniquevalues=uniqueprovider.uniqueValues(id)
progress.setMaximum(len(uniquevalues))
if uniquevalues:
for uv in uniquevalues:
progress.setValue(progress.value()+1)
for field in fields:
if field.name() == fie:
selectList =[]
for f in layer.getFeatures():
attrs = f.attributes()
double = ""#checks for duplicates in case of null attr value
for attr in attrs:
if str(attr) == str(uv):
if str(f)!= str(double):#checks for duplicates in case of null attr value
selectList.append(f.id())
double = f
# writes matching features in a new shapefile
if selectList:
#selectlayer.setSelectedFeatures(selectList)
savname = field.name()+str(uv)+".shp"
savename = os.path.join(dir, savname)
self.dlg.ui.txt.append("Saving "+str(savename))
if len(savename) <= 0:
QMessageBox.information(None, "Shapesaver","No output filename given")
return
else:
outfile = QgsVectorFileWriter(savename, "utf-8", fields, uniqueprovider.geometryType(), uniqueprovider.crs())
for i in range(len(selectList)):
for sf in layer.getFeatures():
if sf.id() == selectList[i]:
attributes = sf.attributes()
geometry = sf.geometry()
feature = QgsFeature()
feature.setAttributes(attributes)
feature.setGeometry(geometry)
outfile.addFeature(feature)
selectList =[]
return
After working one full day on both codes; at last I was able to modify the code to create graduated symbology overlayed with proportional direction arrows.
def validatedDefaultSymbol(geometryType):
symbol = QgsSymbolV2.defaultSymbol(geometryType)
if symbol is None:
if geometryType == QGis.Point:
symbol = QgsMarkerSymbolV2()
elif geometryType == QGis.Line:
symbol = QgsLineSymbolV2()
elif geometryType == QGis.Polygon:
symbol = QgsFillSymbolV2()
return symbol
def makeSymbologyForRange(layer, min ,max, label ,colour, alpha, width):
symbol = validatedDefaultSymbol(layer.geometryType())
#Line layer
lineLayer = lineMeta.createSymbolLayer({'width': '1', 'color': '255,0,0', 'offset': '0', 'penstyle': 'solid', 'use_custom_dash': '0', 'joinstyle': 'bevel', 'capstyle': 'square'})
#Marker layer
markerLayer = markerMeta.createSymbolLayer({'width': '0.26', 'color': '255,0,0', 'rotate': '1', 'placement': 'centralpoint', 'offset': '0'})
subSymbol = markerLayer.subSymbol()
#Replace the default layer with our own SimpleMarker
subSymbol.deleteSymbolLayer(0)
triangle = registry.symbolLayerMetadata("SimpleMarker").createSymbolLayer({'name': 'filled_arrowhead', 'color': '255,0,0', 'color_border': '255,255,255', 'offset': '0,0', 'size': '3', 'outline_width': '0.5', 'angle': '0'})
subSymbol.appendSymbolLayer(triangle)
#Replace the default layer with our two custom layers
symbol.deleteSymbolLayer(0)
symbol.appendSymbolLayer(lineLayer)
symbol.appendSymbolLayer(markerLayer)
symbol.setColor(colour)
symbol.setAlpha(alpha)
symbol.setWidth(width)
range = QgsRendererRangeV2(min, max, symbol, label)
return range
vlayer = QgsVectorLayer(str(SaveShpName), str(SaveShpName), 'ogr')
myTargetField = 'magnitude'
#Equal Interval
if dlg.ui.comboBoxSelectSymbology.currentText() == "Equal Interval":
GradSymNoOfClasses = dlg.ui.spinBoxClasses.value()
GradSymInterval = round(((int(GradSymMax) - int(GradSymMin)) / float(GradSymNoOfClasses)),0)
myRangeList = []
for i in range(GradSymNoOfClasses):
if i == 0:
classlabel = str(GradSymMin)+" - "+str(int(GradSymMin)+GradSymInterval)
myRangeList.append(makeSymbologyForRange( vlayer, int(GradSymMin), int(GradSymMin)+GradSymInterval, classlabel , QColor(0, 255-(255*i/GradSymNoOfClasses), 255*i/GradSymNoOfClasses), 1, 1 ))
elif i == (GradSymNoOfClasses - 1):
classlabel = str(int(GradSymMin)+(GradSymInterval*i)+0.001)+" - "+str(GradSymMax)
myRangeList.append(makeSymbologyForRange( vlayer, (GradSymInterval*i)+0.001, int(GradSymMax), classlabel , QColor(0, 255-(255*i/GradSymNoOfClasses), 255*i/GradSymNoOfClasses), 1, i*3 ))
else:
classlabel = str(int(GradSymMin)+(GradSymInterval*i)+0.001)+" - "+str(int(GradSymMin)+GradSymInterval*(i+1))
myRangeList.append(makeSymbologyForRange( vlayer, int(GradSymMin)+(GradSymInterval*i)+0.001, int(GradSymMin)+GradSymInterval*(i+1), classlabel , QColor(0, 255-(255*i/GradSymNoOfClasses), 255*i/GradSymNoOfClasses), 1, i*3 ))
myRenderer = QgsGraduatedSymbolRendererV2( myTargetField, myRangeList )
vlayer.setRendererV2( myRenderer )
QgsMapLayerRegistry.instance().addMapLayer( vlayer )
Best Answer
The issue is that the renderer class is QgsCategorizedSymbolRendererV2 instead of QgsGraduatedSymbolRendererV2. So, I categorized next shapefile by FID field:
and identical code (Germán Carrillo' script ) was run at the Python Console of QGIS:
As you can see at the next image, result was completely satisfactory (I am not getting any error):
Editing Note:
This is the code for Graduated Symbol Renderer:
After running it at the Python Console of QGIS, values at 'color' field were placed as expected.
By using 'NDVI' field it also works perfectly: