2010-03-10 00:49:23 +00:00
# -*- coding: utf-8 -*-
2009-01-20 22:54:27 +00:00
#-----------------------------------------------------------
2010-12-21 01:52:42 +00:00
#
2011-03-07 23:29:15 +00:00
# fTools
# Copyright (C) 2008-2011 Carson Farmer
2009-01-20 22:54:27 +00:00
# EMAIL: carson.farmer (at) gmail.com
2011-03-07 23:29:15 +00:00
# WEB : http://www.ftools.ca/fTools.html
#
# A collection of data management and analysis tools for vector data
2009-01-20 22:54:27 +00:00
#
#-----------------------------------------------------------
2010-12-21 01:52:42 +00:00
#
2009-01-20 22:54:27 +00:00
# licensed under the terms of GNU GPL 2
2010-12-21 01:52:42 +00:00
#
2009-01-20 22:54:27 +00:00
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
2010-12-21 01:52:42 +00:00
#
2009-01-20 22:54:27 +00:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
2010-12-21 01:52:42 +00:00
#
2009-01-20 22:54:27 +00:00
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2010-12-21 01:52:42 +00:00
#
2009-01-20 22:54:27 +00:00
#---------------------------------------------------------------------
2009-02-01 15:44:02 +00:00
from PyQt4 . QtCore import *
from PyQt4 . QtGui import *
import ftools_utils
from qgis . core import *
2009-08-16 22:40:48 +00:00
from ui_frmSpatialJoin import Ui_Dialog
2009-02-01 15:44:02 +00:00
2010-12-21 01:52:42 +00:00
def myself ( L ) :
#median computation
nVal = len ( L )
if nVal == 1 :
return L [ 0 ]
L . sort ( )
#test for list length
medianVal = 0
if nVal > 1 :
if ( nVal % 2 ) == 0 :
#index begin at 0
#remove 1 to index in standard median computation
medianVal = 0.5 * ( ( L [ ( nVal ) / 2 - 1 ] ) + ( L [ ( nVal ) / 2 ] ) )
else :
medianVal = L [ ( nVal + 1 ) / 2 - 1 ]
return medianVal
2009-01-20 22:54:27 +00:00
class Dialog ( QDialog , Ui_Dialog ) :
2010-03-10 00:49:23 +00:00
def __init__ ( self , iface ) :
2012-01-26 00:54:56 +01:00
QDialog . __init__ ( self , iface . mainWindow ( ) )
2010-03-10 00:49:23 +00:00
self . iface = iface
# Set up the user interface from Designer.
self . setupUi ( self )
QObject . connect ( self . toolOut , SIGNAL ( " clicked() " ) , self . outFile )
self . setWindowTitle ( self . tr ( " Join attributes by location " ) )
2010-05-13 22:55:59 +00:00
self . buttonOk = self . buttonBox_2 . button ( QDialogButtonBox . Ok )
2010-03-10 00:49:23 +00:00
# populate layer list
self . progressBar . setValue ( 0 )
mapCanvas = self . iface . mapCanvas ( )
layers = ftools_utils . getLayerNames ( [ QGis . Point , QGis . Line , QGis . Polygon ] )
self . inShape . addItems ( layers )
self . joinShape . addItems ( layers )
2010-12-21 01:52:42 +00:00
2010-03-10 00:49:23 +00:00
def accept ( self ) :
2010-05-13 22:55:59 +00:00
self . buttonOk . setEnabled ( False )
2010-03-10 00:49:23 +00:00
if self . inShape . currentText ( ) == " " :
QMessageBox . information ( self , self . tr ( " Spatial Join " ) , self . tr ( " Please specify target vector layer " ) )
elif self . outShape . text ( ) == " " :
QMessageBox . information ( self , self . tr ( " Spatial Join " ) , self . tr ( " Please specify output shapefile " ) )
elif self . joinShape . currentText ( ) == " " :
QMessageBox . information ( self , self . tr ( " Spatial Join " ) , self . tr ( " Please specify join vector layer " ) )
2010-12-21 01:52:42 +00:00
elif self . rdoSummary . isChecked ( ) and not ( self . chkMean . isChecked ( ) or self . chkSum . isChecked ( ) or self . chkMin . isChecked ( ) or self . chkMax . isChecked ( ) or self . chkMean . isChecked ( ) or self . chkMedian . isChecked ( ) ) :
2010-03-10 00:49:23 +00:00
QMessageBox . information ( self , self . tr ( " Spatial Join " ) , self . tr ( " Please specify at least one summary statistic " ) )
else :
inName = self . inShape . currentText ( )
joinName = self . joinShape . currentText ( )
outPath = self . outShape . text ( )
if self . rdoSummary . isChecked ( ) :
summary = True
sumList = [ ]
if self . chkSum . isChecked ( ) : sumList . append ( " SUM " )
if self . chkMean . isChecked ( ) : sumList . append ( " MEAN " )
if self . chkMin . isChecked ( ) : sumList . append ( " MIN " )
if self . chkMax . isChecked ( ) : sumList . append ( " MAX " )
2010-12-21 01:52:42 +00:00
if self . chkMedian . isChecked ( ) : sumList . append ( " MED " )
2010-03-10 00:49:23 +00:00
else :
summary = False
sumList = [ " all " ]
if self . rdoKeep . isChecked ( ) : keep = True
else : keep = False
2013-06-14 19:51:48 +12:00
outName = ftools_utils . getShapefileName ( outPath )
2010-04-26 22:41:28 +00:00
res = self . compute ( inName , joinName , outPath , summary , sumList , keep , self . progressBar )
2010-03-10 00:49:23 +00:00
self . outShape . clear ( )
2010-04-15 00:27:11 +00:00
if res :
addToTOC = QMessageBox . question ( self , self . tr ( " Spatial Join " ) ,
2013-05-31 14:57:34 +04:00
self . tr ( " Created output shapefile: \n %s \n \n Would you like to add the new layer to the TOC? " ) % ( unicode ( outPath ) ) , QMessageBox . Yes , QMessageBox . No , QMessageBox . NoButton )
2010-04-15 00:27:11 +00:00
if addToTOC == QMessageBox . Yes :
2010-03-10 00:49:23 +00:00
self . vlayer = QgsVectorLayer ( outPath , unicode ( outName ) , " ogr " )
2013-01-04 23:02:44 +01:00
QgsMapLayerRegistry . instance ( ) . addMapLayers ( [ self . vlayer ] )
2010-03-10 00:49:23 +00:00
self . progressBar . setValue ( 0 )
2010-05-13 22:55:59 +00:00
self . buttonOk . setEnabled ( True )
2009-01-20 22:54:27 +00:00
2010-03-10 00:49:23 +00:00
def outFile ( self ) :
self . outShape . clear ( )
( self . shapefileName , self . encoding ) = ftools_utils . saveDialog ( self )
if self . shapefileName is None or self . encoding is None :
return
2013-06-03 16:05:08 +04:00
self . outShape . setText ( self . shapefileName )
2009-01-20 22:54:27 +00:00
2010-03-10 00:49:23 +00:00
def compute ( self , inName , joinName , outName , summary , sumList , keep , progressBar ) :
layer1 = ftools_utils . getVectorLayerByName ( inName )
provider1 = layer1 . dataProvider ( )
2013-05-31 14:57:34 +04:00
fieldList1 = ftools_utils . getFieldList ( layer1 )
2009-01-20 22:54:27 +00:00
2010-03-10 00:49:23 +00:00
layer2 = ftools_utils . getVectorLayerByName ( joinName )
provider2 = layer2 . dataProvider ( )
2013-02-01 01:03:19 +01:00
2013-05-31 14:57:34 +04:00
fieldList2 = ftools_utils . getFieldList ( layer2 )
2013-07-03 15:04:26 +09:00
fieldList = QgsFields ( )
2010-12-21 01:52:42 +00:00
if provider1 . crs ( ) != provider2 . crs ( ) :
2010-03-10 00:49:23 +00:00
QMessageBox . warning ( self , self . tr ( " CRS warning! " ) , self . tr ( " Warning: Input layers have non-matching CRS. \n This may cause unexpected results. " ) )
if not summary :
2013-03-13 17:20:27 +04:00
fieldList2 = ftools_utils . testForUniqueness ( fieldList1 , fieldList2 )
2010-03-10 00:49:23 +00:00
seq = range ( 0 , len ( fieldList1 ) + len ( fieldList2 ) )
fieldList1 . extend ( fieldList2 )
fieldList1 = dict ( zip ( seq , fieldList1 ) )
else :
numFields = { }
2013-03-13 17:20:27 +04:00
for j in xrange ( len ( fieldList2 ) ) :
2010-03-10 00:49:23 +00:00
if fieldList2 [ j ] . type ( ) == QVariant . Int or fieldList2 [ j ] . type ( ) == QVariant . Double :
numFields [ j ] = [ ]
for i in sumList :
field = QgsField ( i + unicode ( fieldList2 [ j ] . name ( ) ) , QVariant . Double , " real " , 24 , 16 , self . tr ( " Summary field " ) )
fieldList . append ( field )
field = QgsField ( " COUNT " , QVariant . Double , " real " , 24 , 16 , self . tr ( " Summary field " ) )
fieldList . append ( field )
fieldList2 = ftools_utils . testForUniqueness ( fieldList1 , fieldList )
fieldList1 . extend ( fieldList )
seq = range ( 0 , len ( fieldList1 ) )
fieldList1 = dict ( zip ( seq , fieldList1 ) )
2010-12-21 01:52:42 +00:00
2010-03-10 00:49:23 +00:00
sRs = provider1 . crs ( )
progressBar . setValue ( 13 )
check = QFile ( self . shapefileName )
if check . exists ( ) :
if not QgsVectorFileWriter . deleteShapeFile ( self . shapefileName ) :
2010-04-15 00:27:11 +00:00
QMessageBox . warning ( self , self . tr ( ' Error deleting shapefile ' ) ,
2013-05-31 14:57:34 +04:00
self . tr ( " Can ' t delete existing shapefile \n %s " ) % ( self . shapefileName ) )
2010-04-15 00:27:11 +00:00
return False
2013-03-13 17:20:27 +04:00
fields = QgsFields ( )
for f in fieldList1 . values ( ) :
fields . append ( f )
writer = QgsVectorFileWriter ( self . shapefileName , self . encoding , fields , provider1 . geometryType ( ) , sRs )
2010-03-10 00:49:23 +00:00
#writer = QgsVectorFileWriter(outName, "UTF-8", fieldList1, provider1.geometryType(), sRs)
inFeat = QgsFeature ( )
outFeat = QgsFeature ( )
inFeatB = QgsFeature ( )
inGeom = QgsGeometry ( )
progressBar . setValue ( 15 )
start = 15.00
add = 85.00 / provider1 . featureCount ( )
2013-02-01 01:03:19 +01:00
2010-03-10 00:49:23 +00:00
index = ftools_utils . createIndex ( provider2 )
2013-03-13 17:20:27 +04:00
fit1 = provider1 . getFeatures ( )
2013-02-01 01:03:19 +01:00
while fit1 . nextFeature ( inFeat ) :
2010-03-10 00:49:23 +00:00
inGeom = inFeat . geometry ( )
2013-02-01 01:03:19 +01:00
atMap1 = inFeat . attributes ( )
2010-03-10 00:49:23 +00:00
outFeat . setGeometry ( inGeom )
none = True
joinList = [ ]
if inGeom . type ( ) == QGis . Point :
#(check, joinList) = layer2.featuresInRectangle(inGeom.buffer(10,2).boundingBox(), True, True)
#layer2.select(inGeom.buffer(10,2).boundingBox(), False)
#joinList = layer2.selectedFeatures()
joinList = index . intersects ( inGeom . buffer ( 10 , 2 ) . boundingBox ( ) )
if len ( joinList ) > 0 : check = 0
else : check = 1
else :
#(check, joinList) = layer2.featuresInRectangle(inGeom.boundingBox(), True, True)
#layer2.select(inGeom.boundingBox(), False)
#joinList = layer2.selectedFeatures()
joinList = index . intersects ( inGeom . boundingBox ( ) )
if len ( joinList ) > 0 : check = 0
else : check = 1
if check == 0 :
count = 0
for i in joinList :
#tempGeom = i.geometry()
2013-02-01 01:03:19 +01:00
provider2 . getFeatures ( QgsFeatureRequest ( ) . setFilterFid ( int ( i ) ) ) . nextFeature ( inFeatB )
2013-06-15 10:14:37 +02:00
if inGeom . intersects ( inFeatB . geometry ( ) ) :
2010-03-10 00:49:23 +00:00
count = count + 1
none = False
2013-02-01 01:03:19 +01:00
atMap2 = inFeatB . attributes ( )
2010-03-10 00:49:23 +00:00
if not summary :
2013-03-13 17:20:27 +04:00
atMap = atMap1
atMap2 = atMap2
2010-03-10 00:49:23 +00:00
atMap . extend ( atMap2 )
atMap = dict ( zip ( seq , atMap ) )
break
else :
for j in numFields . keys ( ) :
2013-05-31 14:57:34 +04:00
numFields [ j ] . append ( atMap2 [ j ] )
2010-03-10 00:49:23 +00:00
if summary and not none :
2013-03-13 17:20:27 +04:00
atMap = atMap1
2010-03-10 00:49:23 +00:00
for j in numFields . keys ( ) :
for k in sumList :
2013-06-03 16:05:08 +04:00
if k == " SUM " : atMap . append ( sum ( numFields [ j ] ) )
elif k == " MEAN " : atMap . append ( sum ( numFields [ j ] ) / count )
elif k == " MIN " : atMap . append ( min ( numFields [ j ] ) )
elif k == " MED " : atMap . append ( myself ( numFields [ j ] ) )
else : atMap . append ( max ( numFields [ j ] ) )
2010-03-10 00:49:23 +00:00
numFields [ j ] = [ ]
2013-06-03 16:05:08 +04:00
atMap . append ( count )
2010-03-10 00:49:23 +00:00
atMap = dict ( zip ( seq , atMap ) )
if none :
2013-02-01 01:03:19 +01:00
outFeat . setAttributes ( atMap1 )
2010-03-10 00:49:23 +00:00
else :
2013-03-13 17:20:27 +04:00
outFeat . setAttributes ( atMap . values ( ) )
2010-03-10 00:49:23 +00:00
if keep : # keep all records
writer . addFeature ( outFeat )
else : # keep only matching records
if not none :
writer . addFeature ( outFeat )
start = start + add
progressBar . setValue ( start )
del writer
2010-04-15 00:27:11 +00:00
return True