[QGIS Commit] r14744 - in trunk/qgis/python/plugins/fTools: .
icons/default icons/gis tools
svn_qgis at osgeo.org
svn_qgis at osgeo.org
Mon Nov 22 17:49:40 EST 2010
Author: cfarmer
Date: 2010-11-22 14:49:39 -0800 (Mon, 22 Nov 2010)
New Revision: 14744
Added:
trunk/qgis/python/plugins/fTools/icons/default/voronoi.png
trunk/qgis/python/plugins/fTools/icons/gis/voronoi.png
Modified:
trunk/qgis/python/plugins/fTools/fTools.py
trunk/qgis/python/plugins/fTools/resources.qrc
trunk/qgis/python/plugins/fTools/tools/doGeometry.py
trunk/qgis/python/plugins/fTools/tools/voronoi.py
Log:
[FEATURE] Adds voronoi polygon tool to Vector menu, addresses #2825; Also (partially) addresses #2546
Modified: trunk/qgis/python/plugins/fTools/fTools.py
===================================================================
--- trunk/qgis/python/plugins/fTools/fTools.py 2010-11-22 22:32:03 UTC (rev 14743)
+++ trunk/qgis/python/plugins/fTools/fTools.py 2010-11-22 22:49:39 UTC (rev 14744)
@@ -103,7 +103,7 @@
self.checkGeom.setIcon( QIcon( self.getThemeIcon( "check_geometry.png") ) )
self.centroids.setIcon( QIcon( self.getThemeIcon( "centroids.png") ) )
self.delaunay.setIcon( QIcon( self.getThemeIcon( "delaunay.png") ) )
- self.voronoi.setIcon( QIcon( self.getThemeIcon( "delaunay.png") ) )
+ self.voronoi.setIcon( QIcon( self.getThemeIcon( "voronoi.png") ) )
self.extNodes.setIcon( QIcon( self.getThemeIcon( "extract_nodes.png") ) )
self.simplify.setIcon( QIcon( self.getThemeIcon( "simplify.png") ) )
self.multiToSingle.setIcon( QIcon( self.getThemeIcon( "multi_to_single.png") ) )
Added: trunk/qgis/python/plugins/fTools/icons/default/voronoi.png
===================================================================
(Binary files differ)
Property changes on: trunk/qgis/python/plugins/fTools/icons/default/voronoi.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/qgis/python/plugins/fTools/icons/gis/voronoi.png
===================================================================
(Binary files differ)
Property changes on: trunk/qgis/python/plugins/fTools/icons/gis/voronoi.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Modified: trunk/qgis/python/plugins/fTools/resources.qrc
===================================================================
--- trunk/qgis/python/plugins/fTools/resources.qrc 2010-11-22 22:32:03 UTC (rev 14743)
+++ trunk/qgis/python/plugins/fTools/resources.qrc 2010-11-22 22:49:39 UTC (rev 14744)
@@ -1,24 +1,24 @@
<RCC>
- <qresource>
- <file>icons/default/single_to_multi.png</file>
- <file>icons/default/simplify.png</file>
- <file>icons/default/difference.png</file>
- <file>icons/default/geometry.png</file>
- <file>icons/default/check_geometry.png</file>
- <file>icons/default/to_lines.png</file>
- <file>icons/default/multi_to_single.png</file>
- <file>icons/default/mean.png</file>
- <file>icons/default/select_location.png</file>
- <file>icons/default/union.png</file>
- <file>icons/default/dissolve.png</file>
- <file>icons/default/clip.png</file>
- <file>icons/default/sym_difference.png</file>
- <file>icons/default/intersect.png</file>
- <file>icons/default/analysis.png</file>
- <file>icons/default/geoprocessing.png</file>
- <file>icons/default/sampling.png</file>
- <file>icons/default/management.png</file>
- <file>icons/default/ftools_logo.png</file>
+ <qresource prefix="/">
+ <file>icons/default/single_to_multi.png</file>
+ <file>icons/default/simplify.png</file>
+ <file>icons/default/difference.png</file>
+ <file>icons/default/geometry.png</file>
+ <file>icons/default/check_geometry.png</file>
+ <file>icons/default/to_lines.png</file>
+ <file>icons/default/multi_to_single.png</file>
+ <file>icons/default/mean.png</file>
+ <file>icons/default/select_location.png</file>
+ <file>icons/default/union.png</file>
+ <file>icons/default/dissolve.png</file>
+ <file>icons/default/clip.png</file>
+ <file>icons/default/sym_difference.png</file>
+ <file>icons/default/intersect.png</file>
+ <file>icons/default/analysis.png</file>
+ <file>icons/default/geoprocessing.png</file>
+ <file>icons/default/sampling.png</file>
+ <file>icons/default/management.png</file>
+ <file>icons/default/ftools_logo.png</file>
<file>icons/default/centroids.png</file>
<file>icons/default/export_geometry.png</file>
<file>icons/default/define_projection.png</file>
@@ -42,28 +42,28 @@
<file>icons/default/vector_grid.png</file>
<file>icons/default/split_layer.png</file>
<file>icons/default/merge_shapes.png</file>
- <file>icons/default/neighbour.png</file>
- <file>icons/default/delaunay.png</file>
- <file>icons/default/layer_extent.png</file>
- <file>icons/gis/single_to_multi.png</file>
- <file>icons/gis/simplify.png</file>
- <file>icons/gis/difference.png</file>
- <file>icons/gis/geometry.png</file>
- <file>icons/gis/check_geometry.png</file>
- <file>icons/gis/to_lines.png</file>
- <file>icons/gis/multi_to_single.png</file>
- <file>icons/gis/mean.png</file>
- <file>icons/gis/select_location.png</file>
- <file>icons/gis/union.png</file>
- <file>icons/gis/dissolve.png</file>
- <file>icons/gis/clip.png</file>
- <file>icons/gis/sym_difference.png</file>
- <file>icons/gis/intersect.png</file>
- <file>icons/gis/analysis.png</file>
- <file>icons/gis/geoprocessing.png</file>
- <file>icons/gis/sampling.png</file>
- <file>icons/gis/management.png</file>
- <file>icons/gis/ftools_logo.png</file>
+ <file>icons/default/neighbour.png</file>
+ <file>icons/default/delaunay.png</file>
+ <file>icons/default/layer_extent.png</file>
+ <file>icons/gis/single_to_multi.png</file>
+ <file>icons/gis/simplify.png</file>
+ <file>icons/gis/difference.png</file>
+ <file>icons/gis/geometry.png</file>
+ <file>icons/gis/check_geometry.png</file>
+ <file>icons/gis/to_lines.png</file>
+ <file>icons/gis/multi_to_single.png</file>
+ <file>icons/gis/mean.png</file>
+ <file>icons/gis/select_location.png</file>
+ <file>icons/gis/union.png</file>
+ <file>icons/gis/dissolve.png</file>
+ <file>icons/gis/clip.png</file>
+ <file>icons/gis/sym_difference.png</file>
+ <file>icons/gis/intersect.png</file>
+ <file>icons/gis/analysis.png</file>
+ <file>icons/gis/geoprocessing.png</file>
+ <file>icons/gis/sampling.png</file>
+ <file>icons/gis/management.png</file>
+ <file>icons/gis/ftools_logo.png</file>
<file>icons/gis/centroids.png</file>
<file>icons/gis/export_geometry.png</file>
<file>icons/gis/define_projection.png</file>
@@ -86,8 +86,10 @@
<file>icons/gis/sum_lines.png</file>
<file>icons/gis/vector_grid.png</file>
<file>icons/gis/split_layer.png</file>
- <file>icons/gis/neighbour.png</file>
- <file>icons/gis/delaunay.png</file>
- <file>icons/gis/layer_extent.png</file>
+ <file>icons/gis/neighbour.png</file>
+ <file>icons/gis/delaunay.png</file>
+ <file>icons/gis/layer_extent.png</file>
+ <file>icons/gis/voronoi.png</file>
+ <file>icons/default/voronoi.png</file>
</qresource>
</RCC>
Modified: trunk/qgis/python/plugins/fTools/tools/doGeometry.py
===================================================================
--- trunk/qgis/python/plugins/fTools/tools/doGeometry.py 2010-11-22 22:32:03 UTC (rev 14743)
+++ trunk/qgis/python/plugins/fTools/tools/doGeometry.py 2010-11-22 22:49:39 UTC (rev 14744)
@@ -6,6 +6,8 @@
import ftools_utils
import math
from itertools import izip
+import voronoi
+from sets import Set
class GeometryDialog(QDialog, Ui_Dialog):
@@ -38,7 +40,7 @@
QMessageBox.information(self, self.tr("Geometry"), self.tr( "Please specify input vector layer" ) )
elif self.outShape.text() == "":
QMessageBox.information(self, self.tr("Geometry"), self.tr( "Please specify output shapefile" ) )
- elif self.lineEdit.isVisible() and self.lineEdit.value() <= 0.00:
+ elif self.lineEdit.isVisible() and self.lineEdit.value() < 0.00:
QMessageBox.information(self, self.tr("Geometry"), self.tr( "Please specify valid tolerance value" ) )
elif self.cmbField.isVisible() and self.cmbField.currentText() == "":
QMessageBox.information(self, self.tr("Geometry"), self.tr( "Please specify valid UID field" ) )
@@ -102,12 +104,22 @@
if self.myFunction == 8: # Delaunay triangulation
self.setWindowTitle( self.tr( "Delaunay triangulation" ) )
self.label_3.setText( self.tr( "Input point vector layer" ) )
+ self.label.setVisible( False )
+ self.lineEdit.setVisible( False )
+ elif self.myFunction == 10: # Voronoi Polygons
+ self.setWindowTitle( self.tr( "Voronoi polygon" ) )
+ self.label_3.setText( self.tr( "Input point vector layer" ) )
+ self.label.setText( self.tr( "Buffer region" ) )
+ self.lineEdit.setSuffix(" %")
+ self.lineEdit.setRange(0, 100)
+ self.lineEdit.setSingleStep(5)
+ self.lineEdit.setValue(0)
else: # Polygon from layer extent
self.setWindowTitle( self.tr( "Polygon from layer extent" ) )
self.label_3.setText( self.tr( "Input layer" ) )
+ self.label.setVisible( False )
+ self.lineEdit.setVisible( False )
self.label_2.setText( self.tr( "Output polygon shapefile" ) )
- self.label.setVisible( False )
- self.lineEdit.setVisible( False )
self.cmbField.setVisible( False )
self.field_label.setVisible( False )
self.resize( 381, 100 )
@@ -117,7 +129,7 @@
myList = ftools_utils.getLayerNames( [ QGis.Polygon, QGis.Line ] )
elif self.myFunction == 4 or self.myFunction == 7:
myList = ftools_utils.getLayerNames( [ QGis.Polygon ] )
- elif self.myFunction == 8:
+ elif self.myFunction == 8 or self.myFunction == 10:
myList = ftools_utils.getLayerNames( [ QGis.Point ] )
elif self.myFunction == 9:
myList = ftools_utils.getLayerNames( "all" )
@@ -135,6 +147,7 @@
#7: Polygon centroids
#8: Delaunay triangulation
#9: Polygon from layer extent
+#10:Voronoi polygons
def geometry( self, myLayer, myParam, myField ):
if self.myFunction == 9:
@@ -164,6 +177,7 @@
def runFinishedFromThread( self, success ):
self.testThread.stop()
self.buttonOk.setEnabled( True )
+ extra = ""
if success == "math_error":
QMessageBox.warning( self, self.tr("Geometry"), self.tr("Error processing specified tolerance!\nPlease choose larger tolerance...") )
if not QgsVectorFileWriter.deleteShapeFile( self.shapefileName ):
@@ -173,10 +187,16 @@
if not QgsVectorFileWriter.deleteShapeFile( self.shapefileName ):
QMessageBox.warning( self, self.tr("Geometry"), self.tr( "Unable to delete incomplete shapefile." ) )
else:
+ if success == "valid_error":
+ extra = self.tr("One or more features in the output layer may have invalid "
+ + "geometry, please check using the check validity tool\n")
+ success = True
self.cancel_close.setText( "Close" )
QObject.disconnect( self.cancel_close, SIGNAL( "clicked()" ), self.cancelThread )
if success:
- addToTOC = QMessageBox.question( self, self.tr("Geometry"), self.tr( "Created output shapefile:\n%1\n\nWould you like to add the new layer to the TOC?" ).arg( unicode( self.shapefileName ) ), QMessageBox.Yes, QMessageBox.No, QMessageBox.NoButton )
+ addToTOC = QMessageBox.question( self, self.tr("Geometry"),
+ self.tr( "Created output shapefile:\n%1\n%2\n\nWould you like to add the new layer to the TOC?" ).arg( unicode( self.shapefileName ) ).arg( extra ),
+ QMessageBox.Yes, QMessageBox.No, QMessageBox.NoButton )
if addToTOC == QMessageBox.Yes:
if not ftools_utils.addShapeToCanvas( unicode( self.shapefileName ) ):
QMessageBox.warning( self, self.tr("Geoprocessing"), self.tr( "Error loading output shapefile:\n%1" ).arg( unicode( self.shapefileName ) ))
@@ -220,6 +240,8 @@
success = self.delaunay_triangulation()
elif self.myFunction == 9: # Polygon from layer extent
success = self.layer_extent()
+ elif self.myFunction == 10: # Voronoi Polygons
+ success = self.voronoi_polygons()
self.emit( SIGNAL( "runFinished(PyQt_PyObject)" ), success )
self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 )
@@ -231,8 +253,10 @@
allAttrs = vprovider.attributeIndexes()
vprovider.select( allAttrs )
fields = vprovider.fields()
+ allValid = True
+ geomType = self.singleToMultiGeom(vprovider.geometryType())
writer = QgsVectorFileWriter( self.myName, self.myEncoding,
- fields, vprovider.geometryType(), vprovider.crs() )
+ fields, geomType, vprovider.crs() )
inFeat = QgsFeature()
outFeat = QgsFeature()
inGeom = QgsGeometry()
@@ -272,13 +296,15 @@
nElement += 1
self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), nElement )
outFeat.setAttributeMap( atts )
- outGeom = QgsGeometry( self.convertGeometry( multi_feature, vType ) )
- outFeat.setGeometry( outGeom )
- writer.addFeature( outFeat )
+ outGeom = QgsGeometry( self.convertGeometry(multi_feature, vType) )
+ if not outGeom.isGeosValid():
+ allValid = "valid_error"
+ outFeat.setGeometry(outGeom)
+ writer.addFeature(outFeat)
del writer
else:
return "attr_error"
- return True
+ return allValid
def multi_to_single( self ):
vprovider = self.vlayer.dataProvider()
@@ -424,6 +450,7 @@
def delaunay_triangulation( self ):
import voronoi
+ from sets import Set
vprovider = self.vlayer.dataProvider()
allAttrs = vprovider.attributeIndexes()
vprovider.select( allAttrs )
@@ -434,39 +461,184 @@
writer = QgsVectorFileWriter( self.myName, self.myEncoding,
fields, QGis.WKBPolygon, vprovider.crs() )
inFeat = QgsFeature()
- points = []
- while vprovider.nextFeature( inFeat ):
- inGeom = QgsGeometry( inFeat.geometry() )
- point = inGeom.asPoint()
- points.append( point )
- vprovider.rewind()
- vprovider.select( allAttrs )
- triangles = voronoi.computeDelaunayTriangulation( points )
+ c = voronoi.Context()
+ pts = []
+ while vprovider.nextFeature(inFeat):
+ geom = QgsGeometry(inFeat.geometry())
+ point = geom.asPoint()
+ x = point.x()
+ y = point.y()
+ pts.append((x, y))
+ if len(pts) < 3:
+ return False
+ uniqueSet = Set(item for item in pts)
+ ids = [pts.index(item) for item in uniqueSet]
+ sl = voronoi.SiteList([voronoi.Site(*i) for i in uniqueSet])
+ c.triangulate = True
+ voronoi.voronoi(sl, c)
+ triangles = c.triangles
feat = QgsFeature()
nFeat = len( triangles )
nElement = 0
self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 )
self.emit( SIGNAL( "runRange(PyQt_PyObject)" ), ( 0, nFeat ) )
for triangle in triangles:
- indicies = list( triangle )
- indicies.append( indicies[ 0 ] )
+ indicies = list(triangle)
+ indicies.append(indicies[0])
polygon = []
step = 0
for index in indicies:
- vprovider.featureAtId( index, inFeat, True, allAttrs )
- geom = QgsGeometry( inFeat.geometry() )
- point = QgsPoint( geom.asPoint() )
- polygon.append( point )
- if step <= 3: feat.addAttribute( step, QVariant( index ) )
+ vprovider.featureAtId( ids[index], inFeat, True, allAttrs )
+ geom = QgsGeometry(inFeat.geometry())
+ point = QgsPoint(geom.asPoint())
+ polygon.append(point)
+ if step <= 3: feat.addAttribute(step, QVariant(ids[index]))
step += 1
- geometry = QgsGeometry().fromPolygon( [ polygon ] )
- feat.setGeometry( geometry )
- writer.addFeature( feat )
+ geometry = QgsGeometry().fromPolygon([polygon])
+ feat.setGeometry(geometry)
+ writer.addFeature(feat)
nElement += 1
self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), nElement )
del writer
return True
+ def voronoi_polygons( self ):
+ vprovider = self.vlayer.dataProvider()
+ allAttrs = vprovider.attributeIndexes()
+ vprovider.select( allAttrs )
+ writer = QgsVectorFileWriter( self.myName, self.myEncoding,
+ vprovider.fields(), QGis.WKBPolygon, vprovider.crs() )
+ inFeat = QgsFeature()
+ outFeat = QgsFeature()
+ extent = self.vlayer.extent()
+ extraX = extent.height()*(self.myParam/100.00)
+ extraY = extent.width()*(self.myParam/100.00)
+ height = extent.height()
+ width = extent.width()
+ c = voronoi.Context()
+ pts = []
+ while vprovider.nextFeature(inFeat):
+ geom = QgsGeometry(inFeat.geometry())
+ point = geom.asPoint()
+ x = point.x()-extent.xMinimum()
+ y = point.y()-extent.yMinimum()
+ pts.append((x, y))
+ self.vlayer = None
+ if len(pts) < 3:
+ return False
+ uniqueSet = Set(item for item in pts)
+ ids = [pts.index(item) for item in uniqueSet]
+ sl = voronoi.SiteList([voronoi.Site(*i, sitenum=j) for j, i in enumerate(uniqueSet)])
+ voronoi.voronoi(sl, c)
+ inFeat = QgsFeature()
+ nFeat = len(c.polygons)
+ nElement = 0
+ self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 )
+ self.emit( SIGNAL( "runRange(PyQt_PyObject)" ), ( 0, nFeat ) )
+ for site, edges in c.polygons.iteritems():
+ vprovider.featureAtId(ids[site], inFeat, True, allAttrs)
+ lines = self.clip_voronoi(edges, c, width, height, extent, extraX, extraY)
+ geom = QgsGeometry.fromMultiPoint(lines)
+ geom = QgsGeometry(geom.convexHull())
+ outFeat.setGeometry(geom)
+ outFeat.setAttributeMap(inFeat.attributeMap())
+ writer.addFeature(outFeat)
+ nElement += 1
+ self.emit(SIGNAL("runStatus(PyQt_PyObject)" ), nElement)
+ del writer
+ return True
+
+
+ def clip_voronoi(self, edges, c, width, height, extent, exX, exY):
+ def clip_line(x1, y1, x2, y2, w, h, x, y):
+ if x1 < 0-x and x2 < 0-x:
+ return [0, 0, 0, 0]
+ if x1 > w+x and x2 > w+x:
+ return [0, 0, 0, 0]
+ if x1 < 0-x:
+ y1 = (y1*x2 - y2*x1)/(x2 - x1)
+ x1 = 0-x
+ if x2 < 0-x:
+ y2 = (y1*x2 - y2*x1)/(x2 - x1)
+ x2 = 0-x
+ if x1 > w+x:
+ y1 = y1 + (w+x - x1)*(y2 - y1)/(x2 - x1)
+ x1 = w+x
+ if x2 > w+x:
+ y2 = y1 + (w+x - x1)*(y2 - y1)/(x2 - x1)
+ x2 = w+x
+ if y1 < 0-y and y2 < 0-y:
+ return [0, 0, 0, 0]
+ if y1 > h+y and y2 > h+y:
+ return [0, 0, 0, 0]
+ if x1 == x2 and y1 == y2:
+ return [0, 0, 0, 0]
+ if y1 < 0-y:
+ x1 = (x1*y2 - x2*y1)/(y2 - y1)
+ y1 = 0-y
+ if y2 < 0-y:
+ x2 = (x1*y2 - x2*y1)/(y2 - y1)
+ y2 = 0-y
+ if y1 > h+y:
+ x1 = x1 + (h+y - y1)*(x2 - x1)/(y2 - y1)
+ y1 = h+y
+ if y2 > h+y:
+ x2 = x1 + (h+y - y1)*(x2 - x1)/(y2 - y1)
+ y2 = h+y
+ return [x1, y1, x2, y2]
+ lines = []
+ hasXMin = False
+ hasYMin = False
+ hasXMax = False
+ hasYMax = False
+ for edge in edges:
+ if edge[1] >= 0 and edge[2] >= 0: # two vertices
+ [x1, y1, x2, y2] = clip_line(c.vertices[edge[1]][0], c.vertices[edge[1]][1], c.vertices[edge[2]][0], c.vertices[edge[2]][1], width, height, exX, exY)
+ elif edge[1] >= 0: # only one vertex
+ if c.lines[edge[0]][1] == 0: # vertical line
+ xtemp = c.lines[edge[0]][2]/c.lines[edge[0]][0]
+ if c.vertices[edge[1]][1] > (height+exY)/2:
+ ytemp = height+exY
+ else:
+ ytemp = 0-exX
+ else:
+ xtemp = width+exX
+ ytemp = (c.lines[edge[0]][2] - (width+exX)*c.lines[edge[0]][0])/c.lines[edge[0]][1]
+ [x1, y1, x2, y2] = clip_line(c.vertices[edge[1]][0], c.vertices[edge[1]][1], xtemp, ytemp, width, height, exX, exY)
+ elif edge[2] >= 0: # only one vertex
+ if c.lines[edge[0]][1] == 0: # vertical line
+ xtemp = c.lines[edge[0]][2]/c.lines[edge[0]][0]
+ if c.vertices[edge[2]][1] > (height+exY)/2:
+ ytemp = height+exY
+ else:
+ ytemp = 0.0-exY
+ else:
+ xtemp = 0.0-exX
+ ytemp = c.lines[edge[0]][2]/c.lines[edge[0]][1]
+ [x1, y1, x2, y2] = clip_line(xtemp, ytemp, c.vertices[edge[2]][0], c.vertices[edge[2]][1], width, height, exX, exY)
+ if x1 or x2 or y1 or y2:
+ lines.append(QgsPoint(x1+extent.xMinimum(),y1+extent.yMinimum()))
+ lines.append(QgsPoint(x2+extent.xMinimum(),y2+extent.yMinimum()))
+ if 0-exX in (x1, x2):
+ hasXMin = True
+ if 0-exY in (y1, y2):
+ hasYMin = True
+ if height+exY in (y1, y2):
+ hasYMax = True
+ if width+exX in (x1, x2):
+ hasXMax = True
+ if hasXMin:
+ if hasYMax:
+ lines.append(QgsPoint(extent.xMinimum()-exX, height+extent.yMinimum()+exY))
+ if hasYMin:
+ lines.append(QgsPoint(extent.xMinimum()-exX, extent.yMinimum()-exY))
+ if hasXMax:
+ if hasYMax:
+ lines.append(QgsPoint(width+extent.xMinimum()+exX, height+extent.yMinimum()+exY))
+ if hasYMin:
+ lines.append(QgsPoint(width+extent.xMinimum()+exX, extent.yMinimum()-exY))
+ return lines
+
def layer_extent( self ):
self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 )
self.emit( SIGNAL( "runRange(PyQt_PyObject)" ), ( 0, 0 ) )
@@ -615,6 +787,22 @@
else:
return []
+ def singleToMultiGeom(self, wkbType):
+ try:
+ if wkbType in (QGis.WKBPoint, QGis.WKBMultiPoint,
+ QGis.WKBPoint25D, QGis.WKBMultiPoint25D):
+ return QGis.WKBMultiPoint
+ elif wkbType in (QGis.WKBLineString, QGis.WKBMultiLineString,
+ QGis.WKBMultiLineString25D, QGis.WKBLineString25D):
+ return QGis.WKBMultiLineString
+ elif wkbType in (QGis.WKBPolygon, QGis.WKBMultiPolygon,
+ QGis.WKBMultiPolygon25D, QGis.WKBPolygon25D):
+ return QGis.WKBMultiPolygon
+ else:
+ return QGis.WKBUnknown
+ except Exception, err:
+ print str(err)
+
def extractAsSingle( self, geom ):
multi_geom = QgsGeometry()
temp_geom = []
Modified: trunk/qgis/python/plugins/fTools/tools/voronoi.py
===================================================================
--- trunk/qgis/python/plugins/fTools/tools/voronoi.py 2010-11-22 22:32:03 UTC (rev 14743)
+++ trunk/qgis/python/plugins/fTools/tools/voronoi.py 2010-11-22 22:49:39 UTC (rev 14744)
@@ -100,7 +100,7 @@
BIG_FLOAT = 1e38
#------------------------------------------------------------------
-class Context( object ):
+class Context(object):
def __init__(self):
self.doPrint = 0
self.debug = 0
@@ -110,41 +110,13 @@
self.lines = [] # equation of line 3-tuple (a b c), for the equation of the line a*x+b*y = c
self.edges = [] # edge 3-tuple: (line index, vertex 1 index, vertex 2 index) if either vertex index is -1, the edge extends to infiinity
self.triangles = [] # 3-tuple of vertex indices
-# self.extra_edges = [] # list of additional vertex 2-tubles (x,y) based on bounded voronoi tesselation
-# self.set_bounds(None)
-# self.use_bound = False
- self.xmin = self.ymin = self.xmax = self.ymax = None
+ self.polygons = {} # a dict of site:[edges] pairs
def circle(self,x,y,rad):
pass
- def clip_line(self,edge,lid,rid):
+ def clip_line(self,edge):
pass
- # here is where I will create false verticies if
- # the voronoi line extends to infinity...
- # the extra verticies will be added to the
- # extra edges list as 2-tuples
-# a,b,c = edge.a,edge.b,edge.c
-# if lid == -1:
-# x = self.xMin
-# y = (c-a*x) / b
-# if y < self.yMin or y > self.yMax:
-# if y < self.yMin: y = self.yMin
-# elif y > self.yMax: y = self.yMax
-# x = (c-b*y) / a
-# self.extra_edges.append((x,y))
-# lid = -(len(self.extra_edges)-1)
-# if rid == -1:
-# x = self.xMax
-# y = (c-a*x) / b
-# if y < self.yMin or y > self.yMax:
-# if y < self.yMin: y = self.yMin
-# elif y > self.yMax: y = self.yMax
-# x = (c-b*y) / a
-# self.extra_edges.append((x,y))
-# rid = -(len(self.extra_edges)-1)
-# print lid,rid
-# return (lid,rid)
def line(self,x0,y0,x1,y1):
pass
@@ -155,17 +127,12 @@
elif(self.triangulate):
pass
elif(self.plot):
- self.circle (s.x, s.y, 3) #cradius)
+ self.circle (s.x, s.y, cradius)
elif(self.doPrint):
print "s %f %f" % (s.x, s.y)
def outVertex(self,s):
self.vertices.append((s.x,s.y))
- if s.x < self.xmin: self.xmin = s.x
- elif s.x > self.xmax: self.xmax = s.x
- if s.y < self.ymin: self.ymin = s.y
- elif s.y > self.ymax: self.ymax = s.y
-
if(self.debug):
print "vertex(%d) at %f %f" % (s.sitenum, s.x, s.y)
elif(self.triangulate):
@@ -197,10 +164,12 @@
sitenumR = -1
if edge.ep[Edge.RE] is not None:
sitenumR = edge.ep[Edge.RE].sitenum
-
-# if sitenumL == -1 or sitenumR == -1 and self.use_bound:
-# sitenumL,sitenumR = self.clip_line(edge,sitenumL,sitenumR)
-
+ if edge.reg[0].sitenum not in self.polygons:
+ self.polygons[edge.reg[0].sitenum] = []
+ if edge.reg[1].sitenum not in self.polygons:
+ self.polygons[edge.reg[1].sitenum] = []
+ self.polygons[edge.reg[0].sitenum].append((edge.edgenum,sitenumL,sitenumR))
+ self.polygons[edge.reg[1].sitenum].append((edge.edgenum,sitenumL,sitenumR))
self.edges.append((edge.edgenum,sitenumL,sitenumR))
if(not self.triangulate):
if self.plot:
@@ -210,153 +179,148 @@
print " %d " % sitenumL,
print "%d" % sitenumR
- def set_bounds(self,bounds):
- if not bounds == None:
- self.xmin = bounds.xmin
- self.ymin = bounds.ymin
- self.xmax = bounds.xmax
- self.ymax = bounds.ymax
- else:
- self.xmin = self.ymin = self.xmax = self.ymax = None
-
-
#------------------------------------------------------------------
def voronoi(siteList,context):
- edgeList = EdgeList(siteList.xmin,siteList.xmax,len(siteList))
- priorityQ = PriorityQueue(siteList.ymin,siteList.ymax,len(siteList))
- siteIter = siteList.iterator()
-
- bottomsite = siteIter.next()
- context.outSite(bottomsite)
- newsite = siteIter.next()
- minpt = Site(-BIG_FLOAT,-BIG_FLOAT)
- while True:
- if not priorityQ.isEmpty():
- minpt = priorityQ.getMinPt()
+ try:
+ edgeList = EdgeList(siteList.xmin,siteList.xmax,len(siteList))
+ priorityQ = PriorityQueue(siteList.ymin,siteList.ymax,len(siteList))
+ siteIter = siteList.iterator()
+
+ bottomsite = siteIter.next()
+ context.outSite(bottomsite)
+ newsite = siteIter.next()
+ minpt = Site(-BIG_FLOAT,-BIG_FLOAT)
+ while True:
+ if not priorityQ.isEmpty():
+ minpt = priorityQ.getMinPt()
- if (newsite and (priorityQ.isEmpty() or cmp(newsite,minpt) < 0)):
- # newsite is smallest - this is a site event
- context.outSite(newsite)
-
- # get first Halfedge to the LEFT and RIGHT of the new site
- lbnd = edgeList.leftbnd(newsite)
- rbnd = lbnd.right
-
- # if this halfedge has no edge, bot = bottom site (whatever that is)
- # create a new edge that bisects
- bot = lbnd.rightreg(bottomsite)
- edge = Edge.bisect(bot,newsite)
- context.outBisector(edge)
-
- # create a new Halfedge, setting its pm field to 0 and insert
- # this new bisector edge between the left and right vectors in
- # a linked list
- bisector = Halfedge(edge,Edge.LE)
- edgeList.insert(lbnd,bisector)
+ if (newsite and (priorityQ.isEmpty() or cmp(newsite,minpt) < 0)):
+ # newsite is smallest - this is a site event
+ context.outSite(newsite)
+
+ # get first Halfedge to the LEFT and RIGHT of the new site
+ lbnd = edgeList.leftbnd(newsite)
+ rbnd = lbnd.right
+
+ # if this halfedge has no edge, bot = bottom site (whatever that is)
+ # create a new edge that bisects
+ bot = lbnd.rightreg(bottomsite)
+ edge = Edge.bisect(bot,newsite)
+ context.outBisector(edge)
+
+ # create a new Halfedge, setting its pm field to 0 and insert
+ # this new bisector edge between the left and right vectors in
+ # a linked list
+ bisector = Halfedge(edge,Edge.LE)
+ edgeList.insert(lbnd,bisector)
- # if the new bisector intersects with the left edge, remove
- # the left edge's vertex, and put in the new one
- p = lbnd.intersect(bisector)
- if p is not None:
- priorityQ.delete(lbnd)
- priorityQ.insert(lbnd,p,newsite.distance(p))
+ # if the new bisector intersects with the left edge, remove
+ # the left edge's vertex, and put in the new one
+ p = lbnd.intersect(bisector)
+ if p is not None:
+ priorityQ.delete(lbnd)
+ priorityQ.insert(lbnd,p,newsite.distance(p))
- # create a new Halfedge, setting its pm field to 1
- # insert the new Halfedge to the right of the original bisector
- lbnd = bisector
- bisector = Halfedge(edge,Edge.RE)
- edgeList.insert(lbnd,bisector)
+ # create a new Halfedge, setting its pm field to 1
+ # insert the new Halfedge to the right of the original bisector
+ lbnd = bisector
+ bisector = Halfedge(edge,Edge.RE)
+ edgeList.insert(lbnd,bisector)
- # if this new bisector intersects with the right Halfedge
- p = bisector.intersect(rbnd)
- if p is not None:
- # push the Halfedge into the ordered linked list of vertices
- priorityQ.insert(bisector,p,newsite.distance(p))
-
- newsite = siteIter.next()
+ # if this new bisector intersects with the right Halfedge
+ p = bisector.intersect(rbnd)
+ if p is not None:
+ # push the Halfedge into the ordered linked list of vertices
+ priorityQ.insert(bisector,p,newsite.distance(p))
+
+ newsite = siteIter.next()
- elif not priorityQ.isEmpty():
- # intersection is smallest - this is a vector (circle) event
+ elif not priorityQ.isEmpty():
+ # intersection is smallest - this is a vector (circle) event
- # pop the Halfedge with the lowest vector off the ordered list of
- # vectors. Get the Halfedge to the left and right of the above HE
- # and also the Halfedge to the right of the right HE
- lbnd = priorityQ.popMinHalfedge()
- llbnd = lbnd.left
- rbnd = lbnd.right
- rrbnd = rbnd.right
-
- # get the Site to the left of the left HE and to the right of
- # the right HE which it bisects
- bot = lbnd.leftreg(bottomsite)
- top = rbnd.rightreg(bottomsite)
-
- # output the triple of sites, stating that a circle goes through them
- mid = lbnd.rightreg(bottomsite)
- context.outTriple(bot,top,mid)
+ # pop the Halfedge with the lowest vector off the ordered list of
+ # vectors. Get the Halfedge to the left and right of the above HE
+ # and also the Halfedge to the right of the right HE
+ lbnd = priorityQ.popMinHalfedge()
+ llbnd = lbnd.left
+ rbnd = lbnd.right
+ rrbnd = rbnd.right
+
+ # get the Site to the left of the left HE and to the right of
+ # the right HE which it bisects
+ bot = lbnd.leftreg(bottomsite)
+ top = rbnd.rightreg(bottomsite)
+
+ # output the triple of sites, stating that a circle goes through them
+ mid = lbnd.rightreg(bottomsite)
+ context.outTriple(bot,top,mid)
- # get the vertex that caused this event and set the vertex number
- # couldn't do this earlier since we didn't know when it would be processed
- v = lbnd.vertex
- siteList.setSiteNumber(v)
- context.outVertex(v)
-
- # set the endpoint of the left and right Halfedge to be this vector
- if lbnd.edge.setEndpoint(lbnd.pm,v):
- context.outEdge(lbnd.edge)
-
- if rbnd.edge.setEndpoint(rbnd.pm,v):
- context.outEdge(rbnd.edge)
+ # get the vertex that caused this event and set the vertex number
+ # couldn't do this earlier since we didn't know when it would be processed
+ v = lbnd.vertex
+ siteList.setSiteNumber(v)
+ context.outVertex(v)
+
+ # set the endpoint of the left and right Halfedge to be this vector
+ if lbnd.edge.setEndpoint(lbnd.pm,v):
+ context.outEdge(lbnd.edge)
+
+ if rbnd.edge.setEndpoint(rbnd.pm,v):
+ context.outEdge(rbnd.edge)
-
- # delete the lowest HE, remove all vertex events to do with the
- # right HE and delete the right HE
- edgeList.delete(lbnd)
- priorityQ.delete(rbnd)
- edgeList.delete(rbnd)
-
-
- # if the site to the left of the event is higher than the Site
- # to the right of it, then swap them and set 'pm' to RIGHT
- pm = Edge.LE
- if bot.y > top.y:
- bot,top = top,bot
- pm = Edge.RE
+
+ # delete the lowest HE, remove all vertex events to do with the
+ # right HE and delete the right HE
+ edgeList.delete(lbnd)
+ priorityQ.delete(rbnd)
+ edgeList.delete(rbnd)
+
+
+ # if the site to the left of the event is higher than the Site
+ # to the right of it, then swap them and set 'pm' to RIGHT
+ pm = Edge.LE
+ if bot.y > top.y:
+ bot,top = top,bot
+ pm = Edge.RE
- # Create an Edge (or line) that is between the two Sites. This
- # creates the formula of the line, and assigns a line number to it
- edge = Edge.bisect(bot, top)
- context.outBisector(edge)
+ # Create an Edge (or line) that is between the two Sites. This
+ # creates the formula of the line, and assigns a line number to it
+ edge = Edge.bisect(bot, top)
+ context.outBisector(edge)
- # create a HE from the edge
- bisector = Halfedge(edge, pm)
-
- # insert the new bisector to the right of the left HE
- # set one endpoint to the new edge to be the vector point 'v'
- # If the site to the left of this bisector is higher than the right
- # Site, then this endpoint is put in position 0; otherwise in pos 1
- edgeList.insert(llbnd, bisector)
- if edge.setEndpoint(Edge.RE - pm, v):
- context.outEdge(edge)
-
- # if left HE and the new bisector don't intersect, then delete
- # the left HE, and reinsert it
- p = llbnd.intersect(bisector)
- if p is not None:
- priorityQ.delete(llbnd);
- priorityQ.insert(llbnd, p, bot.distance(p))
+ # create a HE from the edge
+ bisector = Halfedge(edge, pm)
+
+ # insert the new bisector to the right of the left HE
+ # set one endpoint to the new edge to be the vector point 'v'
+ # If the site to the left of this bisector is higher than the right
+ # Site, then this endpoint is put in position 0; otherwise in pos 1
+ edgeList.insert(llbnd, bisector)
+ if edge.setEndpoint(Edge.RE - pm, v):
+ context.outEdge(edge)
+
+ # if left HE and the new bisector don't intersect, then delete
+ # the left HE, and reinsert it
+ p = llbnd.intersect(bisector)
+ if p is not None:
+ priorityQ.delete(llbnd);
+ priorityQ.insert(llbnd, p, bot.distance(p))
- # if right HE and the new bisector don't intersect, then reinsert it
- p = bisector.intersect(rrbnd)
- if p is not None:
- priorityQ.insert(bisector, p, bot.distance(p))
- else:
- break
+ # if right HE and the new bisector don't intersect, then reinsert it
+ p = bisector.intersect(rrbnd)
+ if p is not None:
+ priorityQ.insert(bisector, p, bot.distance(p))
+ else:
+ break
- he = edgeList.leftend.right
- while he is not edgeList.rightend:
- context.outEdge(he.edge)
- he = he.right
+ he = edgeList.leftend.right
+ while he is not edgeList.rightend:
+ context.outEdge(he.edge)
+ he = he.right
+ Edge.EDGE_NUM = 0
+ except Exception, err:
+ print "######################################################"
+ print str(err)
#------------------------------------------------------------------
def isEqual(a,b,relativeError=TOLERANCE):
@@ -724,16 +688,16 @@
self.__sites = []
self.__sitenum = 0
- self.__xmin = pointList[0].x()
- self.__ymin = pointList[0].y()
- self.__xmax = pointList[0].x()
- self.__ymax = pointList[0].y()
+ self.__xmin = pointList[0].x
+ self.__ymin = pointList[0].y
+ self.__xmax = pointList[0].x
+ self.__ymax = pointList[0].y
for i,pt in enumerate(pointList):
- self.__sites.append(Site(pt.x(),pt.y(),i))
- if pt.x() < self.__xmin: self.__xmin = pt.x()
- if pt.y() < self.__ymin: self.__ymin = pt.y()
- if pt.x() > self.__xmax: self.__xmax = pt.x()
- if pt.y() > self.__ymax: self.__ymax = pt.y()
+ self.__sites.append(Site(pt.x,pt.y,i))
+ if pt.x < self.__xmin: self.__xmin = pt.x
+ if pt.y < self.__ymin: self.__ymin = pt.y
+ if pt.x > self.__xmax: self.__xmax = pt.x
+ if pt.y > self.__ymax: self.__ymax = pt.y
self.__sites.sort()
def setSiteNumber(self,site):
@@ -769,7 +733,7 @@
#------------------------------------------------------------------
-def computeVoronoiDiagram( points ):
+def computeVoronoiDiagram(points):
""" Takes a list of point objects (which must have x and y fields).
Returns a 3-tuple of:
@@ -782,22 +746,21 @@
the indices of the vetices at the end of the edge. If
v1 or v2 is -1, the line extends to infinity.
"""
- siteList = SiteList( points )
+ siteList = SiteList(points)
context = Context()
- context.set_bounds( siteList )
- voronoi( siteList, context )
- return ( context.vertices, context.lines, context.edges, (context.xmin,context.ymin,context.xmax,context.ymax))
+ voronoi(siteList,context)
+ return (context.vertices,context.lines,context.edges)
#------------------------------------------------------------------
-def computeDelaunayTriangulation( points ):
+def computeDelaunayTriangulation(points):
""" Takes a list of point objects (which must have x and y fields).
Returns a list of 3-tuples: the indices of the points that form a
Delaunay triangle.
"""
- siteList = SiteList( points )
+ siteList = SiteList(points)
context = Context()
- context.triangulate = True
- voronoi( siteList, context )
+ context.triangulate = true
+ voronoi(siteList,context)
return context.triangles
#-----------------------------------------------------------------------------
More information about the QGIS-commit
mailing list