-
Notifications
You must be signed in to change notification settings - Fork 15
/
vectorbender.py
276 lines (218 loc) · 10.7 KB
/
vectorbender.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# -*- coding: utf-8 -*-
"""
/***************************************************************************
VectorBender
A QGIS plugin
Deforms vector to adapt them despite heavy and irregular deformations
-------------------
begin : 2014-05-21
copyright : (C) 2014 by Olivier Dalang
email : olivier.dalang@gmail.com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
"""
# Import the PyQt and QGIS libraries
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtWidgets import *
from qgis.PyQt.QtGui import *
from qgis.core import *
from qgis.gui import *
# Basic dependencies
import os.path
import sys
import math
# More tricky dependencies
from distutils.version import StrictVersion
dependenciesStatus = 2 # 2: ok, 1: too old, 0: missing
try:
import matplotlib.tri
minVersion = '1.3.0'
if StrictVersion(matplotlib.__version__) < StrictVersion(minVersion):
dependenciesStatus=1
QgsMessageLog.logMessage("Matplotlib version too old (%s instead of %s). You won't be able to use the bending algorithm" % (matplotlib.__version__,minVersion), 'VectorBender')
except Exception:
QgsMessageLog.logMessage("Matplotlib is missing. You won't be able to use the bending algorithm", 'VectorBender')
dependenciesStatus = 0
# Other classes
from .vectorbendertransformers import *
from .vectorbenderdialog import VectorBenderDialog
from .vectorbenderhelp import VectorBenderHelp
class VectorBender:
def __init__(self, iface):
self.iface = iface
self.dlg = VectorBenderDialog(iface,self)
self.ptsA = []
self.ptsB = []
self.transformer = None
self.aboutWindow = None
def initGui(self):
self.action = QAction( QIcon(os.path.join(os.path.dirname(__file__),'resources','icon.png')), "Vector Bender", self.iface.mainWindow())
self.action.triggered.connect(self.showUi)
self.iface.addToolBarIcon(self.action)
self.iface.addPluginToMenu(u"&Vector Bender", self.action)
self.helpAction = QAction( QIcon(os.path.join(os.path.dirname(__file__),'resources','about.png')), "Vector Bender Help", self.iface.mainWindow())
self.helpAction.triggered.connect(self.showHelp)
self.iface.addPluginToMenu(u"&Vector Bender", self.helpAction)
def showHelp(self):
if self.aboutWindow is None:
self.aboutWindow = VectorBenderHelp()
self.aboutWindow.show()
self.aboutWindow.raise_()
def unload(self):
if self.dlg is not None:
self.dlg.close()
self.dlg = None
if self.aboutWindow is not None:
self.aboutWindow.close()
self.aboutWindow = None
self.iface.removePluginMenu(u"&Vector Bender", self.action)
self.iface.removePluginMenu(u"&Vector Bender", self.helpAction)
self.iface.removeToolBarIcon(self.action)
def showUi(self):
self.dlg.show()
self.dlg.raise_()
self.dlg.refreshStates()
def determineTransformationType(self):
"""Returns :
0 if no pairs Found
1 if one pair found => translation
2 if two pairs found => linear
3 if three pairs found => affine
4 if four or more pairs found => bending"""
pairsLayer = self.dlg.pairsLayer()
if pairsLayer is None:
return 0
featuresCount = len(pairsLayer.selectedFeatureIds()) if self.dlg.restrictBox_pairsLayer.isChecked() else len(pairsLayer.allFeatureIds())
if featuresCount == 1:
return 1
elif featuresCount == 2:
return 2
elif featuresCount == 3:
return 3
elif featuresCount >= 4:
if dependenciesStatus != 2:
return 5
else:
return 4
return 0
def run(self):
self.dlg.progressBar.setValue( 0 )
toBendLayer = self.dlg.toBendLayer()
pairsLayer = self.dlg.pairsLayer()
transType = self.determineTransformationType()
# Loading the delaunay
restrictToSelection = self.dlg.restrictBox_pairsLayer.isChecked()
if transType==4:
self.dlg.displayMsg( "Loading delaunay mesh (%i points) ..." % len(self.ptsA) )
QCoreApplication.processEvents()
self.transformer = BendTransformer( pairsLayer, restrictToSelection, self.dlg.bufferValue() )
elif transType==3:
self.dlg.displayMsg( "Loading affine transformation vectors..." )
self.transformer = AffineTransformer( pairsLayer, restrictToSelection )
elif transType==2:
self.dlg.displayMsg( "Loading linear transformation vectors..." )
self.transformer = LinearTransformer( pairsLayer, restrictToSelection )
elif transType==1:
self.dlg.displayMsg( "Loading translation vector..." )
self.transformer = TranslationTransformer( pairsLayer, restrictToSelection )
else:
self.dlg.displayMsg( "INVALID TRANSFORMATION TYPE - YOU SHOULDN'T HAVE BEEN ABLE TO HIT RUN" )
return
# Starting to iterate
features = toBendLayer.getFeatures() if not self.dlg.restrictBox_toBendLayer.isChecked() else toBendLayer.selectedFeatures()
count = toBendLayer.featureCount() if not self.dlg.restrictBox_toBendLayer.isChecked() else len(features)
self.dlg.displayMsg( "Starting to iterate through %i features..." % count )
QCoreApplication.processEvents()
toBendLayer.beginEditCommand("Feature bending")
for i,feature in enumerate(features):
self.dlg.progressBar.setValue( int(100.0*float(i)/float(count)) )
self.dlg.displayMsg( "Aligning features %i out of %i..." % (i, count))
QCoreApplication.processEvents()
geom = feature.geometry()
#TODO : this cood be much simple if we could iterate through to vertices and use QgsGeometry.moveVertex(x,y,index), but QgsGeometry.vertexAt(index) doesn't tell wether the index exists, so there's no clean way to iterate...
if geom.type() == QgsWkbTypes.PointGeometry:
if not geom.isMultipart():
# SINGLE PART POINT
p = geom.asPoint()
newGeom = QgsGeometry.fromPointXY( self.transformer.map(p) )
else:
# MULTI PART POINT
listA = geom.asMultiPoint()
newListA = []
for p in listA:
newListA.append( self.transformer.map(p) )
newGeom = QgsGeometry.fromMultiPointXY( newListA )
elif geom.type() == QgsWkbTypes.LineGeometry:
if not geom.isMultipart():
# SINGLE PART LINESTRING
listA = geom.asPolyline()
newListA = []
for p in listA:
newListA.append( self.transformer.map(p) )
newGeom = QgsGeometry.fromPolylineXY( newListA )
else:
# MULTI PART LINESTRING
listA = geom.asMultiPolyline()
newListA = []
for listB in listA:
newListB = []
for p in listB:
newListB.append( self.transformer.map(p) )
newListA.append( newListB )
newGeom = QgsGeometry.fromMultiPolylineXY( newListA )
elif geom.type() == QgsWkbTypes.PolygonGeometry:
if not geom.isMultipart():
# SINGLE PART POLYGON
listA = geom.asPolygon()
newListA = []
for listB in listA:
newListB = []
for p in listB:
newListB.append( self.transformer.map(p) )
newListA.append( newListB )
newGeom = QgsGeometry.fromPolygonXY( newListA )
else:
# MULTI PART POLYGON
listA = geom.asMultiPolygon()
newListA = []
for listB in listA:
newListB = []
for listC in listB:
newListC = []
for p in listC:
newListC.append( self.transformer.map(p) )
newListB.append( newListC )
newListA.append( newListB )
newGeom = QgsGeometry.fromMultiPolygonXY( newListA )
else:
# FALLBACK, JUST IN CASE ;)
newGeom = geom
toBendLayer.changeGeometry( feature.id(), newGeom )
toBendLayer.endEditCommand()
toBendLayer.repaintRequested.emit()
#Transforming pairs to pins
if self.dlg.pairsToPinsCheckBox.isChecked():
features = pairsLayer.getFeatures() if not self.dlg.restrictBox_pairsLayer.isChecked() else pairsLayer.selectedFeatures()
count = pairsLayer.featureCount() if not self.dlg.restrictBox_pairsLayer.isChecked() else len(features)
self.dlg.progressBar.setValue( 0 )
self.dlg.displayMsg( "Starting to transform %i pairs to pins..." % count )
QCoreApplication.processEvents()
pairsLayer.beginEditCommand("Transforming pairs to pins")
for i,feature in enumerate(features):
self.dlg.progressBar.setValue( int(100.0*float(i)/float(count)) )
self.dlg.displayMsg( "Transforming pair to pin %i out of %i..." % (i, count))
QCoreApplication.processEvents()
geom = feature.geometry().asPolyline()
newGeom = QgsGeometry.fromPolylineXY( [geom[-1],geom[-1]] )
pairsLayer.changeGeometry( feature.id(), newGeom )
pairsLayer.endEditCommand()
self.dlg.displayMsg( "Finished !" )
self.dlg.progressBar.setValue( 100 )
pairsLayer.repaintRequested.emit()