Skip to content

Commit 75afa7e

Browse files
author
cfarmer
committed
Join attributes tool now supports both dbf and csv files. Uses OGR provider to read tabular information (a bit of a hack, but it seems to work).
Should be slightly faster, and more robust to different file encodings and formats... git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@13039 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent c95fda1 commit 75afa7e

File tree

1 file changed

+24
-88
lines changed

1 file changed

+24
-88
lines changed

python/plugins/fTools/tools/doJoinAttributes.py

+24-88
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,10 @@ def inFile(self):
120120
self.outShape.clear()
121121
fileDialog = QFileDialog()
122122
fileDialog.setConfirmOverwrite(False)
123-
outName = fileDialog.getOpenFileName(self, self.tr("Join Table"), ".", "DBase Files (*.dbf)")
123+
outName = fileDialog.getOpenFileName(self, self.tr("Join Table"), ".", "Tables (*.dbf *.csv)")
124124
fileCheck = QFile(outName)
125125
if fileCheck.exists():
126126
filePath = QFileInfo(outName).absoluteFilePath()
127-
if filePath.right(4).toLower() != ".dbf": filePath = filePath + ".dbf"
128127
if not outName.isEmpty():
129128
self.inTable.clear()
130129
self.inTable.insert(filePath)
@@ -135,13 +134,18 @@ def inFile(self):
135134
def updateTableFields(self):
136135
if self.inTable.text() != "":
137136
filePath = self.inTable.text()
138-
f = open(unicode(filePath), 'rb')
139-
table = list(self.dbfreader(f))
140-
f.close()
137+
joinInfo = QFileInfo(filePath)
138+
joinPath = joinInfo.absoluteFilePath()
139+
joinName = joinInfo.completeBaseName()
141140
self.joinField.clear()
142-
for i in table[0]:
143-
self.joinField.addItem(unicode(i))
144-
table = None
141+
changedLayer = QgsVectorLayer(joinPath, joinName, 'ogr')
142+
try:
143+
changedField = ftools_utils.getFieldList(changedLayer)
144+
except:
145+
QMessageBox.warning(self, self.tr("Join Attributes"), self.tr("Unable to read input table!"))
146+
return
147+
for i in changedField:
148+
self.joinField.addItem(unicode(changedField[i].name()))
145149

146150
def compute(self, inName, inField, joinName, joinField, outName, keep, useTable, progressBar):
147151
layer1 = ftools_utils.getVectorLayerByName(inName)
@@ -151,21 +155,18 @@ def compute(self, inName, inField, joinName, joinField, outName, keep, useTable,
151155
fieldList1 = ftools_utils.getFieldList(layer1).values()
152156
index1 = provider1.fieldNameIndex(inField)
153157
if useTable:
154-
f = open(unicode(joinName), 'rb')
155-
table = list(self.dbfreader(f))
156-
f.close()
157-
(fieldList2, index2) = self.createFieldList(table, joinField)
158-
table = table[2:]
159-
func = lambda x: (unicode(type(x)) != "<type 'str'>" and QVariant(float(x))) or (QVariant(x))
160-
table = map(lambda f: map(func, f), table)
161-
158+
joinInfo = QFileInfo(joinName)
159+
joinPath = joinInfo.absoluteFilePath()
160+
joinName = joinInfo.completeBaseName()
161+
layer2 = QgsVectorLayer(joinPath, joinName, 'ogr')
162+
useTable = False
162163
else:
163164
layer2 = ftools_utils.getVectorLayerByName(joinName)
164-
provider2 = layer2.dataProvider()
165-
allAttrs = provider2.attributeIndexes()
166-
provider2.select(allAttrs)
167-
fieldList2 = ftools_utils.getFieldList(layer2)
168-
index2 = provider2.fieldNameIndex(joinField)
165+
provider2 = layer2.dataProvider()
166+
allAttrs = provider2.attributeIndexes()
167+
provider2.select(allAttrs, QgsRectangle(), False, False)
168+
fieldList2 = ftools_utils.getFieldList(layer2)
169+
index2 = provider2.fieldNameIndex(joinField)
169170
fieldList2 = self.testForUniqueness(fieldList1, fieldList2.values())
170171
seq = range(0, len(fieldList1) + len(fieldList2))
171172
fieldList1.extend(fieldList2)
@@ -192,21 +193,8 @@ def compute(self, inName, inField, joinName, joinField, outName, keep, useTable,
192193
outFeat.setAttributeMap(atMap1)
193194
outFeat.setGeometry(inGeom)
194195
none = True
195-
if useTable:
196-
for i in table:
197-
#sequence = range(0, len(table[0]))
198-
#atMap2 = dict(zip(sequence, i))
199-
if atMap1[index1].toString().trimmed() == i[index2].toString().trimmed():
200-
count = count + 1
201-
none = False
202-
atMap = atMap1.values()
203-
atMap2 = i
204-
atMap.extend(atMap2)
205-
atMap = dict(zip(seq, atMap))
206-
break
207-
else:
208-
provider2.rewind()
209-
while provider2.nextFeature(joinFeat):
196+
provider2.select(allAttrs, QgsRectangle(), False, False)
197+
while provider2.nextFeature(joinFeat):
210198
atMap2 = joinFeat.attributeMap()
211199
if atMap1[index1] == atMap2[index2]:
212200
none = False
@@ -259,55 +247,3 @@ def testForUniqueness(self, fieldList1, fieldList2):
259247
j = ftools_utils.createUniqueFieldName(j)
260248
changed = True
261249
return fieldList2
262-
263-
def dbfreader(self, f):
264-
"""Returns an iterator over records in a Xbase DBF file.
265-
266-
The first row returned contains the field names.
267-
The second row contains field specs: (type, size, decimal places).
268-
Subsequent rows contain the data records.
269-
If a record is marked as deleted, it is skipped.
270-
271-
File should be opened for binary reads.
272-
273-
"""
274-
numrec, lenheader = struct.unpack('<xxxxLH22x', f.read(32))
275-
numfields = (lenheader - 33) // 32
276-
277-
fields = []
278-
for fieldno in xrange(numfields):
279-
name, typ, size, deci = struct.unpack('<11sc4xBB14x', f.read(32))
280-
name = name.replace('\0', '') # eliminate NULs from string
281-
fields.append((name, typ, size, deci))
282-
yield [field[0] for field in fields]
283-
yield [tuple(field[1:]) for field in fields]
284-
285-
terminator = f.read(1)
286-
assert terminator == '\r'
287-
288-
fields.insert(0, ('DeletionFlag', 'C', 1, 0))
289-
fmt = ''.join(['%ds' % fieldinfo[2] for fieldinfo in fields])
290-
fmtsiz = struct.calcsize(fmt)
291-
for i in xrange(numrec):
292-
record = struct.unpack(fmt, f.read(fmtsiz))
293-
if record[0] != ' ':
294-
continue # deleted record
295-
result = []
296-
for (name, typ, size, deci), value in itertools.izip(fields, record):
297-
if name == 'DeletionFlag':
298-
continue
299-
if typ == "N":
300-
value = value.replace('\0', '').lstrip()
301-
if value == '':
302-
value = 0
303-
elif deci:
304-
value = decimal.Decimal(value)
305-
else:
306-
value = int(value)
307-
elif typ == 'D':
308-
y, m, d = int(value[:4]), int(value[4:6]), int(value[6:8])
309-
value = datetime.date(y, m, d)
310-
elif typ == 'L':
311-
value = (value in 'YyTt' and 'T') or (value in 'NnFf' and 'F') or '?'
312-
result.append(value)
313-
yield result

0 commit comments

Comments
 (0)