[QGIS Commit] r10265 - in trunk/qgis/python/plugins/fTools: . icons
icons/default icons/gis tools
svn_qgis at osgeo.org
svn_qgis at osgeo.org
Sun Mar 8 20:20:39 EDT 2009
Author: cfarmer
Date: 2009-03-08 20:20:39 -0400 (Sun, 08 Mar 2009)
New Revision: 10265
Added:
trunk/qgis/python/plugins/fTools/icons/default/delaunay.png
trunk/qgis/python/plugins/fTools/icons/default/layer_extent.png
trunk/qgis/python/plugins/fTools/icons/gis/delaunay.png
trunk/qgis/python/plugins/fTools/icons/gis/layer_extent.png
trunk/qgis/python/plugins/fTools/tools/voronoi.py
Modified:
trunk/qgis/python/plugins/fTools/fTools.py
trunk/qgis/python/plugins/fTools/icons/default/CMakeLists.txt
trunk/qgis/python/plugins/fTools/icons/gis-0.1.svg
trunk/qgis/python/plugins/fTools/icons/gis/CMakeLists.txt
trunk/qgis/python/plugins/fTools/resources.py
trunk/qgis/python/plugins/fTools/resources.qrc
trunk/qgis/python/plugins/fTools/tools/CMakeLists.txt
trunk/qgis/python/plugins/fTools/tools/doGeometry.py
trunk/qgis/python/plugins/fTools/tools/doRandPoints.py
trunk/qgis/python/plugins/fTools/tools/ftools_utils.py
Log:
add two new tools - polygon from layer extent - delaunay triangulaltion
changes to menu item - sampling tools -> research tools
Modified: trunk/qgis/python/plugins/fTools/fTools.py
===================================================================
--- trunk/qgis/python/plugins/fTools/fTools.py 2009-03-05 15:06:30 UTC (rev 10264)
+++ trunk/qgis/python/plugins/fTools/fTools.py 2009-03-09 00:20:39 UTC (rev 10265)
@@ -97,8 +97,8 @@
self.analysisMenu.addActions( [ self.distMatrix, self.sumLines, self.pointsPoly,
self.listUnique, self.compStats, self.nearestNeigh, self.meanCoords, self.intLines ] )
- self.samplingMenu = QMenu( QCoreApplication.translate( "fTools", "&Sampling Tools" ) )
- self.samplingMenu.setIcon( QIcon( self.getThemeIcon( "sampling.png" ) ) )
+ self.researchMenu = QMenu( QCoreApplication.translate( "fTools", "&Research Tools" ) )
+ self.researchMenu.setIcon( QIcon( self.getThemeIcon( "sampling.png" ) ) )
self.randSel = QAction( QIcon( self.getThemeIcon( "random_selection.png" ) ),
QCoreApplication.translate( "fTools", "Random selection" ),self.iface.mainWindow() )
self.randSub = QAction( QIcon( self.getThemeIcon( "sub_selection.png" ) ),
@@ -111,8 +111,10 @@
QCoreApplication.translate( "fTools", "Vector grid" ), self.iface.mainWindow() )
self.selectLocation = QAction( QIcon( self.getThemeIcon( "select_location.png" ) ),
QCoreApplication.translate( "fTools", "Select by location" ), self.iface.mainWindow() )
- self.samplingMenu.addActions( [ self.randSel, self.randSub, self.randPoints,
- self.regPoints, self.vectGrid, self.selectLocation ] )
+ self.layerExtent = QAction( QIcon( self.getThemeIcon( "layer_extent.png" ) ),
+ QCoreApplication.translate( "fTools", "Polygon from layer extent" ), self.iface.mainWindow() )
+ self.researchMenu.addActions( [ self.randSel, self.randSub, self.randPoints,
+ self.regPoints, self.vectGrid, self.selectLocation, self.layerExtent ] )
self.geoMenu = QMenu( QCoreApplication.translate( "fTools", "&Geoprocessing Tools" ) )
self.geoMenu.setIcon( QIcon( self.getThemeIcon( "geoprocessing.png" ) ) )
@@ -143,6 +145,8 @@
QCoreApplication.translate( "fTools", "Check geometry validity" ),self.iface.mainWindow() )
self.centroids = QAction( QIcon( self.getThemeIcon( "centroids.png") ),
QCoreApplication.translate( "fTools", "Polygon centroids" ),self.iface.mainWindow() )
+ self.delaunay = QAction( QIcon( self.getThemeIcon( "delaunay.png") ),
+ QCoreApplication.translate( "fTools", "Delaunay triangulation" ),self.iface.mainWindow() )
self.extNodes = QAction( QIcon( self.getThemeIcon( "extract_nodes.png") ),
QCoreApplication.translate( "fTools", "Extract nodes" ),self.iface.mainWindow() )
self.simplify = QAction( QIcon( self.getThemeIcon( "simplify.png") ),
@@ -153,8 +157,8 @@
QCoreApplication.translate( "fTools", "Singleparts to multipart" ),self.iface.mainWindow() )
self.polysToLines = QAction( QIcon( self.getThemeIcon( "to_lines.png") ),
QCoreApplication.translate( "fTools", "Polygons to lines" ),self.iface.mainWindow() )
- self.conversionMenu.addActions( [ self.checkGeom, self.compGeo, self.centroids, self.simplify,
- self.multiToSingle, self.singleToMulti, self.polysToLines, self.extNodes ] )
+ self.conversionMenu.addActions( [ self.checkGeom, self.compGeo, self.centroids, self.delaunay,
+ self.simplify, self.multiToSingle, self.singleToMulti, self.polysToLines, self.extNodes] )
self.dataManageMenu = QMenu( QCoreApplication.translate( "fTools", "&Data Management Tools") )
self.dataManageMenu.setIcon( QIcon( self.getThemeIcon( "management.png") ) )
@@ -170,21 +174,21 @@
QCoreApplication.translate( "fTools", "Split vector layer" ), self.iface.mainWindow() )
self.dataManageMenu.addActions( [ self.project, self.define, self.joinAttr, self.spatJoin, self.splitVect ] )
- self.ftools_about = QAction( QIcon( self.getThemeIcon( "ftools_logo.png" ) ),
- QCoreApplication.translate( "fTools", "About fTools" ), self.iface.mainWindow() )
+ self.ftools_aboot = QAction( QIcon( self.getThemeIcon( "ftools_logo.png" ) ),
+ QCoreApplication.translate( "fTools", "fTools About" ), self.iface.mainWindow() )
self.menu.addMenu( self.analysisMenu )
- self.menu.addMenu( self.samplingMenu )
+ self.menu.addMenu( self.researchMenu )
self.menu.addMenu( self.geoMenu )
self.menu.addMenu( self.conversionMenu )
self.menu.addMenu( self.dataManageMenu )
self.menu.addSeparator()
- self.menu.addAction( self.ftools_about )
+ self.menu.addAction( self.ftools_aboot )
menuBar = self.iface.mainWindow().menuBar()
actions = menuBar.actions()
- helpAction = actions[ len( actions ) - 1 ]
- menuBar.insertMenu( helpAction, self.menu )
+ lastAction = actions[ len( actions ) - 1 ]
+ menuBar.insertMenu( lastAction, self.menu )
QObject.connect( self.distMatrix, SIGNAL("triggered()"), self.dodistMatrix )
QObject.connect( self.sumLines, SIGNAL("triggered()"), self.dosumLines )
@@ -201,6 +205,7 @@
QObject.connect( self.regPoints, SIGNAL("triggered()"), self.doregPoints )
QObject.connect( self.vectGrid, SIGNAL("triggered()"), self.dovectGrid )
QObject.connect( self.selectLocation, SIGNAL("triggered()"), self.doselectLocation )
+ QObject.connect( self.layerExtent, SIGNAL("triggered()"), self.doextent )
QObject.connect( self.minConvex, SIGNAL("triggered()"), self.dominConvex )
QObject.connect( self.intersect, SIGNAL("triggered()"), self.dointersect )
@@ -216,6 +221,7 @@
QObject.connect( self.checkGeom, SIGNAL("triggered()"), self.docheckGeom )
QObject.connect( self.simplify, SIGNAL("triggered()"), self.dosimplify )
QObject.connect( self.centroids, SIGNAL("triggered()"), self.docentroids )
+ QObject.connect( self.delaunay, SIGNAL("triggered()"), self.dodelaunay )
QObject.connect( self.polysToLines, SIGNAL("triggered()"), self.dopolysToLines )
QObject.connect( self.compGeo, SIGNAL("triggered()"), self.docompGeo )
QObject.connect( self.extNodes, SIGNAL("triggered()"), self.doextNodes )
@@ -226,7 +232,7 @@
QObject.connect( self.spatJoin, SIGNAL("triggered()"), self.dospatJoin )
QObject.connect( self.splitVect, SIGNAL("triggered()"), self.dosplitVect )
- QObject.connect( self.ftools_about, SIGNAL("triggered()"), self.doabout )
+ QObject.connect( self.ftools_aboot, SIGNAL("triggered()"), self.doaboot )
def unload( self ):
pass
@@ -302,6 +308,14 @@
def docentroids( self ):
d = doGeometry.GeometryDialog( self.iface, 7 )
d.exec_()
+
+ def dodelaunay( self ):
+ d = doGeometry.GeometryDialog( self.iface, 8 )
+ d.exec_()
+
+ def doextent( self ):
+ d = doGeometry.GeometryDialog( self.iface, 9 )
+ d.exec_()
def dosumLines(self):
d = doSumLines.Dialog(self.iface)
@@ -371,6 +385,6 @@
d = doSpatialJoin.Dialog( self.iface )
d.exec_()
- def doabout( self ):
+ def doaboot( self ):
d = doAbout.Dialog( self.iface )
d.exec_()
Modified: trunk/qgis/python/plugins/fTools/icons/default/CMakeLists.txt
===================================================================
--- trunk/qgis/python/plugins/fTools/icons/default/CMakeLists.txt 2009-03-05 15:06:30 UTC (rev 10264)
+++ trunk/qgis/python/plugins/fTools/icons/default/CMakeLists.txt 2009-03-09 00:20:39 UTC (rev 10265)
@@ -43,5 +43,7 @@
random_selection.png
ftools_logo.png
regular_points.png
+delaunay.png
+layer_extent.png
)
INSTALL(FILES ${ICON_FILES} DESTINATION ${QGIS_DATA_DIR}/python/plugins/fTools/icons/default)
Added: trunk/qgis/python/plugins/fTools/icons/default/delaunay.png
===================================================================
(Binary files differ)
Property changes on: trunk/qgis/python/plugins/fTools/icons/default/delaunay.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/qgis/python/plugins/fTools/icons/default/layer_extent.png
===================================================================
(Binary files differ)
Property changes on: trunk/qgis/python/plugins/fTools/icons/default/layer_extent.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Modified: trunk/qgis/python/plugins/fTools/icons/gis/CMakeLists.txt
===================================================================
--- trunk/qgis/python/plugins/fTools/icons/gis/CMakeLists.txt 2009-03-05 15:06:30 UTC (rev 10264)
+++ trunk/qgis/python/plugins/fTools/icons/gis/CMakeLists.txt 2009-03-09 00:20:39 UTC (rev 10265)
@@ -43,5 +43,7 @@
random_selection.png
ftools_logo.png
regular_points.png
+delaunay.png
+layer_extent.png
)
INSTALL(FILES ${ICON_FILES} DESTINATION ${QGIS_DATA_DIR}/python/plugins/fTools/icons/gis)
Added: trunk/qgis/python/plugins/fTools/icons/gis/delaunay.png
===================================================================
(Binary files differ)
Property changes on: trunk/qgis/python/plugins/fTools/icons/gis/delaunay.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/qgis/python/plugins/fTools/icons/gis/layer_extent.png
===================================================================
(Binary files differ)
Property changes on: trunk/qgis/python/plugins/fTools/icons/gis/layer_extent.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Modified: trunk/qgis/python/plugins/fTools/icons/gis-0.1.svg
===================================================================
--- trunk/qgis/python/plugins/fTools/icons/gis-0.1.svg 2009-03-05 15:06:30 UTC (rev 10264)
+++ trunk/qgis/python/plugins/fTools/icons/gis-0.1.svg 2009-03-09 00:20:39 UTC (rev 10265)
@@ -17,7 +17,7 @@
version="1.0"
sodipodi:docname="gis-0.1.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
- inkscape:export-filename="/home/cfarmer/.qgis/python/plugins/fTools/gis_icons/matrix.png"
+ inkscape:export-filename="/home/cfarmer/dev/cpp/qgis/python/plugins/fTools/icons/gis/layer_extent.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
style="display:inline;enable-background:new">
@@ -32,15 +32,15 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="16"
- inkscape:cx="9.0201001"
- inkscape:cy="8.9456096"
+ inkscape:cx="9.2319959"
+ inkscape:cy="11.794933"
inkscape:document-units="px"
- inkscape:current-layer="layer129"
+ inkscape:current-layer="layer155"
showgrid="true"
inkscape:window-width="1159"
inkscape:window-height="700"
inkscape:window-x="86"
- inkscape:window-y="72"
+ inkscape:window-y="48"
inkscape:snap-global="false"
showguides="true"
inkscape:guide-bbox="true"
@@ -2370,6 +2370,28 @@
style="fill:#82a0b4;fill-opacity:1;fill-rule:nonzero;stroke:#3c5a6e;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:2.4;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer134"
+ inkscape:label="delaunay"
+ style="display:none"
+ sodipodi:insensitive="true">
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#3c5a6e;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 5.5,15.9375 L 12.5,9.5625 L 0.625,6.125"
+ id="path3778"
+ sodipodi:nodetypes="ccc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#3c5a6e;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 8.0625,0.9375 L 12.4375,9.5 L 17.3125,0.8125"
+ id="path3780"
+ sodipodi:nodetypes="ccc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#3c5a6e;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 23.25,14 L 12.5625,9.4375 L 16.9375,23.0625"
+ id="path3782"
+ sodipodi:nodetypes="ccc" />
+ </g>
</g>
<g
style="display:none"
@@ -6099,26 +6121,35 @@
inkscape:groupmode="layer"
id="layer129"
inkscape:label="matrix"
- style="display:inline">
- <g
- id="g4571">
- <path
- id="path3756"
- d="M 5.499146,0.50519709 L 5.499146,23.480886"
- style="fill:none;fill-rule:evenodd;stroke:#969696;stroke-width:0.97941929px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
- <path
- id="path3761"
- d="M 0.51176719,5.499146 L 23.528038,5.499146"
- style="fill:none;fill-rule:evenodd;stroke:#969696;stroke-width:0.98028392px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
- <rect
- y="6.4966083"
- x="6.4907565"
- height="17.023754"
- width="17.035219"
- id="rect4569"
- style="opacity:1;fill:#82a0b4;fill-opacity:0.99215686;fill-rule:nonzero;stroke:#3c5a6e;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.9;stroke-opacity:0.99215686;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
- </g>
+ style="display:none">
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#969696;stroke-width:0.97941929px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ d="M 5.499146,0.50519709 L 5.499146,23.480886"
+ id="path3756" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#969696;stroke-width:0.98028392px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ d="M 0.51176719,5.499146 L 23.528038,5.499146"
+ id="path3761" />
+ <rect
+ style="fill:#82a0b4;fill-opacity:0.99215686;fill-rule:nonzero;stroke:#3c5a6e;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.9;stroke-opacity:0.99215686;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect4569"
+ width="17.035219"
+ height="17.023754"
+ x="6.4907565"
+ y="6.4966083" />
</g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer155"
+ inkscape:label="extent">
+ <rect
+ style="fill:#82a0b4;fill-opacity:0.99215686;fill-rule:nonzero;stroke:#3c5a6e;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.9;stroke-opacity:0.99215686;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect3768"
+ width="17.035219"
+ height="17.023754"
+ x="0.5448904"
+ y="0.55062294" />
+ </g>
</g>
<g
style="display:none"
@@ -7097,7 +7128,7 @@
id="layer39"
inkscape:groupmode="layer">
<g
- style="display:inline"
+ style="display:none"
inkscape:label="print composer"
id="layer43"
inkscape:groupmode="layer">
@@ -8017,7 +8048,7 @@
</g>
</g>
<g
- style="display:none"
+ style="display:inline"
inkscape:label="actions"
id="layer11"
inkscape:groupmode="layer">
@@ -8267,7 +8298,7 @@
</g>
<g
sodipodi:insensitive="true"
- style="display:none"
+ style="display:inline"
inkscape:label="create [yellow]"
id="layer15"
inkscape:groupmode="layer">
Modified: trunk/qgis/python/plugins/fTools/resources.py
===================================================================
--- trunk/qgis/python/plugins/fTools/resources.py 2009-03-05 15:06:30 UTC (rev 10264)
+++ trunk/qgis/python/plugins/fTools/resources.py 2009-03-09 00:20:39 UTC (rev 10265)
@@ -2,8 +2,8 @@
# Resource object code
#
-# Created: Sun Jan 18 22:55:27 2009
-# by: The Resource Compiler for PyQt (Qt v4.3.4)
+# Created: Mon Mar 9 00:09:47 2009
+# by: The Resource Compiler for PyQt (Qt v4.4.3)
#
# WARNING! All changes made in this file will be lost!
@@ -1469,6 +1469,97 @@
\xf4\x92\xbd\x8d\xb3\x84\x5e\xae\x7c\x20\xbb\x4d\x80\xff\x07\x5c\
\x69\x9f\x38\xa5\x9a\x04\xb9\x00\x00\x00\x00\x49\x45\x4e\x44\xae\
\x42\x60\x82\
+\x00\x00\x05\x8c\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
+\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x05\x09\x49\x44\
+\x41\x54\x48\x89\xad\x55\x4b\x4f\x1b\x57\x18\x3d\x33\xe3\x07\xb6\
+\xc7\x24\x36\x7e\x04\x1b\x9c\x50\x0c\x34\x82\x38\x01\x43\x9b\x40\
+\x0a\xca\x2a\x55\x37\x55\xa5\x2a\x51\x21\x54\x5d\x74\x91\x2a\x95\
+\x92\xd0\x4a\x5d\x35\x52\x1b\x55\x95\x5a\x35\x44\x55\x53\x29\x9b\
+\xaa\xa4\x69\x16\x74\x93\xf6\x07\x54\x49\x10\xce\xab\x01\x8c\x91\
+\x2a\xdb\xe0\x07\x60\xde\xb6\xf1\x0b\x7b\xec\x99\x7b\xbb\x48\x4c\
+\x6d\xec\x4a\x59\xf4\xac\x46\xf7\x9e\xef\x9c\xf9\xbe\xfb\xdd\xef\
+\x32\x94\x52\x54\xc3\xc9\xc1\x91\x76\xb9\x8c\xb9\x2b\x11\xe9\x50\
+\x55\x42\x09\x18\x86\x49\x10\x22\xb4\x4c\xfc\xfa\x63\x7c\xef\x9e\
+\xac\x5a\x40\xff\xf0\xe5\x0f\x65\x32\x7c\x6f\x3b\x78\x48\xb9\xb2\
+\x1a\x61\x7b\x7b\x7b\xc1\x32\x4c\x55\x71\xcf\xdc\x1c\x52\xa9\x74\
+\x6d\x36\x87\xf3\x00\xbe\xae\x30\x2f\xcd\xe0\xd4\x99\x0b\x3c\xad\
+\x51\x8e\x29\xe4\xf2\x37\x3b\x3b\xbb\xd4\xbc\x96\xc7\xf4\xcc\x0c\
+\x0c\x06\x03\x1a\x1b\x1a\x2a\xc4\xb7\x13\xdb\x70\xcf\xb8\xe1\x38\
+\xe2\xc0\xd4\xf4\x54\x2c\xad\xca\x1c\xf8\xeb\xe6\xcd\x42\x29\x87\
+\x2d\x7e\xf4\x0d\x5d\x3e\x46\x6b\x94\x7f\x1b\x8d\xc6\xb7\x7a\xfb\
+\xfa\xd4\xbc\x96\x07\x00\xb4\x34\xdb\x11\x0c\x2c\x80\x10\x52\x61\
+\xe0\xf3\x7a\xd1\x62\x6f\x81\x4e\xaf\x83\x4a\xa3\x51\xa8\x53\xaa\
+\xb3\x7b\x39\x2c\x00\x0c\x9c\xbb\x78\x41\xce\x31\xae\x57\x0f\x1f\
+\xb6\x3a\x1c\x8e\x1a\x8e\xe3\x76\x09\xbc\x96\xc7\xbe\x7d\xfb\xb1\
+\xbc\xbc\x5c\x16\xb8\xb6\xb6\x0e\x49\x22\xa8\xb7\x5a\x00\x00\xf6\
+\xa6\x26\x9e\x95\xb3\x57\x2a\x0c\x06\xde\x1f\xb9\xab\x54\xd6\x7c\
+\x7b\xfc\xf8\xeb\x2a\xab\xc5\x52\xb5\xd0\x76\x7b\x33\x42\xc1\x20\
+\x24\xe9\x79\x16\x84\x48\xf0\xfb\x7c\x68\x6b\x6b\x45\x31\xc0\x68\
+\x36\x43\xc6\xc9\x2c\x27\x87\x2e\x9e\x2a\x33\x60\x80\x4e\x51\x92\
+\x54\x33\x6e\x37\x7c\x5e\x2f\x62\xb1\x28\x08\x2d\x2f\x87\x46\xc3\
+\x43\xa7\xd3\x63\x79\x69\x09\x00\x10\x0e\x2f\x82\xd7\xf2\xd0\xeb\
+\xeb\x76\x39\x0c\x80\x43\x4d\x4d\x6a\x99\x8c\xfb\xbc\xcc\x40\x22\
+\xd2\x25\x85\x5c\x9e\x39\xea\x38\x0a\x85\x52\x89\x85\x40\x00\xf7\
+\xef\xdd\x87\x7b\xc6\x8d\x48\x24\x02\x41\x10\x00\x00\xcd\xf6\x66\
+\x84\xc2\x21\x64\xb3\x59\x84\x42\x21\xb4\xb6\xb5\x56\x64\xda\x60\
+\xb1\x32\xa0\xe8\x3d\x39\x7c\xa9\xad\xb8\xc6\x2d\x7a\x1e\x7b\x1b\
+\x3b\x7a\x3e\xaa\x33\x18\x6b\x2d\x16\x0b\xac\x16\x2b\x1a\x1b\x1b\
+\xc0\x71\x1c\xa2\xd1\x18\x7c\x7e\x1f\x56\x57\x57\x21\x11\x09\x84\
+\x52\x04\x03\x01\xd4\xd7\x1f\x80\xa5\xde\x52\x61\xc0\xb2\x2c\xc4\
+\x82\x48\x33\xa9\xf4\xbe\x0f\xde\x39\xfd\x3b\x00\xb0\x94\x52\x4a\
+\x89\x74\x65\x61\x61\x3e\x53\x24\xca\x64\x72\x98\xcd\x66\x74\x74\
+\xb4\x63\xa0\xbf\x1f\xed\xed\xed\x60\x59\x0e\x99\x74\x0a\xf9\x7c\
+\x1e\xc9\x44\x1a\x5b\x5b\x5b\xa8\x76\x49\x0f\x1e\xb4\xc9\x09\x25\
+\xef\x9d\x1a\xfc\xd4\x00\xbc\xe8\xa2\xb4\x3a\x77\x3b\x9d\x4a\xe5\
+\x12\xdb\x89\x8a\x00\x86\x61\x50\x5b\x5b\x0b\x2d\xcf\x83\x50\x0a\
+\x9d\x4e\x07\x96\x63\xe1\xf3\xf9\x31\xe9\x72\x61\x71\x31\x0c\x51\
+\x14\x77\xf9\xca\x9a\x1a\x98\x8c\x26\x42\x38\xe9\x63\xa0\xe4\xa2\
+\xbd\x31\x7c\x71\xa4\xae\xce\xf0\xa5\xb3\xb3\x4b\xb3\xd7\x24\x12\
+\x89\x60\xde\xef\xc7\xb1\xae\x2e\x14\x04\x01\xa1\x70\x18\xdd\xdd\
+\xdd\x88\xc7\xe2\x08\x2f\x2d\x22\x1e\x8b\xa1\xbe\xfe\x00\x1a\x6d\
+\x36\x68\xd4\x1a\xa4\x52\x29\x3c\x79\xfc\x24\xa1\x8e\x8a\xe6\xdd\
+\x51\xa1\x66\x14\x37\xe3\xd1\xd8\x17\x99\x74\x06\x1a\xfe\x5f\x8f\
+\x40\x60\x01\x91\xe5\x15\x74\xbf\xd6\x03\x8d\x5a\x03\x42\x08\x3c\
+\x73\x73\x28\xe4\x0b\xd0\xe9\x75\xd0\xe9\x75\xc8\xe5\x04\x2c\x2f\
+\x2d\xe2\xe9\x93\xa7\xa8\xd5\x6a\x61\xb3\xd9\xa0\xd6\xf2\x5c\x4a\
+\x4a\x0c\x96\x8d\x8a\x81\x73\x23\x57\xcd\x66\xe3\x27\x47\x1c\x0e\
+\x15\xa5\x14\x5e\xaf\x17\xb1\x58\x0c\x4e\xa7\x13\x4a\xa5\x72\x97\
+\x37\xeb\xf1\xa0\x4e\xaf\x87\xd5\x6a\x2d\xcb\x94\x10\x09\x6b\x6b\
+\x6b\x08\x87\x17\x91\xcf\xe7\x21\x4a\x85\x08\x5b\x4a\x28\xe4\x31\
+\xba\xbe\xb1\x89\x6c\x36\x0b\x8f\xc7\x83\x64\x32\x85\x9e\x9e\x9e\
+\x32\x71\x00\x30\x9b\x4c\xd8\x58\x5f\xaf\x38\x2f\x96\xe5\x60\xb1\
+\x58\x71\xe2\xc4\x09\x98\x4d\x26\x02\x70\x5b\x65\x06\xae\xf1\x6b\
+\x31\x30\xf4\xa7\xa9\xa9\x67\x92\x24\x49\x70\x3a\x9d\x90\xcb\xe5\
+\x15\x42\x06\x83\x01\xf1\xed\x44\xd9\xe1\x96\x22\x93\x49\x23\xb2\
+\xb2\x92\xcb\x13\xf2\x2e\xbb\x77\x53\x10\xe9\xd5\x5c\x36\x17\x10\
+\xf3\x85\xb4\x20\xe4\xaa\x0a\x70\x1c\x07\x9d\x5e\x87\xad\xcd\xcd\
+\x8a\x3d\x4a\x28\xdc\xee\xd9\x1d\x42\xf1\x99\xeb\x97\x6b\xf3\x15\
+\x06\x8f\xee\x5c\x5f\x37\x09\x91\xc3\xdb\xe9\xe4\xd5\x87\x0f\x5d\
+\x3b\xc1\x60\x40\xa4\xa4\xb2\xdf\xcd\x26\x33\xd6\x37\x36\x2a\xd6\
+\x03\xc1\x80\x98\xcb\x09\xee\x89\xdb\xa3\x37\x80\x92\x71\x5d\x8a\
+\xf1\xf1\x71\xe9\xc1\xad\xd1\x6f\x28\xc4\x8e\x60\x20\xf4\x68\xd2\
+\xe5\x4a\x27\x92\xc9\x32\x8e\xd1\x68\x40\x34\x1a\xdd\x1d\x80\x00\
+\x90\x4a\x26\x11\x0a\x85\x04\x92\xa7\x67\xe9\x8b\xee\x61\xfe\xeb\
+\xc9\x2c\x45\xff\xe0\xa5\x73\x8c\x8c\xb9\xd1\x60\xb5\x2a\x5a\xec\
+\xad\x35\x9c\xec\xf9\x38\x9f\x7a\xf6\x0c\x0d\x8d\x36\x98\x4c\x46\
+\x10\x42\xe1\x9a\x9c\xdc\xc9\xe6\x73\x17\x1e\xdc\x1a\xfd\xb9\x18\
+\x5b\x35\x83\xbd\x78\x70\xe7\xfa\xed\x1c\x53\x78\x25\xb2\xb2\xf2\
+\xc7\xc4\xc4\xc4\x4e\xb1\xf6\x46\x93\x09\xeb\x2f\xba\x69\xde\xef\
+\x2f\x14\x0a\xf9\x87\xa5\xe2\x2f\x6d\x00\x00\x8f\xc7\x7e\x88\xde\
+\x1b\xbb\x76\x36\x5f\x10\xde\x76\xcf\xce\xae\x4d\xcf\x4c\xef\xe8\
+\xf6\xef\x47\x74\x6b\x13\xf1\xf8\x36\x96\x96\x97\x72\x3b\x05\x69\
+\x68\x6f\xdc\x4b\x95\x68\x2f\x7a\xcf\x8c\xa8\x14\x4a\x7c\x05\x06\
+\xe7\x95\x4a\x85\x4a\x14\x45\x41\x92\xc4\xe1\x7b\xb7\xae\xff\xf6\
+\xbf\x18\x14\xd1\x37\x74\xf9\x98\x5c\xc6\xdc\x61\xc1\x4c\xff\x39\
+\xf6\x5d\xc5\xdf\x03\xc0\x3f\x4b\x1d\x48\xe6\x9d\x4c\x36\xb6\x00\
+\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\x00\x00\x05\x60\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -2699,6 +2790,46 @@
\x06\x2d\xed\xb3\x39\x87\x2c\xa3\x4e\xe4\x50\x7f\xb2\x8d\xee\x40\
\x10\xc9\x27\x6e\xa3\x3b\x7e\x03\x1c\x81\xee\xcf\x3e\x80\x40\xd3\
\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x02\x5f\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
+\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\xdc\x49\x44\
+\x41\x54\x48\x89\xed\x95\x31\x6b\x53\x51\x18\x86\x9f\x73\xce\x35\
+\xbd\x1a\x92\x96\xa6\x12\x6f\x17\x35\x01\x69\x4a\x47\x29\x45\x24\
+\x6a\x8a\xfe\x03\x25\x38\xe8\x22\x4e\x52\x08\x41\xff\x84\x38\x89\
+\x4e\x59\xe2\x20\x82\xb3\x60\x14\xdb\x24\x5a\x91\x8c\x0e\x56\x1a\
+\xa8\x0e\xc6\x24\xd0\xd0\xe6\x56\xed\x25\xf4\x9e\xe3\x20\xc6\x62\
+\x09\xd8\x5b\x70\xca\x37\x9e\xef\x3d\xdf\xf3\xbe\xc3\x77\x8e\x38\
+\x7f\x2d\xf7\x5e\x1b\x93\xe4\x00\x25\x10\x9d\x11\x54\xaa\xf4\xe8\
+\xee\xf7\xbf\x7b\x96\xaf\xf5\x54\x3a\x7d\x4e\x29\x29\x03\x03\x5e\
+\x57\xab\xb1\xde\xf6\x8f\x08\xb0\x17\x00\x70\xc8\x52\x48\xa9\x02\
+\x03\x04\x06\x33\xa0\x17\xdc\xf6\x3f\xd6\x10\x30\x04\x0c\x01\xff\
+\x01\x60\x05\xb9\x34\x26\x3e\x90\x34\x05\x39\xca\x2a\x00\x99\x0c\
+\x47\x80\xe6\x72\xf1\x61\x5f\x63\xa0\x26\x24\xf9\x40\x80\xa4\x29\
+\xc8\xf1\x50\x1b\x3b\x72\x1a\xa9\x6c\xa4\xb2\x49\x5d\x7a\xc9\xca\
+\x8b\x8b\x68\xdf\x43\xfb\x1e\xde\x56\x7d\x76\xa7\xd7\xbd\x17\x08\
+\x30\xca\x2a\xe1\x58\x06\x65\x85\xc1\xfc\x79\xe6\x7e\xc3\x10\x02\
+\x15\x8a\xe2\x36\x17\x67\x03\x01\x00\x26\x8e\x5f\x21\x3a\x39\x4f\
+\xe7\xd3\x13\xa2\xf1\x34\x00\x93\x33\xb7\x71\xdb\x55\x62\x27\xb3\
+\xb8\x5f\x5f\xe1\x36\x17\xb1\xa4\x10\xeb\x4b\x4b\xe5\xf1\xfd\x0c\
+\xcf\xcc\x33\xb2\xf1\xe5\x19\xfe\xce\x37\x9c\x99\x3b\x18\xdd\x63\
+\xb3\xf1\x9c\x48\xfc\x2c\xe1\xa3\x73\xb4\x57\xee\xe3\xb6\x2a\x00\
+\x58\x42\x6d\x26\x60\xcc\xde\x67\x80\x0d\x30\xb8\xad\x32\xf1\xa9\
+\x5b\x6c\xb5\xdf\xd0\xfe\xf8\x00\x80\xe8\xb1\x0b\xb8\xad\x72\x5f\
+\x28\x8c\x19\xf4\x55\x0c\xae\xe5\xa2\x30\x20\x98\x48\x5c\x25\x71\
+\xa6\x80\xf6\xb7\xe9\x7c\x7e\x4a\xec\xc4\x65\xa4\x3a\xcc\xda\xdb\
+\x1b\xac\xaf\x3d\x06\x4c\xf0\x3d\x70\x52\x0b\x38\xd3\x39\xea\x95\
+\x2c\xdd\x46\x89\xf8\xa9\x9b\x74\x1b\x25\xea\x95\x2c\xce\x74\x0e\
+\x27\xb5\x70\xb0\x04\x52\xd9\x68\xdd\x03\xa3\x01\x98\xbb\x6e\x78\
+\x57\x14\xbf\x04\x42\x22\x65\x08\xed\x7b\xc1\x12\x18\xa8\x69\xdf\
+\xeb\x0f\xdf\x2b\xd0\x68\xdf\xc3\x40\x2d\x10\x40\x48\xf2\x06\x6a\
+\xbb\xcf\xfa\xee\x77\x99\x10\x92\xfc\x4f\xfd\xc2\xaa\x73\x0e\x12\
+\x9d\x40\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\x00\x00\x04\x68\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -5381,6 +5512,97 @@
\xf4\x92\xbd\x8d\xb3\x84\x5e\xae\x7c\x20\xbb\x4d\x80\xff\x07\x5c\
\x69\x9f\x38\xa5\x9a\x04\xb9\x00\x00\x00\x00\x49\x45\x4e\x44\xae\
\x42\x60\x82\
+\x00\x00\x05\x8c\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
+\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x05\x09\x49\x44\
+\x41\x54\x48\x89\xad\x55\x4b\x4f\x1b\x57\x18\x3d\x33\xe3\x07\xb6\
+\xc7\x24\x36\x7e\x04\x1b\x9c\x50\x0c\x34\x82\x38\x01\x43\x9b\x40\
+\x0a\xca\x2a\x55\x37\x55\xa5\x2a\x51\x21\x54\x5d\x74\x91\x2a\x95\
+\x92\xd0\x4a\x5d\x35\x52\x1b\x55\x95\x5a\x35\x44\x55\x53\x29\x9b\
+\xaa\xa4\x69\x16\x74\x93\xf6\x07\x54\x49\x10\xce\xab\x01\x8c\x91\
+\x2a\xdb\xe0\x07\x60\xde\xb6\xf1\x0b\x7b\xec\x99\x7b\xbb\x48\x4c\
+\x6d\xec\x4a\x59\xf4\xac\x46\xf7\x9e\xef\x9c\xf9\xbe\xfb\xdd\xef\
+\x32\x94\x52\x54\xc3\xc9\xc1\x91\x76\xb9\x8c\xb9\x2b\x11\xe9\x50\
+\x55\x42\x09\x18\x86\x49\x10\x22\xb4\x4c\xfc\xfa\x63\x7c\xef\x9e\
+\xac\x5a\x40\xff\xf0\xe5\x0f\x65\x32\x7c\x6f\x3b\x78\x48\xb9\xb2\
+\x1a\x61\x7b\x7b\x7b\xc1\x32\x4c\x55\x71\xcf\xdc\x1c\x52\xa9\x74\
+\x6d\x36\x87\xf3\x00\xbe\xae\x30\x2f\xcd\xe0\xd4\x99\x0b\x3c\xad\
+\x51\x8e\x29\xe4\xf2\x37\x3b\x3b\xbb\xd4\xbc\x96\xc7\xf4\xcc\x0c\
+\x0c\x06\x03\x1a\x1b\x1a\x2a\xc4\xb7\x13\xdb\x70\xcf\xb8\xe1\x38\
+\xe2\xc0\xd4\xf4\x54\x2c\xad\xca\x1c\xf8\xeb\xe6\xcd\x42\x29\x87\
+\x2d\x7e\xf4\x0d\x5d\x3e\x46\x6b\x94\x7f\x1b\x8d\xc6\xb7\x7a\xfb\
+\xfa\xd4\xbc\x96\x07\x00\xb4\x34\xdb\x11\x0c\x2c\x80\x10\x52\x61\
+\xe0\xf3\x7a\xd1\x62\x6f\x81\x4e\xaf\x83\x4a\xa3\x51\xa8\x53\xaa\
+\xb3\x7b\x39\x2c\x00\x0c\x9c\xbb\x78\x41\xce\x31\xae\x57\x0f\x1f\
+\xb6\x3a\x1c\x8e\x1a\x8e\xe3\x76\x09\xbc\x96\xc7\xbe\x7d\xfb\xb1\
+\xbc\xbc\x5c\x16\xb8\xb6\xb6\x0e\x49\x22\xa8\xb7\x5a\x00\x00\xf6\
+\xa6\x26\x9e\x95\xb3\x57\x2a\x0c\x06\xde\x1f\xb9\xab\x54\xd6\x7c\
+\x7b\xfc\xf8\xeb\x2a\xab\xc5\x52\xb5\xd0\x76\x7b\x33\x42\xc1\x20\
+\x24\xe9\x79\x16\x84\x48\xf0\xfb\x7c\x68\x6b\x6b\x45\x31\xc0\x68\
+\x36\x43\xc6\xc9\x2c\x27\x87\x2e\x9e\x2a\x33\x60\x80\x4e\x51\x92\
+\x54\x33\x6e\x37\x7c\x5e\x2f\x62\xb1\x28\x08\x2d\x2f\x87\x46\xc3\
+\x43\xa7\xd3\x63\x79\x69\x09\x00\x10\x0e\x2f\x82\xd7\xf2\xd0\xeb\
+\xeb\x76\x39\x0c\x80\x43\x4d\x4d\x6a\x99\x8c\xfb\xbc\xcc\x40\x22\
+\xd2\x25\x85\x5c\x9e\x39\xea\x38\x0a\x85\x52\x89\x85\x40\x00\xf7\
+\xef\xdd\x87\x7b\xc6\x8d\x48\x24\x02\x41\x10\x00\x00\xcd\xf6\x66\
+\x84\xc2\x21\x64\xb3\x59\x84\x42\x21\xb4\xb6\xb5\x56\x64\xda\x60\
+\xb1\x32\xa0\xe8\x3d\x39\x7c\xa9\xad\xb8\xc6\x2d\x7a\x1e\x7b\x1b\
+\x3b\x7a\x3e\xaa\x33\x18\x6b\x2d\x16\x0b\xac\x16\x2b\x1a\x1b\x1b\
+\xc0\x71\x1c\xa2\xd1\x18\x7c\x7e\x1f\x56\x57\x57\x21\x11\x09\x84\
+\x52\x04\x03\x01\xd4\xd7\x1f\x80\xa5\xde\x52\x61\xc0\xb2\x2c\xc4\
+\x82\x48\x33\xa9\xf4\xbe\x0f\xde\x39\xfd\x3b\x00\xb0\x94\x52\x4a\
+\x89\x74\x65\x61\x61\x3e\x53\x24\xca\x64\x72\x98\xcd\x66\x74\x74\
+\xb4\x63\xa0\xbf\x1f\xed\xed\xed\x60\x59\x0e\x99\x74\x0a\xf9\x7c\
+\x1e\xc9\x44\x1a\x5b\x5b\x5b\xa8\x76\x49\x0f\x1e\xb4\xc9\x09\x25\
+\xef\x9d\x1a\xfc\xd4\x00\xbc\xe8\xa2\xb4\x3a\x77\x3b\x9d\x4a\xe5\
+\x12\xdb\x89\x8a\x00\x86\x61\x50\x5b\x5b\x0b\x2d\xcf\x83\x50\x0a\
+\x9d\x4e\x07\x96\x63\xe1\xf3\xf9\x31\xe9\x72\x61\x71\x31\x0c\x51\
+\x14\x77\xf9\xca\x9a\x1a\x98\x8c\x26\x42\x38\xe9\x63\xa0\xe4\xa2\
+\xbd\x31\x7c\x71\xa4\xae\xce\xf0\xa5\xb3\xb3\x4b\xb3\xd7\x24\x12\
+\x89\x60\xde\xef\xc7\xb1\xae\x2e\x14\x04\x01\xa1\x70\x18\xdd\xdd\
+\xdd\x88\xc7\xe2\x08\x2f\x2d\x22\x1e\x8b\xa1\xbe\xfe\x00\x1a\x6d\
+\x36\x68\xd4\x1a\xa4\x52\x29\x3c\x79\xfc\x24\xa1\x8e\x8a\xe6\xdd\
+\x51\xa1\x66\x14\x37\xe3\xd1\xd8\x17\x99\x74\x06\x1a\xfe\x5f\x8f\
+\x40\x60\x01\x91\xe5\x15\x74\xbf\xd6\x03\x8d\x5a\x03\x42\x08\x3c\
+\x73\x73\x28\xe4\x0b\xd0\xe9\x75\xd0\xe9\x75\xc8\xe5\x04\x2c\x2f\
+\x2d\xe2\xe9\x93\xa7\xa8\xd5\x6a\x61\xb3\xd9\xa0\xd6\xf2\x5c\x4a\
+\x4a\x0c\x96\x8d\x8a\x81\x73\x23\x57\xcd\x66\xe3\x27\x47\x1c\x0e\
+\x15\xa5\x14\x5e\xaf\x17\xb1\x58\x0c\x4e\xa7\x13\x4a\xa5\x72\x97\
+\x37\xeb\xf1\xa0\x4e\xaf\x87\xd5\x6a\x2d\xcb\x94\x10\x09\x6b\x6b\
+\x6b\x08\x87\x17\x91\xcf\xe7\x21\x4a\x85\x08\x5b\x4a\x28\xe4\x31\
+\xba\xbe\xb1\x89\x6c\x36\x0b\x8f\xc7\x83\x64\x32\x85\x9e\x9e\x9e\
+\x32\x71\x00\x30\x9b\x4c\xd8\x58\x5f\xaf\x38\x2f\x96\xe5\x60\xb1\
+\x58\x71\xe2\xc4\x09\x98\x4d\x26\x02\x70\x5b\x65\x06\xae\xf1\x6b\
+\x31\x30\xf4\xa7\xa9\xa9\x67\x92\x24\x49\x70\x3a\x9d\x90\xcb\xe5\
+\x15\x42\x06\x83\x01\xf1\xed\x44\xd9\xe1\x96\x22\x93\x49\x23\xb2\
+\xb2\x92\xcb\x13\xf2\x2e\xbb\x77\x53\x10\xe9\xd5\x5c\x36\x17\x10\
+\xf3\x85\xb4\x20\xe4\xaa\x0a\x70\x1c\x07\x9d\x5e\x87\xad\xcd\xcd\
+\x8a\x3d\x4a\x28\xdc\xee\xd9\x1d\x42\xf1\x99\xeb\x97\x6b\xf3\x15\
+\x06\x8f\xee\x5c\x5f\x37\x09\x91\xc3\xdb\xe9\xe4\xd5\x87\x0f\x5d\
+\x3b\xc1\x60\x40\xa4\xa4\xb2\xdf\xcd\x26\x33\xd6\x37\x36\x2a\xd6\
+\x03\xc1\x80\x98\xcb\x09\xee\x89\xdb\xa3\x37\x80\x92\x71\x5d\x8a\
+\xf1\xf1\x71\xe9\xc1\xad\xd1\x6f\x28\xc4\x8e\x60\x20\xf4\x68\xd2\
+\xe5\x4a\x27\x92\xc9\x32\x8e\xd1\x68\x40\x34\x1a\xdd\x1d\x80\x00\
+\x90\x4a\x26\x11\x0a\x85\x04\x92\xa7\x67\xe9\x8b\xee\x61\xfe\xeb\
+\xc9\x2c\x45\xff\xe0\xa5\x73\x8c\x8c\xb9\xd1\x60\xb5\x2a\x5a\xec\
+\xad\x35\x9c\xec\xf9\x38\x9f\x7a\xf6\x0c\x0d\x8d\x36\x98\x4c\x46\
+\x10\x42\xe1\x9a\x9c\xdc\xc9\xe6\x73\x17\x1e\xdc\x1a\xfd\xb9\x18\
+\x5b\x35\x83\xbd\x78\x70\xe7\xfa\xed\x1c\x53\x78\x25\xb2\xb2\xf2\
+\xc7\xc4\xc4\xc4\x4e\xb1\xf6\x46\x93\x09\xeb\x2f\xba\x69\xde\xef\
+\x2f\x14\x0a\xf9\x87\xa5\xe2\x2f\x6d\x00\x00\x8f\xc7\x7e\x88\xde\
+\x1b\xbb\x76\x36\x5f\x10\xde\x76\xcf\xce\xae\x4d\xcf\x4c\xef\xe8\
+\xf6\xef\x47\x74\x6b\x13\xf1\xf8\x36\x96\x96\x97\x72\x3b\x05\x69\
+\x68\x6f\xdc\x4b\x95\x68\x2f\x7a\xcf\x8c\xa8\x14\x4a\x7c\x05\x06\
+\xe7\x95\x4a\x85\x4a\x14\x45\x41\x92\xc4\xe1\x7b\xb7\xae\xff\xf6\
+\xbf\x18\x14\xd1\x37\x74\xf9\x98\x5c\xc6\xdc\x61\xc1\x4c\xff\x39\
+\xf6\x5d\xc5\xdf\x03\xc0\x3f\x4b\x1d\x48\xe6\x9d\x4c\x36\xb6\x00\
+\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\x00\x00\x07\x53\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -7579,6 +7801,46 @@
\x14\xaa\x9a\xa3\x61\x0c\xfc\x0e\x5c\x02\xae\x00\x37\xf9\x3f\xda\
\x7f\xdb\xbb\xc9\xf9\x3e\x97\xfc\xce\x00\x00\x00\x00\x49\x45\x4e\
\x44\xae\x42\x60\x82\
+\x00\x00\x02\x5f\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
+\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\xdc\x49\x44\
+\x41\x54\x48\x89\xed\x95\x31\x6b\x53\x51\x18\x86\x9f\x73\xce\x35\
+\xbd\x1a\x92\x96\xa6\x12\x6f\x17\x35\x01\x69\x4a\x47\x29\x45\x24\
+\x6a\x8a\xfe\x03\x25\x38\xe8\x22\x4e\x52\x08\x41\xff\x84\x38\x89\
+\x4e\x59\xe2\x20\x82\xb3\x60\x14\xdb\x24\x5a\x91\x8c\x0e\x56\x1a\
+\xa8\x0e\xc6\x24\xd0\xd0\xe6\x56\xed\x25\xf4\x9e\xe3\x20\xc6\x62\
+\x09\xd8\x5b\x70\xca\x37\x9e\xef\x3d\xdf\xf3\xbe\xc3\x77\x8e\x38\
+\x7f\x2d\xf7\x5e\x1b\x93\xe4\x00\x25\x10\x9d\x11\x54\xaa\xf4\xe8\
+\xee\xf7\xbf\x7b\x96\xaf\xf5\x54\x3a\x7d\x4e\x29\x29\x03\x03\x5e\
+\x57\xab\xb1\xde\xf6\x8f\x08\xb0\x17\x00\x70\xc8\x52\x48\xa9\x02\
+\x03\x04\x06\x33\xa0\x17\xdc\xf6\x3f\xd6\x10\x30\x04\x0c\x01\xff\
+\x01\x60\x05\xb9\x34\x26\x3e\x90\x34\x05\x39\xca\x2a\x00\x99\x0c\
+\x47\x80\xe6\x72\xf1\x61\x5f\x63\xa0\x26\x24\xf9\x40\x80\xa4\x29\
+\xc8\xf1\x50\x1b\x3b\x72\x1a\xa9\x6c\xa4\xb2\x49\x5d\x7a\xc9\xca\
+\x8b\x8b\x68\xdf\x43\xfb\x1e\xde\x56\x7d\x76\xa7\xd7\xbd\x17\x08\
+\x30\xca\x2a\xe1\x58\x06\x65\x85\xc1\xfc\x79\xe6\x7e\xc3\x10\x02\
+\x15\x8a\xe2\x36\x17\x67\x03\x01\x00\x26\x8e\x5f\x21\x3a\x39\x4f\
+\xe7\xd3\x13\xa2\xf1\x34\x00\x93\x33\xb7\x71\xdb\x55\x62\x27\xb3\
+\xb8\x5f\x5f\xe1\x36\x17\xb1\xa4\x10\xeb\x4b\x4b\xe5\xf1\xfd\x0c\
+\xcf\xcc\x33\xb2\xf1\xe5\x19\xfe\xce\x37\x9c\x99\x3b\x18\xdd\x63\
+\xb3\xf1\x9c\x48\xfc\x2c\xe1\xa3\x73\xb4\x57\xee\xe3\xb6\x2a\x00\
+\x58\x42\x6d\x26\x60\xcc\xde\x67\x80\x0d\x30\xb8\xad\x32\xf1\xa9\
+\x5b\x6c\xb5\xdf\xd0\xfe\xf8\x00\x80\xe8\xb1\x0b\xb8\xad\x72\x5f\
+\x28\x8c\x19\xf4\x55\x0c\xae\xe5\xa2\x30\x20\x98\x48\x5c\x25\x71\
+\xa6\x80\xf6\xb7\xe9\x7c\x7e\x4a\xec\xc4\x65\xa4\x3a\xcc\xda\xdb\
+\x1b\xac\xaf\x3d\x06\x4c\xf0\x3d\x70\x52\x0b\x38\xd3\x39\xea\x95\
+\x2c\xdd\x46\x89\xf8\xa9\x9b\x74\x1b\x25\xea\x95\x2c\xce\x74\x0e\
+\x27\xb5\x70\xb0\x04\x52\xd9\x68\xdd\x03\xa3\x01\x98\xbb\x6e\x78\
+\x57\x14\xbf\x04\x42\x22\x65\x08\xed\x7b\xc1\x12\x18\xa8\x69\xdf\
+\xeb\x0f\xdf\x2b\xd0\x68\xdf\xc3\x40\x2d\x10\x40\x48\xf2\x06\x6a\
+\xbb\xcf\xfa\xee\x77\x99\x10\x92\xfc\x4f\xfd\xc2\xaa\x73\x0e\x12\
+\x9d\x40\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\x00\x00\x04\xfd\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -8024,6 +8286,10 @@
\x09\x28\xf5\xe7\
\x00\x66\
\x00\x74\x00\x6f\x00\x6f\x00\x6c\x00\x73\x00\x5f\x00\x6c\x00\x6f\x00\x67\x00\x6f\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x0c\
+\x09\xd4\x06\xc7\
+\x00\x64\
+\x00\x65\x00\x6c\x00\x61\x00\x75\x00\x6e\x00\x61\x00\x79\x00\x2e\x00\x70\x00\x6e\x00\x67\
\x00\x15\
\x0a\xb1\xae\x07\
\x00\x64\
@@ -8109,6 +8375,10 @@
\x00\x65\
\x00\x78\x00\x70\x00\x6f\x00\x72\x00\x74\x00\x5f\x00\x67\x00\x65\x00\x6f\x00\x6d\x00\x65\x00\x74\x00\x72\x00\x79\x00\x2e\x00\x70\
\x00\x6e\x00\x67\
+\x00\x10\
+\x05\x40\x6b\xe7\
+\x00\x6c\
+\x00\x61\x00\x79\x00\x65\x00\x72\x00\x5f\x00\x65\x00\x78\x00\x74\x00\x65\x00\x6e\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\
\x00\x13\
\x06\xd5\x17\xc7\
\x00\x73\
@@ -8133,91 +8403,95 @@
qt_resource_struct = "\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\
-\x00\x00\x00\x24\x00\x02\x00\x00\x00\x2a\x00\x00\x00\x2e\
-\x00\x00\x00\x10\x00\x02\x00\x00\x00\x2a\x00\x00\x00\x04\
-\x00\x00\x03\x6c\x00\x00\x00\x00\x00\x01\x00\x01\x6e\x70\
-\x00\x00\x02\xd2\x00\x00\x00\x00\x00\x01\x00\x01\x4e\xaa\
-\x00\x00\x03\xe2\x00\x00\x00\x00\x00\x01\x00\x01\x86\x54\
-\x00\x00\x00\xb6\x00\x00\x00\x00\x00\x01\x00\x00\xd0\x92\
-\x00\x00\x03\x84\x00\x00\x00\x00\x00\x01\x00\x01\x74\xc5\
-\x00\x00\x00\xe6\x00\x00\x00\x00\x00\x01\x00\x00\xdb\x54\
-\x00\x00\x00\x6a\x00\x00\x00\x00\x00\x01\x00\x00\xc1\x2f\
-\x00\x00\x05\x32\x00\x00\x00\x00\x00\x01\x00\x01\xc9\x5a\
-\x00\x00\x02\x1c\x00\x00\x00\x00\x00\x01\x00\x01\x19\xef\
-\x00\x00\x04\x8a\x00\x00\x00\x00\x00\x01\x00\x01\xa9\x20\
-\x00\x00\x01\xcc\x00\x00\x00\x00\x00\x01\x00\x01\x09\x30\
-\x00\x00\x04\xf4\x00\x00\x00\x00\x00\x01\x00\x01\xbc\xe0\
-\x00\x00\x05\x5e\x00\x00\x00\x00\x00\x01\x00\x01\xcd\xf4\
-\x00\x00\x04\xb4\x00\x00\x00\x00\x00\x01\x00\x01\xaf\xb7\
-\x00\x00\x02\xf0\x00\x00\x00\x00\x00\x01\x00\x01\x55\xc4\
-\x00\x00\x03\x1c\x00\x00\x00\x00\x00\x01\x00\x01\x5b\x84\
-\x00\x00\x00\x8e\x00\x00\x00\x00\x00\x01\x00\x00\xc8\x9b\
-\x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00\xd5\x72\
-\x00\x00\x05\xb6\x00\x00\x00\x00\x00\x01\x00\x01\xd8\x12\
-\x00\x00\x02\x64\x00\x00\x00\x00\x00\x01\x00\x01\x26\xcd\
-\x00\x00\x02\x7e\x00\x00\x00\x00\x00\x01\x00\x01\x31\x5b\
-\x00\x00\x05\xe4\x00\x00\x00\x00\x00\x01\x00\x01\xdb\xbd\
-\x00\x00\x01\xa2\x00\x00\x00\x00\x00\x01\x00\x01\x04\x55\
-\x00\x00\x04\x60\x00\x00\x00\x00\x00\x01\x00\x01\xa1\xda\
-\x00\x00\x02\xa2\x00\x00\x00\x00\x00\x01\x00\x01\x47\x53\
-\x00\x00\x01\x4a\x00\x00\x00\x00\x00\x01\x00\x00\xf3\xf7\
-\x00\x00\x01\x82\x00\x00\x00\x00\x00\x01\x00\x00\xfe\x7e\
-\x00\x00\x01\x6c\x00\x00\x00\x00\x00\x01\x00\x00\xfa\xa4\
-\x00\x00\x05\x8a\x00\x00\x00\x00\x00\x01\x00\x01\xd2\xf5\
-\x00\x00\x04\x20\x00\x00\x00\x00\x00\x01\x00\x01\x95\x53\
-\x00\x00\x00\x54\x00\x00\x00\x00\x00\x01\x00\x00\xb8\x76\
-\x00\x00\x05\x12\x00\x00\x00\x00\x00\x01\x00\x01\xc4\xc6\
-\x00\x00\x01\x04\x00\x00\x00\x00\x00\x01\x00\x00\xe2\x5f\
-\x00\x00\x04\xd2\x00\x00\x00\x00\x00\x01\x00\x01\xb7\x0b\
-\x00\x00\x00\x30\x00\x00\x00\x00\x00\x01\x00\x00\xb1\x9f\
-\x00\x00\x04\x3e\x00\x00\x00\x00\x00\x01\x00\x01\x9a\x4e\
-\x00\x00\x04\x02\x00\x00\x00\x00\x00\x01\x00\x01\x8e\xeb\
-\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x01\x00\x01\x0f\x84\
-\x00\x00\x03\xb4\x00\x00\x00\x00\x00\x01\x00\x01\x7c\x32\
-\x00\x00\x01\x1e\x00\x00\x00\x00\x00\x01\x00\x00\xec\x50\
-\x00\x00\x03\x44\x00\x00\x00\x00\x00\x01\x00\x01\x65\x33\
-\x00\x00\x02\x44\x00\x00\x00\x00\x00\x01\x00\x01\x20\xeb\
-\x00\x00\x03\x6c\x00\x00\x00\x00\x00\x01\x00\x00\x6d\x8f\
-\x00\x00\x02\xd2\x00\x00\x00\x00\x00\x01\x00\x00\x5d\xf4\
-\x00\x00\x03\xe2\x00\x00\x00\x00\x00\x01\x00\x00\x78\x85\
+\x00\x00\x00\x24\x00\x02\x00\x00\x00\x2c\x00\x00\x00\x30\
+\x00\x00\x00\x10\x00\x02\x00\x00\x00\x2c\x00\x00\x00\x04\
+\x00\x00\x03\x8a\x00\x00\x00\x00\x00\x01\x00\x01\x7b\xf3\
+\x00\x00\x02\xf0\x00\x00\x00\x00\x00\x01\x00\x01\x5c\x2d\
+\x00\x00\x04\x00\x00\x00\x00\x00\x00\x01\x00\x01\x93\xd7\
+\x00\x00\x00\xb6\x00\x00\x00\x00\x00\x01\x00\x00\xd8\x85\
+\x00\x00\x03\xa2\x00\x00\x00\x00\x00\x01\x00\x01\x82\x48\
+\x00\x00\x00\xe6\x00\x00\x00\x00\x00\x01\x00\x00\xe3\x47\
+\x00\x00\x00\x6a\x00\x00\x00\x00\x00\x01\x00\x00\xc9\x22\
+\x00\x00\x05\x50\x00\x00\x00\x00\x00\x01\x00\x01\xd6\xdd\
+\x00\x00\x02\x1c\x00\x00\x00\x00\x00\x01\x00\x01\x21\xe2\
+\x00\x00\x05\x7c\x00\x00\x00\x00\x00\x01\x00\x01\xdb\x77\
+\x00\x00\x04\xa8\x00\x00\x00\x00\x00\x01\x00\x01\xb6\xa3\
+\x00\x00\x01\xcc\x00\x00\x00\x00\x00\x01\x00\x01\x11\x23\
+\x00\x00\x05\x12\x00\x00\x00\x00\x00\x01\x00\x01\xca\x63\
+\x00\x00\x05\xa2\x00\x00\x00\x00\x00\x01\x00\x01\xdd\xda\
+\x00\x00\x04\xd2\x00\x00\x00\x00\x00\x01\x00\x01\xbd\x3a\
+\x00\x00\x03\x0e\x00\x00\x00\x00\x00\x01\x00\x01\x63\x47\
+\x00\x00\x03\x3a\x00\x00\x00\x00\x00\x01\x00\x01\x69\x07\
+\x00\x00\x00\x8e\x00\x00\x00\x00\x00\x01\x00\x00\xd0\x8e\
+\x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00\xdd\x65\
+\x00\x00\x05\xfa\x00\x00\x00\x00\x00\x01\x00\x01\xe7\xf8\
+\x00\x00\x02\x64\x00\x00\x00\x00\x00\x01\x00\x01\x2e\xc0\
+\x00\x00\x02\x7e\x00\x00\x00\x00\x00\x01\x00\x01\x39\x4e\
+\x00\x00\x06\x28\x00\x00\x00\x00\x00\x01\x00\x01\xeb\xa3\
+\x00\x00\x02\xa2\x00\x00\x00\x00\x00\x01\x00\x01\x4f\x46\
+\x00\x00\x01\xa2\x00\x00\x00\x00\x00\x01\x00\x01\x0c\x48\
+\x00\x00\x04\x7e\x00\x00\x00\x00\x00\x01\x00\x01\xaf\x5d\
+\x00\x00\x02\xc0\x00\x00\x00\x00\x00\x01\x00\x01\x54\xd6\
+\x00\x00\x01\x4a\x00\x00\x00\x00\x00\x01\x00\x00\xfb\xea\
+\x00\x00\x01\x82\x00\x00\x00\x00\x00\x01\x00\x01\x06\x71\
+\x00\x00\x01\x6c\x00\x00\x00\x00\x00\x01\x00\x01\x02\x97\
+\x00\x00\x05\xce\x00\x00\x00\x00\x00\x01\x00\x01\xe2\xdb\
+\x00\x00\x04\x3e\x00\x00\x00\x00\x00\x01\x00\x01\xa2\xd6\
+\x00\x00\x00\x54\x00\x00\x00\x00\x00\x01\x00\x00\xc0\x69\
+\x00\x00\x05\x30\x00\x00\x00\x00\x00\x01\x00\x01\xd2\x49\
+\x00\x00\x01\x04\x00\x00\x00\x00\x00\x01\x00\x00\xea\x52\
+\x00\x00\x04\xf0\x00\x00\x00\x00\x00\x01\x00\x01\xc4\x8e\
+\x00\x00\x00\x30\x00\x00\x00\x00\x00\x01\x00\x00\xb9\x92\
+\x00\x00\x04\x5c\x00\x00\x00\x00\x00\x01\x00\x01\xa7\xd1\
+\x00\x00\x04\x20\x00\x00\x00\x00\x00\x01\x00\x01\x9c\x6e\
+\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x01\x00\x01\x17\x77\
+\x00\x00\x03\xd2\x00\x00\x00\x00\x00\x01\x00\x01\x89\xb5\
+\x00\x00\x01\x1e\x00\x00\x00\x00\x00\x01\x00\x00\xf4\x43\
+\x00\x00\x03\x62\x00\x00\x00\x00\x00\x01\x00\x01\x72\xb6\
+\x00\x00\x02\x44\x00\x00\x00\x00\x00\x01\x00\x01\x28\xde\
+\x00\x00\x03\x8a\x00\x00\x00\x00\x00\x01\x00\x00\x73\x1f\
+\x00\x00\x02\xf0\x00\x00\x00\x00\x00\x01\x00\x00\x63\x84\
+\x00\x00\x04\x00\x00\x00\x00\x00\x00\x01\x00\x00\x7e\x15\
\x00\x00\x00\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x13\x9c\
-\x00\x00\x03\x84\x00\x00\x00\x00\x00\x01\x00\x00\x70\xcd\
+\x00\x00\x03\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x76\x5d\
\x00\x00\x00\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x19\x04\
\x00\x00\x00\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x09\xbd\
-\x00\x00\x05\x32\x00\x00\x00\x00\x00\x01\x00\x00\xa0\x0a\
+\x00\x00\x05\x50\x00\x00\x00\x00\x00\x01\x00\x00\xa5\x9a\
\x00\x00\x02\x1c\x00\x00\x00\x00\x00\x01\x00\x00\x3a\xc5\
-\x00\x00\x04\x8a\x00\x00\x00\x00\x00\x01\x00\x00\x8a\x39\
+\x00\x00\x05\x7c\x00\x00\x00\x00\x00\x01\x00\x00\xa8\x7b\
+\x00\x00\x04\xa8\x00\x00\x00\x00\x00\x01\x00\x00\x8f\xc9\
\x00\x00\x01\xcc\x00\x00\x00\x00\x00\x01\x00\x00\x33\xf7\
-\x00\x00\x04\xf4\x00\x00\x00\x00\x00\x01\x00\x00\x96\x18\
-\x00\x00\x05\x5e\x00\x00\x00\x00\x00\x01\x00\x00\xa2\xeb\
-\x00\x00\x04\xb4\x00\x00\x00\x00\x00\x01\x00\x00\x8d\x88\
-\x00\x00\x02\xf0\x00\x00\x00\x00\x00\x01\x00\x00\x61\x96\
-\x00\x00\x03\x1c\x00\x00\x00\x00\x00\x01\x00\x00\x64\x12\
+\x00\x00\x05\x12\x00\x00\x00\x00\x00\x01\x00\x00\x9b\xa8\
+\x00\x00\x05\xa2\x00\x00\x00\x00\x00\x01\x00\x00\xaa\xde\
+\x00\x00\x04\xd2\x00\x00\x00\x00\x00\x01\x00\x00\x93\x18\
+\x00\x00\x03\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x67\x26\
+\x00\x00\x03\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x69\xa2\
\x00\x00\x00\x8e\x00\x00\x00\x00\x00\x01\x00\x00\x0d\x65\
\x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00\x17\x33\
-\x00\x00\x05\xb6\x00\x00\x00\x00\x00\x01\x00\x00\xab\xd0\
+\x00\x00\x05\xfa\x00\x00\x00\x00\x00\x01\x00\x00\xb3\xc3\
\x00\x00\x02\x64\x00\x00\x00\x00\x00\x01\x00\x00\x41\x8a\
\x00\x00\x02\x7e\x00\x00\x00\x00\x00\x01\x00\x00\x42\x98\
-\x00\x00\x05\xe4\x00\x00\x00\x00\x00\x01\x00\x00\xad\xfd\
+\x00\x00\x06\x28\x00\x00\x00\x00\x00\x01\x00\x00\xb5\xf0\
+\x00\x00\x02\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x58\x90\
\x00\x00\x01\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x31\x1f\
-\x00\x00\x04\x60\x00\x00\x00\x00\x00\x01\x00\x00\x88\xd6\
-\x00\x00\x02\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x58\x90\
+\x00\x00\x04\x7e\x00\x00\x00\x00\x00\x01\x00\x00\x8e\x66\
+\x00\x00\x02\xc0\x00\x00\x00\x00\x00\x01\x00\x00\x5e\x20\
\x00\x00\x01\x4a\x00\x00\x00\x00\x00\x01\x00\x00\x25\x4f\
\x00\x00\x01\x82\x00\x00\x00\x00\x00\x01\x00\x00\x2d\xad\
\x00\x00\x01\x6c\x00\x00\x00\x00\x00\x01\x00\x00\x28\x55\
-\x00\x00\x05\x8a\x00\x00\x00\x00\x00\x01\x00\x00\xa7\x57\
-\x00\x00\x04\x20\x00\x00\x00\x00\x00\x01\x00\x00\x80\x2b\
+\x00\x00\x05\xce\x00\x00\x00\x00\x00\x01\x00\x00\xaf\x4a\
+\x00\x00\x04\x3e\x00\x00\x00\x00\x00\x01\x00\x00\x85\xbb\
\x00\x00\x00\x54\x00\x00\x00\x00\x00\x01\x00\x00\x01\x04\
-\x00\x00\x05\x12\x00\x00\x00\x00\x00\x01\x00\x00\x9b\x3f\
+\x00\x00\x05\x30\x00\x00\x00\x00\x00\x01\x00\x00\xa0\xcf\
\x00\x00\x01\x04\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xe8\
-\x00\x00\x04\xd2\x00\x00\x00\x00\x00\x01\x00\x00\x92\x34\
+\x00\x00\x04\xf0\x00\x00\x00\x00\x00\x01\x00\x00\x97\xc4\
\x00\x00\x00\x30\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x04\x3e\x00\x00\x00\x00\x00\x01\x00\x00\x83\x8d\
-\x00\x00\x04\x02\x00\x00\x00\x00\x00\x01\x00\x00\x7d\x6e\
+\x00\x00\x04\x5c\x00\x00\x00\x00\x00\x01\x00\x00\x89\x1d\
+\x00\x00\x04\x20\x00\x00\x00\x00\x00\x01\x00\x00\x82\xfe\
\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x01\x00\x00\x37\x71\
-\x00\x00\x03\xb4\x00\x00\x00\x00\x00\x01\x00\x00\x76\x38\
+\x00\x00\x03\xd2\x00\x00\x00\x00\x00\x01\x00\x00\x7b\xc8\
\x00\x00\x01\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x22\x2e\
-\x00\x00\x03\x44\x00\x00\x00\x00\x00\x01\x00\x00\x67\x31\
+\x00\x00\x03\x62\x00\x00\x00\x00\x00\x01\x00\x00\x6c\xc1\
\x00\x00\x02\x44\x00\x00\x00\x00\x00\x01\x00\x00\x3d\xd6\
"
Modified: trunk/qgis/python/plugins/fTools/resources.qrc
===================================================================
--- trunk/qgis/python/plugins/fTools/resources.qrc 2009-03-05 15:06:30 UTC (rev 10264)
+++ trunk/qgis/python/plugins/fTools/resources.qrc 2009-03-09 00:20:39 UTC (rev 10265)
@@ -42,6 +42,8 @@
<file>icons/default/vector_grid.png</file>
<file>icons/default/split_layer.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>
@@ -84,5 +86,7 @@
<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>
</qresource>
</RCC>
Modified: trunk/qgis/python/plugins/fTools/tools/CMakeLists.txt
===================================================================
--- trunk/qgis/python/plugins/fTools/tools/CMakeLists.txt 2009-03-05 15:06:30 UTC (rev 10264)
+++ trunk/qgis/python/plugins/fTools/tools/CMakeLists.txt 2009-03-09 00:20:39 UTC (rev 10265)
@@ -53,6 +53,7 @@
doVectorGrid.py
frmRandPoints.py
ftools_utils.py
+voronoi.py
doVectorSplit.py
frmRandPoints.ui
)
Modified: trunk/qgis/python/plugins/fTools/tools/doGeometry.py
===================================================================
--- trunk/qgis/python/plugins/fTools/tools/doGeometry.py 2009-03-05 15:06:30 UTC (rev 10264)
+++ trunk/qgis/python/plugins/fTools/tools/doGeometry.py 2009-03-09 00:20:39 UTC (rev 10265)
@@ -4,6 +4,7 @@
from frmGeometry import Ui_Dialog
import ftools_utils
import math
+from itertools import izip
class GeometryDialog(QDialog, Ui_Dialog):
@@ -92,7 +93,7 @@
self.label_2.setText( self.tr( "Output shapefile" ) )
self.cmbField.setVisible(False)
self.field_label.setVisible(False)
- else: # Polygon centroids
+ elif self.myFunction == 7: # Polygon centroids
self.setWindowTitle( self.tr( "Polygon centroids" ) )
self.label_2.setText( self.tr( "Output point shapefile" ) )
self.label_3.setText( self.tr( "Input polygon vector layer" ) )
@@ -100,6 +101,18 @@
self.lineEdit.setVisible( False )
self.cmbField.setVisible( False )
self.field_label.setVisible( False )
+ else:
+ if self.myFunction == 8: # Delaunay triangulation
+ self.setWindowTitle( self.tr( "Delaunay triangulation" ) )
+ self.label_3.setText( self.tr( "Input point vector layer" ) )
+ else: # Polygon from layer extent
+ self.setWindowTitle( self.tr( "Polygon from layer extent" ) )
+ self.label_3.setText( self.tr( "Input layer" ) )
+ 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 )
myList = []
self.inShape.clear()
@@ -107,6 +120,10 @@
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:
+ myList = ftools_utils.getLayerNames( [ QGis.Point ] )
+ elif self.myFunction == 9:
+ myList = ftools_utils.getLayerNames( "all" )
else:
myList = ftools_utils.getLayerNames( [ QGis.Point, QGis.Line, QGis.Polygon ] )
self.inShape.addItems( myList )
@@ -119,9 +136,14 @@
#5: Export/Add geometry columns
#6: Simplify geometries
#7: Polygon centroids
+#8: Delaunay triangulation
+#9: Polygon from layer extent
def geometry( self, myLayer, myParam, myField ):
- vlayer = ftools_utils.getVectorLayerByName( myLayer )
+ if self.myFunction == 9:
+ vlayer = ftools_utils.getMapLayerByName( myLayer )
+ else:
+ vlayer = ftools_utils.getVectorLayerByName( myLayer )
error = False
check = QFile( self.shapefileName )
if check.exists():
@@ -143,21 +165,21 @@
def runFinishedFromThread( self, success ):
self.testThread.stop()
if success == "math_error":
- QMessageBox.warning( self, "Geoprocessing", self.tr( "Error processing specified tolerance!" ) + "\n"
+ QMessageBox.warning( self, "Geometry", self.tr( "Error processing specified tolerance!" ) + "\n"
+ self.tr( "Please choose larger tolerance..." ) )
if not QgsVectorFileWriter.deleteShapeFile( self.shapefileName ):
- QMessageBox.warning( self, "Geoprocessing", self.tr( "Unable to delete incomplete shapefile." ) )
+ QMessageBox.warning( self, "Geometry", self.tr( "Unable to delete incomplete shapefile." ) )
else:
self.cancel_close.setText( "Close" )
QObject.disconnect( self.cancel_close, SIGNAL( "clicked()" ), self.cancelThread )
if success:
- addToTOC = QMessageBox.question( self, "Geoprocessing", self.tr( "Created output shapefile:" ) + "\n" +
+ addToTOC = QMessageBox.question( self, "Geometry", self.tr( "Created output shapefile:" ) + "\n" +
unicode( self.shapefileName ) + "\n\n" + self.tr( "Would you like to add the new layer to the TOC?" ),
QMessageBox.Yes, QMessageBox.No, QMessageBox.NoButton )
if addToTOC == QMessageBox.Yes:
ftools_utils.addShapeToCanvas( unicode( self.shapefileName ) )
else:
- QMessageBox.warning( self, "Geoprocessing", self.tr( "Error writing output shapefile." ) )
+ QMessageBox.warning( self, "Geometry", self.tr( "Error writing output shapefile." ) )
def runStatusFromThread( self, status ):
self.progressBar.setValue( status )
@@ -193,6 +215,10 @@
success = self.simplify_geometry()
elif self.myFunction == 7: # Polygon centroids
success = self.polygon_centroids()
+ elif self.myFunction == 8: # Delaunay triangulation
+ success = self.delaunay_triangulation()
+ elif self.myFunction == 9: # Polygon from layer extent
+ success = self.layer_extent()
self.emit( SIGNAL( "runFinished(PyQt_PyObject)" ), success )
self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 )
@@ -452,7 +478,109 @@
self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), nElement )
del writer
return True
+
+ def delaunay_triangulation( self ):
+ import voronoi
+ vprovider = self.vlayer.dataProvider()
+ allAttrs = vprovider.attributeIndexes()
+ vprovider.select( allAttrs )
+ fields = {
+ 0 : QgsField( "POINTA", QVariant.Double ),
+ 1 : QgsField( "POINTB", QVariant.Double ),
+ 2 : QgsField( "POINTC", QVariant.Double ) }
+ writer = QgsVectorFileWriter( self.myName, self.myEncoding,
+ fields, QGis.WKBPolygon, vprovider.crs() )
+ inFeat = QgsFeature()
+ points = []
+ print "here"
+ while vprovider.nextFeature( inFeat ):
+ inGeom = QgsGeometry( inFeat.geometry() )
+ point = inGeom.asPoint()
+ points.append( point )
+ print "or here"
+ vprovider.rewind()
+ vprovider.select( allAttrs )
+ triangles = voronoi.computeDelaunayTriangulation( points )
+ 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 ] )
+ polygon = []
+ step = 0
+ for index in indicies:
+ vprovider.featureAtId( index, feat, True, allAttrs )
+ geom = QgsGeometry( feat.geometry() )
+ point = QgsPoint( geom.asPoint() )
+ polygon.append( point )
+ feat.addAttribute( step, QVariant( index ) )
+ step += 1
+ geometry = QgsGeometry().fromPolygon( [ polygon ] )
+ feat.setGeometry( geometry )
+ writer.addFeature( feat )
+ nElement += 1
+ self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), nElement )
+ del writer
+ return True
+
+ def layer_extent( self ):
+ self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 )
+ self.emit( SIGNAL( "runRange(PyQt_PyObject)" ), ( 0, 0 ) )
+ fields = {
+ 0 : QgsField( "MINX", QVariant.Double ),
+ 1 : QgsField( "MINY", QVariant.Double ),
+ 2 : QgsField( "MAXX", QVariant.Double ),
+ 3 : QgsField( "MAXY", QVariant.Double ),
+ 4 : QgsField( "CNTX", QVariant.Double ),
+ 5 : QgsField( "CNTY", QVariant.Double ),
+ 6 : QgsField( "AREA", QVariant.Double ),
+ 7 : QgsField( "PERIM", QVariant.Double ),
+ 8 : QgsField( "HEIGHT", QVariant.Double ),
+ 9 : QgsField( "WIDTH", QVariant.Double ) }
+ writer = QgsVectorFileWriter( self.myName, self.myEncoding,
+ fields, QGis.WKBPolygon, self.vlayer.srs() )
+ rect = self.vlayer.extent()
+ minx = rect.xMinimum()
+ miny = rect.yMinimum()
+ maxx = rect.xMaximum()
+ maxy = rect.yMaximum()
+ height = rect.height()
+ width = rect.width()
+ cntx = minx + ( width / 2.0 )
+ cnty = miny + ( height / 2.0 )
+ area = width * height
+ perim = ( 2 * width ) + (2 * height )
+ rect = [
+ QgsPoint( minx, miny ),
+ QgsPoint( minx, maxy ),
+ QgsPoint( maxx, maxy ),
+ QgsPoint( maxx, miny ),
+ QgsPoint( minx, miny ) ]
+ geometry = QgsGeometry().fromPolygon( [ rect ] )
+ feat = QgsFeature()
+ feat.setGeometry( geometry )
+ feat.setAttributeMap( {
+ 0 : QVariant( minx ),
+ 1 : QVariant( miny ),
+ 2 : QVariant( maxx ),
+ 3 : QVariant( maxy ),
+ 4 : QVariant( cntx ),
+ 5 : QVariant( cnty ),
+ 6 : QVariant( area ),
+ 7 : QVariant( perim ),
+ 8 : QVariant( height ),
+ 9 : QVariant( width ) } )
+ writer.addFeature( feat )
+ self.emit( SIGNAL( "runRange(PyQt_PyObject)" ), ( 0, 100 ) )
+ self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 )
+ del writer
+
+ return True
+
def extractAsSimple( self, geom, tolerance ):
temp_geom1 = []
temp_geom2 = []
Modified: trunk/qgis/python/plugins/fTools/tools/doRandPoints.py
===================================================================
--- trunk/qgis/python/plugins/fTools/tools/doRandPoints.py 2009-03-05 15:06:30 UTC (rev 10264)
+++ trunk/qgis/python/plugins/fTools/tools/doRandPoints.py 2009-03-09 00:20:39 UTC (rev 10265)
@@ -218,7 +218,6 @@
if not QgsVectorFileWriter.deleteShapeFile(self.shapefileName):
return
writer = QgsVectorFileWriter(self.shapefileName, self.encoding, fields, QGis.WKBPoint, None)
- #writer = QgsVectorFileWriter(unicode(outPath), "CP1250", fields, QGis.WKBPoint, None)
idVar = 0
count = 70.00
add = 30.00 / len(points)
Modified: trunk/qgis/python/plugins/fTools/tools/ftools_utils.py
===================================================================
--- trunk/qgis/python/plugins/fTools/tools/ftools_utils.py 2009-03-05 15:06:30 UTC (rev 10264)
+++ trunk/qgis/python/plugins/fTools/tools/ftools_utils.py 2009-03-09 00:20:39 UTC (rev 10265)
@@ -159,10 +159,14 @@
def getLayerNames( vTypes ):
layermap = QgsMapLayerRegistry.instance().mapLayers()
layerlist = []
- for name, layer in layermap.iteritems():
- if layer.type() == QgsMapLayer.VectorLayer:
- if layer.geometryType() in vTypes and not layer.name() in layerlist:
- layerlist.append( unicode( layer.name() ) )
+ if vTypes == "all":
+ for name, layer in layermap.iteritems():
+ layerlist.append( unicode( layer.name() ) )
+ else:
+ for name, layer in layermap.iteritems():
+ if layer.type() == QgsMapLayer.VectorLayer:
+ if layer.geometryType() in vTypes:
+ layerlist.append( unicode( layer.name() ) )
return layerlist
# Return list of names of all fields from input QgsVectorLayer
Added: trunk/qgis/python/plugins/fTools/tools/voronoi.py
===================================================================
--- trunk/qgis/python/plugins/fTools/tools/voronoi.py (rev 0)
+++ trunk/qgis/python/plugins/fTools/tools/voronoi.py 2009-03-09 00:20:39 UTC (rev 10265)
@@ -0,0 +1,838 @@
+#############################################################################
+#
+# Voronoi diagram calculator/ Delaunay triangulator
+# Translated to Python by Bill Simons
+# September, 2005
+#
+# Calculate Delaunay triangulation or the Voronoi polygons for a set of
+# 2D input points.
+#
+# Derived from code bearing the following notice:
+#
+# The author of this software is Steven Fortune. Copyright (c) 1994 by AT&T
+# Bell Laboratories.
+# Permission to use, copy, modify, and distribute this software for any
+# purpose without fee is hereby granted, provided that this entire notice
+# is included in all copies of any software which is or includes a copy
+# or modification of this software and in all copies of the supporting
+# documentation for such software.
+# THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+# WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
+# REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+# OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+#
+# Comments were incorporated from Shane O'Sullivan's translation of the
+# original code into C++ (http://mapviewer.skynet.ie/voronoi.html)
+#
+# Steve Fortune's homepage: http://netlib.bell-labs.com/cm/cs/who/sjf/index.html
+#
+#############################################################################
+
+def usage():
+ print """
+voronoi - compute Voronoi diagram or Delaunay triangulation
+
+voronoi [-t -p -d] [filename]
+
+Voronoi reads from filename (or standard input if no filename given) for a set
+of points in the plane and writes either the Voronoi diagram or the Delaunay
+triangulation to the standard output. Each input line should consist of two
+real numbers, separated by white space.
+
+If option -t is present, the Delaunay triangulation is produced.
+Each output line is a triple i j k, which are the indices of the three points
+in a Delaunay triangle. Points are numbered starting at 0.
+
+If option -t is not present, the Voronoi diagram is produced.
+There are four output record types.
+
+s a b indicates that an input point at coordinates a b was seen.
+l a b c indicates a line with equation ax + by = c.
+v a b indicates a vertex at a b.
+e l v1 v2 indicates a Voronoi segment which is a subsegment of line number l
+ with endpoints numbered v1 and v2. If v1 or v2 is -1, the line
+ extends to infinity.
+
+Other options include:
+
+d Print debugging info
+
+p Produce output suitable for input to plot (1), rather than the forms
+ described above.
+
+On unsorted data uniformly distributed in the unit square, voronoi uses about
+20n+140 bytes of storage.
+
+AUTHOR
+Steve J. Fortune (1987) A Sweepline Algorithm for Voronoi Diagrams,
+Algorithmica 2, 153-174.
+"""
+
+#############################################################################
+#
+# For programmatic use two functions are available:
+#
+# computeVoronoiDiagram(points)
+#
+# Takes a list of point objects (which must have x and y fields).
+# Returns a 3-tuple of:
+#
+# (1) a list of 2-tuples, which are the x,y coordinates of the
+# Voronoi diagram vertices
+# (2) a list of 3-tuples (a,b,c) which are the equations of the
+# lines in the Voronoi diagram: a*x + b*y = c
+# (3) a list of 3-tuples, (l, v1, v2) representing edges of the
+# Voronoi diagram. l is the index of the line, v1 and v2 are
+# the indices of the vetices at the end of the edge. If
+# v1 or v2 is -1, the line extends to infinity.
+#
+# 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.
+#
+#############################################################################
+import math
+import sys
+import getopt
+TOLERANCE = 1e-9
+BIG_FLOAT = 1e38
+
+#------------------------------------------------------------------
+class Context( object ):
+ def __init__(self):
+ self.doPrint = 0
+ self.debug = 0
+ self.plot = 0
+ self.triangulate = False
+ self.vertices = [] # list of vertex 2-tuples: (x,y)
+ 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
+
+ def circle(self,x,y,rad):
+ pass
+
+ def clip_line(self,edge,lid,rid):
+ 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
+
+ def outSite(self,s):
+ if(self.debug):
+ print "site (%d) at %f %f" % (s.sitenum, s.x, s.y)
+ elif(self.triangulate):
+ pass
+ elif(self.plot):
+ self.circle (s.x, s.y, 3) #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):
+ pass
+ elif(self.doPrint and not self.plot):
+ print "v %f %f" % (s.x,s.y)
+
+ def outTriple(self,s1,s2,s3):
+ self.triangles.append((s1.sitenum, s2.sitenum, s3.sitenum))
+ if(self.debug):
+ print "circle through left=%d right=%d bottom=%d" % (s1.sitenum, s2.sitenum, s3.sitenum)
+ elif(self.triangulate and self.doPrint and not self.plot):
+ print "%d %d %d" % (s1.sitenum, s2.sitenum, s3.sitenum)
+
+ def outBisector(self,edge):
+ self.lines.append((edge.a, edge.b, edge.c))
+ if(self.debug):
+ print "line(%d) %gx+%gy=%g, bisecting %d %d" % (edge.edgenum, edge.a, edge.b, edge.c, edge.reg[0].sitenum, edge.reg[1].sitenum)
+ elif(self.triangulate):
+ if(self.plot):
+ self.line(edge.reg[0].x, edge.reg[0].y, edge.reg[1].x, edge.reg[1].y)
+ elif(self.doPrint and not self.plot):
+ print "l %f %f %f" % (edge.a, edge.b, edge.c)
+
+ def outEdge(self,edge):
+ sitenumL = -1
+ if edge.ep[Edge.LE] is not None:
+ sitenumL = edge.ep[Edge.LE].sitenum
+ 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)
+
+ self.edges.append((edge.edgenum,sitenumL,sitenumR))
+ if(not self.triangulate):
+ if self.plot:
+ self.clip_line(edge)
+ elif(self.doPrint):
+ print "e %d" % edge.edgenum,
+ 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()
+
+ 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))
+
+ # 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()
+
+ 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)
+
+ # 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
+
+ # 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))
+
+ # 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
+
+#------------------------------------------------------------------
+def isEqual(a,b,relativeError=TOLERANCE):
+ # is nearly equal to within the allowed relative error
+ norm = max(abs(a),abs(b))
+ return (norm < relativeError) or (abs(a - b) < (relativeError * norm))
+
+#------------------------------------------------------------------
+class Site(object):
+ def __init__(self,x=0.0,y=0.0,sitenum=0):
+ self.x = x
+ self.y = y
+ self.sitenum = sitenum
+
+ def dump(self):
+ print "Site #%d (%g, %g)" % (self.sitenum,self.x,self.y)
+
+ def __cmp__(self,other):
+ if self.y < other.y:
+ return -1
+ elif self.y > other.y:
+ return 1
+ elif self.x < other.x:
+ return -1
+ elif self.x > other.x:
+ return 1
+ else:
+ return 0
+
+ def distance(self,other):
+ dx = self.x - other.x
+ dy = self.y - other.y
+ return math.sqrt(dx*dx + dy*dy)
+
+#------------------------------------------------------------------
+class Edge(object):
+ LE = 0
+ RE = 1
+ EDGE_NUM = 0
+ DELETED = {} # marker value
+
+ def __init__(self):
+ self.a = 0.0
+ self.b = 0.0
+ self.c = 0.0
+ self.ep = [None,None]
+ self.reg = [None,None]
+ self.edgenum = 0
+
+ def dump(self):
+ print "(#%d a=%g, b=%g, c=%g)" % (self.edgenum,self.a,self.b,self.c)
+ print "ep",self.ep
+ print "reg",self.reg
+
+ def setEndpoint(self, lrFlag, site):
+ self.ep[lrFlag] = site
+ if self.ep[Edge.RE - lrFlag] is None:
+ return False
+ return True
+
+ @staticmethod
+ def bisect(s1,s2):
+ newedge = Edge()
+ newedge.reg[0] = s1 # store the sites that this edge is bisecting
+ newedge.reg[1] = s2
+
+ # to begin with, there are no endpoints on the bisector - it goes to infinity
+ # ep[0] and ep[1] are None
+
+ # get the difference in x dist between the sites
+ dx = float(s2.x - s1.x)
+ dy = float(s2.y - s1.y)
+ adx = abs(dx) # make sure that the difference in positive
+ ady = abs(dy)
+
+ # get the slope of the line
+ newedge.c = float(s1.x * dx + s1.y * dy + (dx*dx + dy*dy)*0.5)
+ if adx > ady :
+ # set formula of line, with x fixed to 1
+ newedge.a = 1.0
+ newedge.b = dy/dx
+ newedge.c /= dx
+ else:
+ # set formula of line, with y fixed to 1
+ newedge.b = 1.0
+ newedge.a = dx/dy
+ newedge.c /= dy
+
+ newedge.edgenum = Edge.EDGE_NUM
+ Edge.EDGE_NUM += 1
+ return newedge
+
+
+#------------------------------------------------------------------
+class Halfedge(object):
+ def __init__(self,edge=None,pm=Edge.LE):
+ self.left = None # left Halfedge in the edge list
+ self.right = None # right Halfedge in the edge list
+ self.qnext = None # priority queue linked list pointer
+ self.edge = edge # edge list Edge
+ self.pm = pm
+ self.vertex = None # Site()
+ self.ystar = BIG_FLOAT
+
+ def dump(self):
+ print "Halfedge--------------------------"
+ print "left: ", self.left
+ print "right: ", self.right
+ print "edge: ", self.edge
+ print "pm: ", self.pm
+ print "vertex: ",
+ if self.vertex: self.vertex.dump()
+ else: print "None"
+ print "ystar: ", self.ystar
+
+
+ def __cmp__(self,other):
+ if self.ystar > other.ystar:
+ return 1
+ elif self.ystar < other.ystar:
+ return -1
+ elif self.vertex.x > other.vertex.x:
+ return 1
+ elif self.vertex.x < other.vertex.x:
+ return -1
+ else:
+ return 0
+
+ def leftreg(self,default):
+ if not self.edge:
+ return default
+ elif self.pm == Edge.LE:
+ return self.edge.reg[Edge.LE]
+ else:
+ return self.edge.reg[Edge.RE]
+
+ def rightreg(self,default):
+ if not self.edge:
+ return default
+ elif self.pm == Edge.LE:
+ return self.edge.reg[Edge.RE]
+ else:
+ return self.edge.reg[Edge.LE]
+
+
+ # returns True if p is to right of halfedge self
+ def isPointRightOf(self,pt):
+ e = self.edge
+ topsite = e.reg[1]
+ right_of_site = pt.x > topsite.x
+
+ if(right_of_site and self.pm == Edge.LE):
+ return True
+
+ if(not right_of_site and self.pm == Edge.RE):
+ return False
+
+ if(e.a == 1.0):
+ dyp = pt.y - topsite.y
+ dxp = pt.x - topsite.x
+ fast = 0;
+ if ((not right_of_site and e.b < 0.0) or (right_of_site and e.b >= 0.0)):
+ above = dyp >= e.b * dxp
+ fast = above
+ else:
+ above = pt.x + pt.y * e.b > e.c
+ if(e.b < 0.0):
+ above = not above
+ if (not above):
+ fast = 1
+ if (not fast):
+ dxs = topsite.x - (e.reg[0]).x
+ above = e.b * (dxp*dxp - dyp*dyp) < dxs*dyp*(1.0+2.0*dxp/dxs + e.b*e.b)
+ if(e.b < 0.0):
+ above = not above
+ else: # e.b == 1.0
+ yl = e.c - e.a * pt.x
+ t1 = pt.y - yl
+ t2 = pt.x - topsite.x
+ t3 = yl - topsite.y
+ above = t1*t1 > t2*t2 + t3*t3
+
+ if(self.pm==Edge.LE):
+ return above
+ else:
+ return not above
+
+ #--------------------------
+ # create a new site where the Halfedges el1 and el2 intersect
+ def intersect(self,other):
+ e1 = self.edge
+ e2 = other.edge
+ if (e1 is None) or (e2 is None):
+ return None
+
+ # if the two edges bisect the same parent return None
+ if e1.reg[1] is e2.reg[1]:
+ return None
+
+ d = e1.a * e2.b - e1.b * e2.a
+ if isEqual(d,0.0):
+ return None
+
+ xint = (e1.c*e2.b - e2.c*e1.b) / d
+ yint = (e2.c*e1.a - e1.c*e2.a) / d
+ if(cmp(e1.reg[1],e2.reg[1]) < 0):
+ he = self
+ e = e1
+ else:
+ he = other
+ e = e2
+
+ rightOfSite = xint >= e.reg[1].x
+ if((rightOfSite and he.pm == Edge.LE) or
+ (not rightOfSite and he.pm == Edge.RE)):
+ return None
+
+ # create a new site at the point of intersection - this is a new
+ # vector event waiting to happen
+ return Site(xint,yint)
+
+
+
+#------------------------------------------------------------------
+class EdgeList(object):
+ def __init__(self,xmin,xmax,nsites):
+ if xmin > xmax: xmin,xmax = xmax,xmin
+ self.hashsize = int(2*math.sqrt(nsites+4))
+
+ self.xmin = xmin
+ self.deltax = float(xmax - xmin)
+ self.hash = [None]*self.hashsize
+
+ self.leftend = Halfedge()
+ self.rightend = Halfedge()
+ self.leftend.right = self.rightend
+ self.rightend.left = self.leftend
+ self.hash[0] = self.leftend
+ self.hash[-1] = self.rightend
+
+ def insert(self,left,he):
+ he.left = left
+ he.right = left.right
+ left.right.left = he
+ left.right = he
+
+ def delete(self,he):
+ he.left.right = he.right
+ he.right.left = he.left
+ he.edge = Edge.DELETED
+
+ # Get entry from hash table, pruning any deleted nodes
+ def gethash(self,b):
+ if(b < 0 or b >= self.hashsize):
+ return None
+ he = self.hash[b]
+ if he is None or he.edge is not Edge.DELETED:
+ return he
+
+ # Hash table points to deleted half edge. Patch as necessary.
+ self.hash[b] = None
+ return None
+
+ def leftbnd(self,pt):
+ # Use hash table to get close to desired halfedge
+ bucket = int(((pt.x - self.xmin)/self.deltax * self.hashsize))
+
+ if(bucket < 0):
+ bucket =0;
+
+ if(bucket >=self.hashsize):
+ bucket = self.hashsize-1
+
+ he = self.gethash(bucket)
+ if(he is None):
+ i = 1
+ while True:
+ he = self.gethash(bucket-i)
+ if (he is not None): break;
+ he = self.gethash(bucket+i)
+ if (he is not None): break;
+ i += 1
+
+ # Now search linear list of halfedges for the corect one
+ if (he is self.leftend) or (he is not self.rightend and he.isPointRightOf(pt)):
+ he = he.right
+ while he is not self.rightend and he.isPointRightOf(pt):
+ he = he.right
+ he = he.left;
+ else:
+ he = he.left
+ while (he is not self.leftend and not he.isPointRightOf(pt)):
+ he = he.left
+
+ # Update hash table and reference counts
+ if(bucket > 0 and bucket < self.hashsize-1):
+ self.hash[bucket] = he
+ return he
+
+
+#------------------------------------------------------------------
+class PriorityQueue(object):
+ def __init__(self,ymin,ymax,nsites):
+ self.ymin = ymin
+ self.deltay = ymax - ymin
+ self.hashsize = int(4 * math.sqrt(nsites))
+ self.count = 0
+ self.minidx = 0
+ self.hash = []
+ for i in range(self.hashsize):
+ self.hash.append(Halfedge())
+
+ def __len__(self):
+ return self.count
+
+ def isEmpty(self):
+ return self.count == 0
+
+ def insert(self,he,site,offset):
+ he.vertex = site
+ he.ystar = site.y + offset
+ last = self.hash[self.getBucket(he)]
+ next = last.qnext
+ while((next is not None) and cmp(he,next) > 0):
+ last = next
+ next = last.qnext
+ he.qnext = last.qnext
+ last.qnext = he
+ self.count += 1
+
+ def delete(self,he):
+ if (he.vertex is not None):
+ last = self.hash[self.getBucket(he)]
+ while last.qnext is not he:
+ last = last.qnext
+ last.qnext = he.qnext
+ self.count -= 1
+ he.vertex = None
+
+ def getBucket(self,he):
+ bucket = int(((he.ystar - self.ymin) / self.deltay) * self.hashsize)
+ if bucket < 0: bucket = 0
+ if bucket >= self.hashsize: bucket = self.hashsize-1
+ if bucket < self.minidx: self.minidx = bucket
+ return bucket
+
+ def getMinPt(self):
+ while(self.hash[self.minidx].qnext is None):
+ self.minidx += 1
+ he = self.hash[self.minidx].qnext
+ x = he.vertex.x
+ y = he.ystar
+ return Site(x,y)
+
+ def popMinHalfedge(self):
+ curr = self.hash[self.minidx].qnext
+ self.hash[self.minidx].qnext = curr.qnext
+ self.count -= 1
+ return curr
+
+
+#------------------------------------------------------------------
+class SiteList(object):
+ def __init__(self,pointList):
+ 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()
+ 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.sort()
+
+ def setSiteNumber(self,site):
+ site.sitenum = self.__sitenum
+ self.__sitenum += 1
+
+ class Iterator(object):
+ def __init__(this,lst): this.generator = (s for s in lst)
+ def __iter__(this): return this
+ def next(this):
+ try:
+ return this.generator.next()
+ except StopIteration:
+ return None
+
+ def iterator(self):
+ return SiteList.Iterator(self.__sites)
+
+ def __iter__(self):
+ return SiteList.Iterator(self.__sites)
+
+ def __len__(self):
+ return len(self.__sites)
+
+ def _getxmin(self): return self.__xmin
+ def _getymin(self): return self.__ymin
+ def _getxmax(self): return self.__xmax
+ def _getymax(self): return self.__ymax
+ xmin = property(_getxmin)
+ ymin = property(_getymin)
+ xmax = property(_getxmax)
+ ymax = property(_getymax)
+
+
+#------------------------------------------------------------------
+def computeVoronoiDiagram( points ):
+ """ Takes a list of point objects (which must have x and y fields).
+ Returns a 3-tuple of:
+
+ (1) a list of 2-tuples, which are the x,y coordinates of the
+ Voronoi diagram vertices
+ (2) a list of 3-tuples (a,b,c) which are the equations of the
+ lines in the Voronoi diagram: a*x + b*y = c
+ (3) a list of 3-tuples, (l, v1, v2) representing edges of the
+ Voronoi diagram. l is the index of the line, v1 and v2 are
+ 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 )
+ context = Context()
+ context.set_bounds( siteList )
+ voronoi( siteList, context )
+ return ( context.vertices, context.lines, context.edges, (context.xmin,context.ymin,context.xmax,context.ymax))
+
+#------------------------------------------------------------------
+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 )
+ context = Context()
+ context.triangulate = True
+ voronoi( siteList, context )
+ return context.triangles
+
+#-----------------------------------------------------------------------------
+if __name__=="__main__":
+ try:
+ optlist,args = getopt.getopt(sys.argv[1:],"thdp")
+ except getopt.GetoptError:
+ usage()
+ sys.exit(2)
+
+ doHelp = 0
+ c = Context()
+ c.doPrint = 1
+ for opt in optlist:
+ if opt[0] == "-d": c.debug = 1
+ if opt[0] == "-p": c.plot = 1
+ if opt[0] == "-t": c.triangulate = 1
+ if opt[0] == "-h": doHelp = 1
+
+ if not doHelp:
+ pts = []
+ fp = sys.stdin
+ if len(args) > 0:
+ fp = open(args[0],'r')
+ for line in fp:
+ fld = line.split()
+ x = float(fld[0])
+ y = float(fld[1])
+ pts.append(Site(x,y))
+ if len(args) > 0: fp.close()
+
+ if doHelp or len(pts) == 0:
+ usage()
+ sys.exit(2)
+
+ sl = SiteList(pts)
+ voronoi(sl,c)
+
More information about the QGIS-commit
mailing list