ftools_utils.py
1 |
# -*- coding: utf-8 -*-
|
---|---|
2 |
|
3 |
#-----------------------------------------------------------
|
4 |
#
|
5 |
# fTools
|
6 |
# Copyright (C) 2008-2011 Carson Farmer
|
7 |
# EMAIL: carson.farmer (at) gmail.com
|
8 |
# WEB : http://www.ftools.ca/fTools.html
|
9 |
#
|
10 |
# A collection of data management and analysis tools for vector data
|
11 |
#
|
12 |
#-----------------------------------------------------------
|
13 |
#
|
14 |
# licensed under the terms of GNU GPL 2
|
15 |
#
|
16 |
# This program is free software; you can redistribute it and/or modify
|
17 |
# it under the terms of the GNU General Public License as published by
|
18 |
# the Free Software Foundation; either version 2 of the License, or
|
19 |
# (at your option) any later version.
|
20 |
#
|
21 |
# This program is distributed in the hope that it will be useful,
|
22 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
23 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
24 |
# GNU General Public License for more details.
|
25 |
#
|
26 |
# You should have received a copy of the GNU General Public License along
|
27 |
# with this program; if not, write to the Free Software Foundation, Inc.,
|
28 |
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
29 |
#
|
30 |
#---------------------------------------------------------------------
|
31 |
|
32 |
# Utility functions
|
33 |
# -------------------------------------------------
|
34 |
#
|
35 |
# convertFieldNameType( QgsField.name() )
|
36 |
# combineVectorFields( QgsVectorLayer, QgsVectorLayer )
|
37 |
# checkCRSCompatibility( QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem )
|
38 |
# writeVectorLayerToShape(QgsVectorLayer, QString *file path, QString *encoding style )
|
39 |
# getVectorTypeAsString( QgsVectorLayer )
|
40 |
# measurePerimeter( QgsGeometry )
|
41 |
# extractPoints( QgsGeometry )
|
42 |
# testForUniqueness( QList *QgsField, QList *QgsField )
|
43 |
# createUniqueFieldName( QgsField.name() )
|
44 |
# checkFieldNameLength( QgsFieldMap )
|
45 |
# getLayerNames( QGis.vectorType() )
|
46 |
# getFieldNames( QgsVectorLayer )
|
47 |
# getVectorLayerByName( QgsVectorLayer.name() )
|
48 |
# getFieldList( QgsVectorLayer )
|
49 |
# createIndex( QgsVectorDataProvider )
|
50 |
# addShapeToCanvas( QString *file path )
|
51 |
# getUniqueValues( QgsVectorDataProvider, int *field id )
|
52 |
# saveDialog( QWidget *parent )
|
53 |
# getFieldType( QgsVectorLayer, QgsField.name() )
|
54 |
# getUniqueValuesCount( QgsVectorLayer, int fieldIndex, bool useSelection ):
|
55 |
#
|
56 |
# -------------------------------------------------
|
57 |
|
58 |
from PyQt4.QtCore import * |
59 |
from PyQt4.QtGui import * |
60 |
from qgis.core import * |
61 |
from qgis.gui import * |
62 |
|
63 |
import locale |
64 |
|
65 |
# For use with memory provider/layer, converts full field type to simple string
|
66 |
def convertFieldNameType( inName ): |
67 |
if inName == "Integer": |
68 |
return "int" |
69 |
elif inName == "Real": |
70 |
return "double" |
71 |
else:
|
72 |
return "string" |
73 |
|
74 |
# From two input field maps, create single field map
|
75 |
def combineVectorFields( layerA, layerB ): |
76 |
fieldsA = layerA.dataProvider().fields() |
77 |
fieldsB = layerB.dataProvider().fields() |
78 |
fieldsB = testForUniqueness( fieldsA, fieldsB ) |
79 |
for f in fieldsB: |
80 |
fieldsA.append( f ) |
81 |
return fieldsA
|
82 |
|
83 |
# Check if two input CRSs are identical
|
84 |
def checkCRSCompatibility( crsA, crsB ): |
85 |
if crsA == crsB:
|
86 |
return True |
87 |
else:
|
88 |
return False |
89 |
|
90 |
# Convenience function to write vector layer to shapefile
|
91 |
def writeVectorLayerToShape( vlayer, outputPath, encoding ): |
92 |
mCodec = QTextCodec.codecForName( encoding ) |
93 |
if not mCodec: |
94 |
return False |
95 |
#Here we should check that the output path is valid
|
96 |
QgsVectorFileWriter.writeAsVectorFormat( vlayer, outputPath, encoding, vlayer.dataProvider().crs(), "ESRI Shapefile", False ) |
97 |
return True |
98 |
|
99 |
# For use with memory provider/layer, converts QGis vector type definition to simple string
|
100 |
def getVectorTypeAsString( vlayer ): |
101 |
if vlayer.geometryType() == QGis.Polygon:
|
102 |
return "Polygon" |
103 |
elif vlayer.geometryType() == QGis.Line:
|
104 |
return "LineString" |
105 |
elif vlayer.geometryType() == QGis.Point:
|
106 |
return "Point" |
107 |
else:
|
108 |
return False |
109 |
|
110 |
# Compute area and perimeter of input polygon geometry
|
111 |
def getAreaAndPerimeter( geom ): |
112 |
measure = QgsDistanceArea() |
113 |
area = measure.measure( geom ) |
114 |
perim = measurePerimeter( geom, measure ) |
115 |
return ( area, perim )
|
116 |
|
117 |
# Compute perimeter of input polygon geometry
|
118 |
def measurePerimeter( geom ): |
119 |
measure = QgsDistanceArea() |
120 |
value = 0.00
|
121 |
polygon = geom.asPolygon() |
122 |
for line in polygon: |
123 |
value += measure.measureLine( line ) |
124 |
return value
|
125 |
|
126 |
# Generate list of QgsPoints from input geometry ( can be point, line, or polygon )
|
127 |
def extractPoints( geom ): |
128 |
multi_geom = QgsGeometry() |
129 |
temp_geom = [] |
130 |
if geom.type() == 0: # it's a point |
131 |
if geom.isMultipart():
|
132 |
temp_geom = geom.asMultiPoint() |
133 |
else:
|
134 |
temp_geom.append(geom.asPoint()) |
135 |
if geom.type() == 1: # it's a line |
136 |
if geom.isMultipart():
|
137 |
multi_geom = geom.asMultiPolyline() #multi_geog is a multiline
|
138 |
for i in multi_geom: #i is a line |
139 |
temp_geom.extend( i ) |
140 |
else:
|
141 |
temp_geom = geom.asPolyline() |
142 |
elif geom.type() == 2: # it's a polygon |
143 |
if geom.isMultipart():
|
144 |
multi_geom = geom.asMultiPolygon() #multi_geom is a multipolygon
|
145 |
for i in multi_geom: #i is a polygon |
146 |
for j in i: #j is a line |
147 |
temp_geom.extend( j ) |
148 |
else:
|
149 |
multi_geom = geom.asPolygon() #multi_geom is a polygon
|
150 |
for i in multi_geom: #i is a line |
151 |
temp_geom.extend( i ) |
152 |
return temp_geom
|
153 |
|
154 |
# Check if two input field maps are unique, and resolve name issues if they aren't
|
155 |
def testForUniqueness( fieldList1, fieldList2 ): |
156 |
changed = True
|
157 |
while changed:
|
158 |
changed = False
|
159 |
for i in range(0,len(fieldList1)): |
160 |
for j in range(0,len(fieldList2)): |
161 |
if fieldList1[i].name() == fieldList2[j].name():
|
162 |
fieldList2[j] = createUniqueFieldName( fieldList2[j] ) |
163 |
changed = True
|
164 |
return fieldList2
|
165 |
|
166 |
# Create a unique field name based on input field name
|
167 |
def createUniqueFieldName( field ): |
168 |
check = field.name()[-2:]
|
169 |
shortName = field.name()[:8]
|
170 |
if check[0] == "_": |
171 |
try:
|
172 |
val = int( check[-1:] ) |
173 |
if val < 2: |
174 |
val = 2
|
175 |
else:
|
176 |
val = val + 1
|
177 |
field.setName( shortName[len( shortName )-1:] + unicode( val ) ) |
178 |
# except exceptions.ValueError: # the original file where exceptions are not defined
|
179 |
except:
|
180 |
field.setName( shortName + "_2" )
|
181 |
else:
|
182 |
field.setName( shortName + "_2" )
|
183 |
return field
|
184 |
|
185 |
# Return list of field names with more than 10 characters length
|
186 |
def checkFieldNameLength( fieldList ): |
187 |
longNames = [] |
188 |
for field in fieldList: |
189 |
if len ( field.name() ) > 10: |
190 |
longNames.append( field.name() ) |
191 |
return longNames
|
192 |
|
193 |
# Return list of names of all layers in QgsMapLayerRegistry
|
194 |
def getLayerNames( vTypes ): |
195 |
layermap = QgsMapLayerRegistry.instance().mapLayers() |
196 |
layerlist = [] |
197 |
if vTypes == "all": |
198 |
for name, layer in layermap.iteritems(): |
199 |
layerlist.append( layer.name() ) |
200 |
else:
|
201 |
for name, layer in layermap.iteritems(): |
202 |
if layer.type() == QgsMapLayer.VectorLayer:
|
203 |
if layer.geometryType() in vTypes: |
204 |
layerlist.append( layer.name() ) |
205 |
elif layer.type() == QgsMapLayer.RasterLayer:
|
206 |
if "Raster" in vTypes: |
207 |
layerlist.append( layer.name() ) |
208 |
return sorted( layerlist, cmp=locale.strcoll ) |
209 |
|
210 |
# Return list of names of all fields from input QgsVectorLayer
|
211 |
def getFieldNames( vlayer ): |
212 |
fieldmap = getFieldList( vlayer ) |
213 |
fieldlist = [] |
214 |
for field in fieldmap: |
215 |
if not field.name() in fieldlist: |
216 |
fieldlist.append( field.name() ) |
217 |
return sorted( fieldlist, cmp=locale.strcoll ) |
218 |
|
219 |
# Return QgsVectorLayer from a layer name ( as string )
|
220 |
def getVectorLayerByName( myName ): |
221 |
layermap = QgsMapLayerRegistry.instance().mapLayers() |
222 |
for name, layer in layermap.iteritems(): |
223 |
if layer.type() == QgsMapLayer.VectorLayer and layer.name() == myName: |
224 |
if layer.isValid():
|
225 |
return layer
|
226 |
else:
|
227 |
return None |
228 |
|
229 |
# Return QgsRasterLayer from a layer name ( as string )
|
230 |
def getRasterLayerByName( myName ): |
231 |
layermap = QgsMapLayerRegistry.instance().mapLayers() |
232 |
for name, layer in layermap.iteritems(): |
233 |
if layer.type() == QgsMapLayer.RasterLayer and layer.name() == myName: |
234 |
if layer.isValid():
|
235 |
return layer
|
236 |
else:
|
237 |
return None |
238 |
|
239 |
# Return QgsMapLayer from a layer name ( as string )
|
240 |
def getMapLayerByName( myName ): |
241 |
layermap = QgsMapLayerRegistry.instance().mapLayers() |
242 |
for name, layer in layermap.iteritems(): |
243 |
if layer.name() == myName:
|
244 |
if layer.isValid():
|
245 |
return layer
|
246 |
else:
|
247 |
return None |
248 |
|
249 |
# Return the field list of a vector layer
|
250 |
def getFieldList( vlayer ): |
251 |
return vlayer.dataProvider().fields()
|
252 |
|
253 |
# Convinience function to create a spatial index for input QgsVectorDataProvider
|
254 |
def createIndex( provider ): |
255 |
feat = QgsFeature() |
256 |
index = QgsSpatialIndex() |
257 |
fit = provider.getFeatures() |
258 |
while fit.nextFeature( feat ):
|
259 |
index.insertFeature( feat ) |
260 |
return index
|
261 |
|
262 |
# Convinience function to add a vector layer to canvas based on input shapefile path ( as string )
|
263 |
def addShapeToCanvas( shapefile_path ): |
264 |
file_info = QFileInfo( shapefile_path ) |
265 |
if file_info.exists():
|
266 |
layer_name = file_info.completeBaseName() |
267 |
else:
|
268 |
return False |
269 |
vlayer_new = QgsVectorLayer( shapefile_path, layer_name, "ogr" )
|
270 |
if vlayer_new.isValid():
|
271 |
QgsMapLayerRegistry.instance().addMapLayers( [vlayer_new] ) |
272 |
return True |
273 |
else:
|
274 |
return False |
275 |
|
276 |
# Return all unique values in field based on field index
|
277 |
def getUniqueValues( provider, index ): |
278 |
return provider.uniqueValues( index )
|
279 |
|
280 |
# Generate a save file dialog with a dropdown box for choosing encoding style
|
281 |
def saveDialog( parent, filtering="Shapefiles (*.shp *.SHP)"): |
282 |
settings = QSettings() |
283 |
dirName = settings.value( "/UI/lastShapefileDir" )
|
284 |
encode = settings.value( "/UI/encoding" )
|
285 |
fileDialog = QgsEncodingFileDialog( parent, "Save output shapefile", dirName, filtering, encode )
|
286 |
fileDialog.setDefaultSuffix( "shp" )
|
287 |
fileDialog.setFileMode( QFileDialog.AnyFile ) |
288 |
fileDialog.setAcceptMode( QFileDialog.AcceptSave ) |
289 |
fileDialog.setConfirmOverwrite( True )
|
290 |
if not fileDialog.exec_() == QDialog.Accepted: |
291 |
return None, None |
292 |
files = fileDialog.selectedFiles() |
293 |
settings.setValue("/UI/lastShapefileDir", QFileInfo( unicode( files[0] ) ).absolutePath() ) |
294 |
return ( unicode( files[0] ), unicode( fileDialog.encoding() ) ) |
295 |
|
296 |
# Generate a save file dialog with a dropdown box for choosing encoding style
|
297 |
# with mode="SingleFile" will allow to select only one file, in other cases - several files
|
298 |
def openDialog( parent, filtering="Shapefiles (*.shp *.SHP)", dialogMode="SingleFile"): |
299 |
settings = QSettings() |
300 |
dirName = settings.value( "/UI/lastShapefileDir" )
|
301 |
encode = settings.value( "/UI/encoding" )
|
302 |
fileDialog = QgsEncodingFileDialog( parent, "Save output shapefile", dirName, filtering, encode )
|
303 |
fileDialog.setFileMode( QFileDialog.ExistingFiles ) |
304 |
fileDialog.setAcceptMode( QFileDialog.AcceptOpen ) |
305 |
if not fileDialog.exec_() == QDialog.Accepted: |
306 |
return None, None |
307 |
files = fileDialog.selectedFiles() |
308 |
settings.setValue("/UI/lastShapefileDir", QFileInfo( unicode( files[0] ) ).absolutePath() ) |
309 |
if dialogMode == "SingleFile": |
310 |
return ( unicode( files[0] ), unicode( fileDialog.encoding() ) ) |
311 |
else:
|
312 |
return ( files, unicode( fileDialog.encoding() ) ) |
313 |
|
314 |
# Generate a select directory dialog with a dropdown box for choosing encoding style
|
315 |
def dirDialog( parent ): |
316 |
settings = QSettings() |
317 |
dirName = settings.value( "/UI/lastShapefileDir" )
|
318 |
encode = settings.value( "/UI/encoding" )
|
319 |
fileDialog = QgsEncodingFileDialog( parent, "Save output shapefile", dirName, encode )
|
320 |
fileDialog.setFileMode( QFileDialog.DirectoryOnly ) |
321 |
fileDialog.setAcceptMode( QFileDialog.AcceptSave ) |
322 |
fileDialog.setConfirmOverwrite( False )
|
323 |
if not fileDialog.exec_() == QDialog.Accepted: |
324 |
return None, None |
325 |
folders = fileDialog.selectedFiles() |
326 |
settings.setValue("/UI/lastShapefileDir", QFileInfo( unicode( folders[0] ) ).absolutePath() ) |
327 |
return ( unicode( folders[0] ), unicode( fileDialog.encoding() ) ) |
328 |
|
329 |
# Return field type from it's name
|
330 |
def getFieldType(vlayer, fieldName): |
331 |
for field in vlayer.dataProvider().fields(): |
332 |
if field.name() == fieldName:
|
333 |
return field.typeName()
|
334 |
|
335 |
# return the number of unique values in field
|
336 |
def getUniqueValuesCount( vlayer, fieldIndex, useSelection ): |
337 |
count = 0
|
338 |
values = [] |
339 |
if useSelection:
|
340 |
selection = vlayer.selectedFeatures() |
341 |
for f in selection: |
342 |
v = f.attributes()[ fieldIndex ] |
343 |
if v not in values: |
344 |
values.append( v ) |
345 |
count += 1
|
346 |
else:
|
347 |
feat = QgsFeature() |
348 |
fit = vlayer.dataProvider().getFeatures() |
349 |
while fit.nextFeature( feat ):
|
350 |
v = feat.attributes()[ fieldIndex ] |
351 |
if v not in values: |
352 |
values.append( v ) |
353 |
count += 1
|
354 |
return count
|
355 |
|
356 |
def getGeomType(gT): |
357 |
if gT == 3 or gT == 6: |
358 |
gTypeListPoly = [ QGis.WKBPolygon, QGis.WKBMultiPolygon ] |
359 |
return gTypeListPoly
|
360 |
elif gT == 2 or gT == 5: |
361 |
gTypeListLine = [ QGis.WKBLineString, QGis.WKBMultiLineString ] |
362 |
return gTypeListLine
|
363 |
elif gT == 1 or gT == 4: |
364 |
gTypeListPoint = [ QGis.WKBPoint, QGis.WKBMultiPoint ] |
365 |
return gTypeListPoint
|
366 |
|
367 |
def getShapesByGeometryType( baseDir, inShapes, geomType ): |
368 |
outShapes = [] |
369 |
for fileName in inShapes: |
370 |
layerPath = QFileInfo( baseDir + "/" + fileName ).absoluteFilePath()
|
371 |
vLayer = QgsVectorLayer( layerPath, QFileInfo( layerPath ).baseName(), "ogr" )
|
372 |
if not vLayer.isValid(): |
373 |
continue
|
374 |
layerGeometry = vLayer.geometryType() |
375 |
if layerGeometry == QGis.Polygon and geomType == 0: |
376 |
outShapes.append(fileName) |
377 |
elif layerGeometry == QGis.Line and geomType == 1: |
378 |
outShapes.append(fileName) |
379 |
elif layerGeometry == QGis.Point and geomType == 2: |
380 |
outShapes.append(fileName) |
381 |
|
382 |
if len(outShapes) == 0: |
383 |
return None |
384 |
|
385 |
return outShapes
|
386 |
|
387 |
def getShapefileName( outPath, extension='.shp' ): |
388 |
import os.path |
389 |
outName=os.path.basename(outPath) |
390 |
if outName.endswith(extension):
|
391 |
outName=outName[:-len(extension)]
|
392 |
return outName
|
393 |
|