mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-18 00:03:05 -04:00
Updates distance matrix tool to allow non ascii characters in id fields. This is based on a workaround (from http://docs.python.org/library/csv.html), as the default Python csv function(s) do not support nonascii characters. Fixes #2496
git-svn-id: http://svn.osgeo.org/qgis/trunk@13019 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
parent
e41bb3466b
commit
ebc2e6f72a
@ -1,4 +1,5 @@
|
||||
#-----------------------------------------------------------
|
||||
# -*- coding: utf-8 -*-
|
||||
#-----------------------------------------------------------
|
||||
#
|
||||
# Create Point Distance Matrix
|
||||
#
|
||||
@ -37,198 +38,233 @@ from PyQt4.QtGui import *
|
||||
|
||||
from qgis.core import *
|
||||
from ui_frmPointDistance import Ui_Dialog
|
||||
import csv
|
||||
import csv, codecs, cStringIO
|
||||
from math import *
|
||||
|
||||
class UnicodeWriter:
|
||||
"""
|
||||
A CSV writer which will write rows to CSV file "f",
|
||||
which is encoded in the given encoding.
|
||||
Taken from http://docs.python.org/library/csv.html
|
||||
to allow handling of nonascii output
|
||||
"""
|
||||
|
||||
def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
|
||||
# Redirect output to a queue
|
||||
self.queue = cStringIO.StringIO()
|
||||
self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
|
||||
self.stream = f
|
||||
self.encoder = codecs.getincrementalencoder(encoding)()
|
||||
|
||||
def writerow(self, row):
|
||||
try:
|
||||
self.writer.writerow([s.encode("utf-8") for s in row])
|
||||
except:
|
||||
self.writer.writerow(row)
|
||||
# Fetch UTF-8 output from the queue ...
|
||||
data = self.queue.getvalue()
|
||||
data = data.decode("utf-8")
|
||||
# ... and reencode it into the target encoding
|
||||
data = self.encoder.encode(data)
|
||||
# write to the target stream
|
||||
self.stream.write(data)
|
||||
# empty queue
|
||||
self.queue.truncate(0)
|
||||
|
||||
def writerows(self, rows):
|
||||
for row in rows:
|
||||
self.writerow(row)
|
||||
|
||||
class Dialog(QDialog, Ui_Dialog):
|
||||
def __init__(self, iface):
|
||||
QDialog.__init__(self)
|
||||
self.iface = iface
|
||||
# Set up the user interface from Designer.
|
||||
self.setupUi(self)
|
||||
QObject.connect(self.btnFile, SIGNAL("clicked()"), self.saveFile)
|
||||
QObject.connect(self.inPoint1, SIGNAL("currentIndexChanged(QString)"), self.update1)
|
||||
QObject.connect(self.inPoint2, SIGNAL("currentIndexChanged(QString)"), self.update2)
|
||||
# populate layer list
|
||||
self.setWindowTitle(self.tr("Distance matrix"))
|
||||
self.progressBar.setValue(0)
|
||||
mapCanvas = self.iface.mapCanvas()
|
||||
for i in range(mapCanvas.layerCount()):
|
||||
layer = mapCanvas.layer(i)
|
||||
if layer.type() == layer.VectorLayer:
|
||||
if layer.geometryType() == QGis.Point:
|
||||
self.inPoint1.addItem(layer.name())
|
||||
self.inPoint2.addItem(layer.name())
|
||||
def __init__(self, iface):
|
||||
QDialog.__init__(self)
|
||||
self.iface = iface
|
||||
# Set up the user interface from Designer.
|
||||
self.setupUi(self)
|
||||
QObject.connect(self.btnFile, SIGNAL("clicked()"), self.saveFile)
|
||||
QObject.connect(self.inPoint1, SIGNAL("currentIndexChanged(QString)"), self.update1)
|
||||
QObject.connect(self.inPoint2, SIGNAL("currentIndexChanged(QString)"), self.update2)
|
||||
# populate layer list
|
||||
self.setWindowTitle(self.tr("Distance matrix"))
|
||||
self.progressBar.setValue(0)
|
||||
mapCanvas = self.iface.mapCanvas()
|
||||
for i in range(mapCanvas.layerCount()):
|
||||
layer = mapCanvas.layer(i)
|
||||
if layer.type() == layer.VectorLayer:
|
||||
if layer.geometryType() == QGis.Point:
|
||||
self.inPoint1.addItem(layer.name())
|
||||
self.inPoint2.addItem(layer.name())
|
||||
|
||||
def update1(self, inputLayer):
|
||||
changedLayer = self.getVectorLayerByName(unicode(inputLayer))
|
||||
changedField = self.getFieldList(changedLayer)
|
||||
for i in changedField:
|
||||
if changedField[i].type() == QVariant.Int or changedField[i].type() == QVariant.String:
|
||||
self.inField1.addItem(unicode(changedField[i].name()))
|
||||
def update1(self, inputLayer):
|
||||
changedLayer = self.getVectorLayerByName(unicode(inputLayer))
|
||||
changedField = self.getFieldList(changedLayer)
|
||||
for i in changedField:
|
||||
if changedField[i].type() == QVariant.Int or changedField[i].type() == QVariant.String:
|
||||
self.inField1.addItem(unicode(changedField[i].name()))
|
||||
|
||||
def update2(self, inputLayer):
|
||||
changedLayer = self.getVectorLayerByName(unicode(inputLayer))
|
||||
changedField = self.getFieldList(changedLayer)
|
||||
for i in changedField:
|
||||
if changedField[i].type() == QVariant.Int or changedField[i].type() == QVariant.String:
|
||||
self.inField2.addItem(unicode(changedField[i].name()))
|
||||
def update2(self, inputLayer):
|
||||
changedLayer = self.getVectorLayerByName(unicode(inputLayer))
|
||||
changedField = self.getFieldList(changedLayer)
|
||||
for i in changedField:
|
||||
if changedField[i].type() == QVariant.Int or changedField[i].type() == QVariant.String:
|
||||
self.inField2.addItem(unicode(changedField[i].name()))
|
||||
|
||||
def accept(self):
|
||||
if self.inPoint1.currentText() == "":
|
||||
QMessageBox.information(self, self.tr("Create Point Distance Matrix"), self.tr("Please specify input point layer"))
|
||||
elif self.outFile.text() == "":
|
||||
QMessageBox.information(self, self.tr("Create Point Distance Matrix"), self.tr("Please specify output file"))
|
||||
elif self.inPoint2.currentText() == "":
|
||||
QMessageBox.information(self, self.tr("Create Point Distance Matrix"), self.tr("Please specify target point layer"))
|
||||
elif self.inField1.currentText() == "":
|
||||
QMessageBox.information(self, self.tr("Create Point Distance Matrix"), self.tr("Please specify input unique ID field"))
|
||||
elif self.inField2.currentText() == "":
|
||||
QMessageBox.information(self, self.tr("Create Point Distance Matrix"), self.tr("Please specify target unique ID field"))
|
||||
else:
|
||||
point1 = self.inPoint1.currentText()
|
||||
point2 = self.inPoint2.currentText()
|
||||
field1 = self.inField1.currentText()
|
||||
field2 = self.inField2.currentText()
|
||||
outPath = self.outFile.text()
|
||||
if self.rdoLinear.isChecked(): matType = "Linear"
|
||||
elif self.rdoStandard.isChecked(): matType = "Standard"
|
||||
else: matType = "Summary"
|
||||
if self.chkNearest.isChecked(): nearest = self.spnNearest.value()
|
||||
else: nearest = nearest = 0
|
||||
if outPath.contains("\\"):
|
||||
outName = outPath.right((outPath.length() - outPath.lastIndexOf("\\")) - 1)
|
||||
else:
|
||||
outName = outPath.right((outPath.length() - outPath.lastIndexOf("/")) - 1)
|
||||
if outName.endsWith(".csv"):
|
||||
outName = outName.left(outName.length() - 4)
|
||||
self.outFile.clear()
|
||||
self.compute(point1, point2, field1, field2, outPath, matType, nearest, self.progressBar)
|
||||
self.progressBar.setValue(100)
|
||||
addToTOC = QMessageBox.information(self, "Create Point Distance Matrix", self.tr("Created output matrix:\n") + outPath)
|
||||
self.progressBar.setValue(0)
|
||||
def accept(self):
|
||||
if self.inPoint1.currentText() == "":
|
||||
QMessageBox.information(self, self.tr("Create Point Distance Matrix"), self.tr("Please specify input point layer"))
|
||||
elif self.outFile.text() == "":
|
||||
QMessageBox.information(self, self.tr("Create Point Distance Matrix"), self.tr("Please specify output file"))
|
||||
elif self.inPoint2.currentText() == "":
|
||||
QMessageBox.information(self, self.tr("Create Point Distance Matrix"), self.tr("Please specify target point layer"))
|
||||
elif self.inField1.currentText() == "":
|
||||
QMessageBox.information(self, self.tr("Create Point Distance Matrix"), self.tr("Please specify input unique ID field"))
|
||||
elif self.inField2.currentText() == "":
|
||||
QMessageBox.information(self, self.tr("Create Point Distance Matrix"), self.tr("Please specify target unique ID field"))
|
||||
else:
|
||||
point1 = self.inPoint1.currentText()
|
||||
point2 = self.inPoint2.currentText()
|
||||
field1 = self.inField1.currentText()
|
||||
field2 = self.inField2.currentText()
|
||||
outPath = self.outFile.text()
|
||||
if self.rdoLinear.isChecked(): matType = "Linear"
|
||||
elif self.rdoStandard.isChecked(): matType = "Standard"
|
||||
else: matType = "Summary"
|
||||
if self.chkNearest.isChecked(): nearest = self.spnNearest.value()
|
||||
else: nearest = nearest = 0
|
||||
if outPath.contains("\\"):
|
||||
outName = outPath.right((outPath.length() - outPath.lastIndexOf("\\")) - 1)
|
||||
else:
|
||||
outName = outPath.right((outPath.length() - outPath.lastIndexOf("/")) - 1)
|
||||
if outName.endsWith(".csv"):
|
||||
outName = outName.left(outName.length() - 4)
|
||||
self.outFile.clear()
|
||||
self.compute(point1, point2, field1, field2, outPath, matType, nearest, self.progressBar)
|
||||
self.progressBar.setValue(100)
|
||||
addToTOC = QMessageBox.information(self, "Create Point Distance Matrix", self.tr("Created output matrix:\n") + outPath)
|
||||
self.progressBar.setValue(0)
|
||||
|
||||
def saveFile(self):
|
||||
self.outFile.clear()
|
||||
fileDialog = QFileDialog()
|
||||
outName = fileDialog.getSaveFileName(self, "Output Distance Matrix",".", "Delimited txt file (*.csv)")
|
||||
fileCheck = QFile(outName)
|
||||
filePath = QFileInfo(outName).absoluteFilePath()
|
||||
if filePath.right(4) != ".csv": filePath = filePath + ".csv"
|
||||
if not outName.isEmpty():
|
||||
self.outFile.insert(filePath)
|
||||
def saveFile(self):
|
||||
self.outFile.clear()
|
||||
fileDialog = QFileDialog()
|
||||
outName = fileDialog.getSaveFileName(self, "Output Distance Matrix",".", "Delimited txt file (*.csv)")
|
||||
fileCheck = QFile(outName)
|
||||
filePath = QFileInfo(outName).absoluteFilePath()
|
||||
if filePath.right(4) != ".csv": filePath = filePath + ".csv"
|
||||
if not outName.isEmpty():
|
||||
self.outFile.insert(filePath)
|
||||
|
||||
def compute(self, line1, line2, field1, field2, outPath, matType, nearest, progressBar):
|
||||
layer1 = self.getVectorLayerByName(line1)
|
||||
layer2 = self.getVectorLayerByName(line2)
|
||||
provider1 = layer1.dataProvider()
|
||||
provider2 = layer2.dataProvider()
|
||||
allAttrs = provider1.attributeIndexes()
|
||||
provider1.select(allAttrs)
|
||||
allAttrs = provider2.attributeIndexes()
|
||||
provider2.select(allAttrs)
|
||||
sindex = QgsSpatialIndex()
|
||||
inFeat = QgsFeature()
|
||||
while provider2.nextFeature(inFeat):
|
||||
sindex.insertFeature(inFeat)
|
||||
provider2.rewind()
|
||||
if nearest < 1: nearest = layer2.featureCount()
|
||||
else: nearest = nearest + 1
|
||||
index1 = provider1.fieldNameIndex(field1)
|
||||
index2 = provider2.fieldNameIndex(field2)
|
||||
sRs = provider1.crs()
|
||||
distArea = QgsDistanceArea()
|
||||
#use srs of the first layer (users should ensure that they are both in the same projection)
|
||||
#distArea.setSourceSRS(sRs)
|
||||
def compute(self, line1, line2, field1, field2, outPath, matType, nearest, progressBar):
|
||||
layer1 = self.getVectorLayerByName(line1)
|
||||
layer2 = self.getVectorLayerByName(line2)
|
||||
provider1 = layer1.dataProvider()
|
||||
provider2 = layer2.dataProvider()
|
||||
allAttrs = provider1.attributeIndexes()
|
||||
provider1.select(allAttrs)
|
||||
allAttrs = provider2.attributeIndexes()
|
||||
provider2.select(allAttrs)
|
||||
sindex = QgsSpatialIndex()
|
||||
inFeat = QgsFeature()
|
||||
while provider2.nextFeature(inFeat):
|
||||
sindex.insertFeature(inFeat)
|
||||
provider2.rewind()
|
||||
if nearest < 1: nearest = layer2.featureCount()
|
||||
else: nearest = nearest + 1
|
||||
index1 = provider1.fieldNameIndex(field1)
|
||||
index2 = provider2.fieldNameIndex(field2)
|
||||
sRs = provider1.crs()
|
||||
distArea = QgsDistanceArea()
|
||||
#use srs of the first layer (users should ensure that they are both in the same projection)
|
||||
#distArea.setSourceSRS(sRs)
|
||||
|
||||
f = open(unicode(outPath), "wb")
|
||||
writer = csv.writer(f)
|
||||
if matType <> "Standard":
|
||||
if matType == "Linear":
|
||||
writer.writerow(["InputID", "TargetID", "Distance"])
|
||||
else:
|
||||
writer.writerow(["InputID", "MEAN", "STDDEV", "MIN", "MAX"])
|
||||
self.linearMatrix(writer, provider1, provider2, index1, index2, nearest, distArea, matType, sindex, progressBar)
|
||||
else:
|
||||
self.regularMatrix(writer, provider1, provider2, index1, index2, nearest, distArea, sindex, progressBar)
|
||||
f.close()
|
||||
f = open(unicode(outPath), "wb")
|
||||
writer = UnicodeWriter(f)
|
||||
if matType <> "Standard":
|
||||
if matType == "Linear":
|
||||
writer.writerow(["InputID", "TargetID", "Distance"])
|
||||
else:
|
||||
writer.writerow(["InputID", "MEAN", "STDDEV", "MIN", "MAX"])
|
||||
self.linearMatrix(writer, provider1, provider2, index1, index2, nearest, distArea, matType, sindex, progressBar)
|
||||
else:
|
||||
self.regularMatrix(writer, provider1, provider2, index1, index2, nearest, distArea, sindex, progressBar)
|
||||
f.close()
|
||||
|
||||
def regularMatrix(self, writer, provider1, provider2, index1, index2, nearest, distArea, sindex, progressBar):
|
||||
inFeat = QgsFeature()
|
||||
outFeat = QgsFeature()
|
||||
inGeom = QgsGeometry()
|
||||
outGeom = QgsGeometry()
|
||||
first = True
|
||||
start = 15.00
|
||||
add = 85.00 / provider1.featureCount()
|
||||
while provider1.nextFeature(inFeat):
|
||||
inGeom = inFeat.geometry()
|
||||
inID = inFeat.attributeMap()[index1].toString()
|
||||
if first:
|
||||
featList = sindex.nearestNeighbor(inGeom.asPoint(), nearest)
|
||||
first = False
|
||||
data = ["ID"]
|
||||
for i in featList:
|
||||
provider2.featureAtId(int(i), outFeat, True, [index2])
|
||||
data.append(unicode(outFeat.attributeMap()[index2].toString()))
|
||||
writer.writerow(data)
|
||||
data = [unicode(inID)]
|
||||
for j in featList:
|
||||
provider2.featureAtId(int(j), outFeat, True)
|
||||
outGeom = outFeat.geometry()
|
||||
dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint())
|
||||
data.append(float(dist))
|
||||
writer.writerow(data)
|
||||
start = start + add
|
||||
progressBar.setValue(start)
|
||||
del writer
|
||||
def regularMatrix(self, writer, provider1, provider2, index1, index2, nearest, distArea, sindex, progressBar):
|
||||
inFeat = QgsFeature()
|
||||
outFeat = QgsFeature()
|
||||
inGeom = QgsGeometry()
|
||||
outGeom = QgsGeometry()
|
||||
first = True
|
||||
start = 15.00
|
||||
add = 85.00 / provider1.featureCount()
|
||||
while provider1.nextFeature(inFeat):
|
||||
inGeom = inFeat.geometry()
|
||||
inID = inFeat.attributeMap()[index1].toString()
|
||||
if first:
|
||||
featList = sindex.nearestNeighbor(inGeom.asPoint(), nearest)
|
||||
first = False
|
||||
data = ["ID"]
|
||||
for i in featList:
|
||||
provider2.featureAtId(int(i), outFeat, True, [index2])
|
||||
data.append(unicode(outFeat.attributeMap()[index2].toString()))
|
||||
writer.writerow(data)
|
||||
data = [unicode(inID)]
|
||||
for j in featList:
|
||||
provider2.featureAtId(int(j), outFeat, True)
|
||||
outGeom = outFeat.geometry()
|
||||
dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint())
|
||||
data.append(str(float(dist)))
|
||||
writer.writerow(data)
|
||||
start = start + add
|
||||
progressBar.setValue(start)
|
||||
del writer
|
||||
|
||||
def linearMatrix(self, writer, provider1, provider2, index1, index2, nearest, distArea, matType, sindex, progressBar):
|
||||
inFeat = QgsFeature()
|
||||
outFeat = QgsFeature()
|
||||
inGeom = QgsGeometry()
|
||||
outGeom = QgsGeometry()
|
||||
start = 15.00
|
||||
add = 85.00 / provider1.featureCount()
|
||||
while provider1.nextFeature(inFeat):
|
||||
inGeom = inFeat.geometry()
|
||||
inID = inFeat.attributeMap()[index1].toString()
|
||||
featList = sindex.nearestNeighbor(inGeom.asPoint(), nearest)
|
||||
distList = []
|
||||
vari = 0.00
|
||||
for i in featList:
|
||||
provider2.featureAtId(int(i), outFeat, True, [index2])
|
||||
outID = outFeat.attributeMap()[index2].toString()
|
||||
outGeom = outFeat.geometry()
|
||||
dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint())
|
||||
if dist > 0:
|
||||
if matType == "Linear": writer.writerow([unicode(inID), unicode(outID), float(dist)])
|
||||
else: distList.append(float(dist))
|
||||
if matType == "Summary":
|
||||
mean = sum(distList) / len(distList)
|
||||
for i in distList:
|
||||
vari = vari + ((i - mean)*(i - mean))
|
||||
vari = sqrt(vari / len(distList))
|
||||
writer.writerow([unicode(inID), float(mean), float(vari), float(min(distList)), float(max(distList))])
|
||||
start = start + add
|
||||
progressBar.setValue(start)
|
||||
del writer
|
||||
def linearMatrix(self, writer, provider1, provider2, index1, index2, nearest, distArea, matType, sindex, progressBar):
|
||||
inFeat = QgsFeature()
|
||||
outFeat = QgsFeature()
|
||||
inGeom = QgsGeometry()
|
||||
outGeom = QgsGeometry()
|
||||
start = 15.00
|
||||
add = 85.00 / provider1.featureCount()
|
||||
while provider1.nextFeature(inFeat):
|
||||
inGeom = inFeat.geometry()
|
||||
inID = inFeat.attributeMap()[index1].toString()
|
||||
featList = sindex.nearestNeighbor(inGeom.asPoint(), nearest)
|
||||
distList = []
|
||||
vari = 0.00
|
||||
for i in featList:
|
||||
provider2.featureAtId(int(i), outFeat, True, [index2])
|
||||
outID = outFeat.attributeMap()[index2].toString()
|
||||
outGeom = outFeat.geometry()
|
||||
dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint())
|
||||
if dist > 0:
|
||||
if matType == "Linear": writer.writerow([unicode(inID), unicode(outID), float(dist)])
|
||||
else: distList.append(float(dist))
|
||||
if matType == "Summary":
|
||||
mean = sum(distList) / len(distList)
|
||||
for i in distList:
|
||||
vari = vari + ((i - mean)*(i - mean))
|
||||
vari = sqrt(vari / len(distList))
|
||||
writer.writerow([unicode(inID), float(mean), float(vari), float(min(distList)), float(max(distList))])
|
||||
start = start + add
|
||||
progressBar.setValue(start)
|
||||
del writer
|
||||
|
||||
def getVectorLayerByName(self, myName):
|
||||
mc = self.iface.mapCanvas()
|
||||
nLayers = mc.layerCount()
|
||||
for l in range(nLayers):
|
||||
layer = mc.layer(l)
|
||||
if layer.name() == unicode(myName):
|
||||
vlayer = QgsVectorLayer(unicode(layer.source()), unicode(myName), unicode(layer.dataProvider().name()))
|
||||
if vlayer.isValid():
|
||||
return vlayer
|
||||
else:
|
||||
QMessageBox.information(self, self.tr("Locate Line Intersections"), self.tr("Vector layer is not valid"))
|
||||
def getVectorLayerByName(self, myName):
|
||||
mc = self.iface.mapCanvas()
|
||||
nLayers = mc.layerCount()
|
||||
for l in range(nLayers):
|
||||
layer = mc.layer(l)
|
||||
if layer.name() == unicode(myName):
|
||||
vlayer = QgsVectorLayer(unicode(layer.source()), unicode(myName), unicode(layer.dataProvider().name()))
|
||||
if vlayer.isValid():
|
||||
return vlayer
|
||||
else:
|
||||
QMessageBox.information(self, self.tr("Locate Line Intersections"), self.tr("Vector layer is not valid"))
|
||||
|
||||
def getFieldList(self, vlayer):
|
||||
fProvider = vlayer.dataProvider()
|
||||
feat = QgsFeature()
|
||||
allAttrs = fProvider.attributeIndexes()
|
||||
fProvider.select(allAttrs)
|
||||
myFields = fProvider.fields()
|
||||
return myFields
|
||||
def getFieldList(self, vlayer):
|
||||
fProvider = vlayer.dataProvider()
|
||||
feat = QgsFeature()
|
||||
allAttrs = fProvider.attributeIndexes()
|
||||
fProvider.select(allAttrs)
|
||||
myFields = fProvider.fields()
|
||||
return myFields
|
||||
|
Loading…
x
Reference in New Issue
Block a user