dtutils.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. # -*- coding: utf-8 -*-
  2. """
  3. dtutils
  4. ---------
  5. Contains various utitlity functions
  6. some code from fTools plugin contained.
  7. """
  8. """
  9. Part of DigitizingTools, a QGIS plugin that
  10. subsumes different tools neded during digitizing sessions.
  11. * begin : 2013-02-25
  12. * copyright : (C) 2013 by Bernhard Ströbl
  13. * email : bernhard.stroebl@jena.de
  14. license
  15. ````````
  16. This program is free software; you can redistribute it and/or modify
  17. it under the terms of the GNU General Public License as published by
  18. the Free Software Foundation; either version 2 of the License, or
  19. (at your option) any later version.
  20. """
  21. from builtins import range
  22. from builtins import str
  23. from qgis.PyQt import QtCore, QtGui, QtWidgets
  24. from qgis.core import *
  25. from qgis.gui import *
  26. def debug(msg):
  27. QtWidgets.QMessageBox.information(None, "debug", str(msg))
  28. def dtGetFeatureForId(layer, fid):
  29. '''Function that returns the QgsFeature with FeatureId *fid* in QgsVectorLayer *layer*'''
  30. feat = QgsFeature()
  31. if layer.getFeatures(QgsFeatureRequest().setFilterFid(fid)).nextFeature(feat):
  32. return feat
  33. else:
  34. return None
  35. def dtCopyFeature(layer, srcFeature = None, srcFid = None):
  36. '''Copy the QgsFeature with FeatureId *srcFid* in *layer* and return it. Alternatively the
  37. source Feature can be given as paramter. The feature is not added to the layer!'''
  38. if srcFid != None:
  39. srcFeature = dtGetFeatureForId(layer, srcFid)
  40. if srcFeature:
  41. #get layer type
  42. layerType = layer.geometryType()
  43. if layerType == 0:
  44. dummyGeomTxt = 'Point()'
  45. elif layerType == 1:
  46. dummyGeomTxt = 'LineString()'
  47. elif layerType == 2:
  48. dummyGeomTxt = 'Polygon()'
  49. #set dummy geom
  50. dummyGeom = QgsGeometry.fromWkt(dummyGeomTxt)
  51. #copy the attribute values
  52. attributes = {i: v for i, v in enumerate(srcFeature.attributes())}
  53. newFeature = QgsVectorLayerUtils.createFeature(layer, dummyGeom, attributes)
  54. return newFeature
  55. else:
  56. return None
  57. def dtMakeFeaturesFromGeometries(layer, srcFeat, geometries):
  58. '''create new features from geometries and copy attributes from srcFeat'''
  59. newFeatures = []
  60. for aGeom in geometries:
  61. newFeat = dtCopyFeature(layer, srcFeat)
  62. newFeat.setGeometry(aGeom)
  63. newFeatures.append(newFeat)
  64. return newFeatures
  65. def dtGetVectorLayersByType(iface, geomType = None, skipActive = False):
  66. '''Returns a dict of layers [name: id] in the project for the given
  67. *geomType*; geomTypes are 0: point, 1: line, 2: polygon
  68. If *skipActive* is True the active Layer is not included.'''
  69. layerList = {}
  70. for anId, aLayer in QgsProject.instance().mapLayers().items():
  71. if 0 == aLayer.type(): # vectorLayer
  72. if skipActive and (iface.mapCanvas().currentLayer().id() == anId):
  73. continue
  74. else:
  75. if geomType:
  76. if isinstance(geomType, int):
  77. if aLayer.geometryType() == geomType:
  78. layerList[aLayer.name()] = [anId, aLayer]
  79. else:
  80. layerList[aLayer.name()] = [anId, aLayer]
  81. return layerList
  82. def dtChooseVectorLayer(iface, geomType = None, skipActive = True, msg = None):
  83. '''Offers a QInputDialog where the user can choose a Layer of type *geomType*.
  84. If *skipActive* is True the active Layer can not be chosen. *msg* is displayed as the dialog's message.'''
  85. layerList = dtGetVectorLayersByType(iface, geomType, skipActive)
  86. chooseFrom = []
  87. retValue = None
  88. if len(layerList) > 0:
  89. for aName in layerList:
  90. chooseFrom.append(aName)
  91. if not msg:
  92. msg = ""
  93. selectedLayer, ok = QtWidgets.QInputDialog.getItem(None,
  94. QtWidgets.QApplication.translate("dtutils", "Choose Layer"),
  95. msg, chooseFrom, editable = False)
  96. if ok:
  97. retValue = layerList[selectedLayer][1]
  98. return retValue
  99. def dtGetNoSelMessage():
  100. '''Returns an array of QStrings (default messages)'''
  101. noSelMsg1 = QtCore.QCoreApplication.translate("digitizingtools", "No Selection in layer")
  102. noSelMsg2 = QtCore.QCoreApplication.translate("digitizingtools", "Use all features for process?")
  103. return [noSelMsg1, noSelMsg2]
  104. def dtGetManySelMessage(layer):
  105. '''Returns an array of QStrings (default messages)'''
  106. manySelMsg = QtCore.QCoreApplication.translate("digitizingtools", "There are ")
  107. manySelMsg += str(layer.selectedFeatureCount())
  108. manySelMsg += QtCore.QCoreApplication.translate("digitizingtools", " features selected in layer " )
  109. manySelMsg += layer.name() + "."
  110. return manySelMsg
  111. def dtGetInvalidGeomWarning(layer):
  112. invalidGeomMsg = QtCore.QCoreApplication.translate("digitizingtools", "There are invalid geometries in layer ")
  113. invalidGeomMsg += layer.name()
  114. return invalidGeomMsg
  115. def dtGetNotMatchingGeomWarning(layer):
  116. notMatchingGeomMsg = QtCore.QCoreApplication.translate(
  117. "digitizingtools", "Geometry's type is not compatible with the following layer: ")
  118. notMatchingGeomMsg += layer.name() + ". "
  119. notMatchingGeomMsg += QtCore.QCoreApplication.translate(
  120. "digitizingtools", "Fix geometries before commiting changes.")
  121. return notMatchingGeomMsg
  122. def showSnapSettingsWarning(iface):
  123. title = QtCore.QCoreApplication.translate("digitizingtools", "Snap Tolerance")
  124. msg1 = QtCore.QCoreApplication.translate(
  125. "digitizingtools", "Could not snap vertex")
  126. msg2 = QtCore.QCoreApplication.translate("digitizingtools",
  127. "Have you set the tolerance in Settings > Snapping Options?")
  128. dtShowWarning(iface, msg1 + " " + msg2, title)
  129. def dtShowWarning(iface, msg, title = None):
  130. iface.messageBar().pushWarning(title, msg)
  131. def dtGetErrorMessage():
  132. '''Returns the default error message which can be appended'''
  133. return QtCore.QCoreApplication.translate("digitizingtools", "Error occured during")
  134. # code taken from fTools plugin
  135. def dtExtractPoints( geom ):
  136. '''Generate list of QgsPoints from QgsGeometry *geom* ( can be point, line, or polygon )'''
  137. multi_geom = QgsGeometry()
  138. temp_geom = []
  139. if geom.type() == 0: # it's a point
  140. if geom.isMultipart():
  141. temp_geom = geom.asMultiPoint()
  142. else:
  143. temp_geom.append(geom.asPoint())
  144. if geom.type() == 1: # it's a line
  145. if geom.isMultipart():
  146. multi_geom = geom.asMultiPolyline() #multi_geog is a multiline
  147. for i in multi_geom: #i is a line
  148. temp_geom.extend( i )
  149. else:
  150. temp_geom = geom.asPolyline()
  151. elif geom.type() == 2: # it's a polygon
  152. if geom.isMultipart():
  153. multi_geom = geom.asMultiPolygon() #multi_geom is a multipolygon
  154. for i in multi_geom: #i is a polygon
  155. for j in i: #j is a line
  156. temp_geom.extend( j )
  157. else:
  158. multi_geom = geom.asPolygon() #multi_geom is a polygon
  159. for i in multi_geom: #i is a line
  160. temp_geom.extend( i )
  161. return temp_geom
  162. # code adopted from ringer plugin
  163. def dtExtractRings(geom):
  164. '''Generate a list of QgsPolygons representing all rings within *geom* (= polygon)'''
  165. rings = []
  166. if geom.type() == 2: # it's a polygon
  167. if geom.isMultipart():
  168. multi_geom = geom.asMultiPolygon() #multi_geom is a multipolygon
  169. for poly in multi_geom:
  170. if len(poly) > 1:
  171. for aRing in poly[1:]:
  172. rings.append(QgsGeometry.fromPolygonXY([aRing]))
  173. else:
  174. poly = geom.asPolygon()
  175. if len(poly) > 1:
  176. for aRing in poly[1:]:
  177. rings.append(QgsGeometry.fromPolygonXY([aRing]))
  178. return rings
  179. def dtCombineSelectedPolygons(layer, iface, multiGeom = None, fillRings = True):
  180. '''
  181. make one polygon from selected polygons in layer, optionally fill
  182. all rings contained in the input polygons
  183. '''
  184. for aFeat in layer.selectedFeatures():
  185. aGeom = QgsGeometry(aFeat.geometry())
  186. if not aGeom.isGeosValid():
  187. thisWarning = dtGetInvalidGeomWarning(layer)
  188. dtShowWarning(iface, thisWarning)
  189. return None
  190. # fill rings contained in the polygon
  191. if aGeom.isMultipart():
  192. tempGeom = None
  193. for poly in aGeom.asMultiPolygon():
  194. if fillRings:
  195. noRingGeom = dtDeleteRings(poly)
  196. else:
  197. noRingGeom = poly
  198. if tempGeom == None:
  199. tempGeom = noRingGeom
  200. else:
  201. tempGeom = tempGeom.combine(noRingGeom)
  202. else:
  203. if fillRings:
  204. tempGeom = dtDeleteRings(aGeom.asPolygon())
  205. else:
  206. tempGeom = aGeom
  207. # make a large polygon from all selected
  208. if multiGeom == None:
  209. multiGeom = tempGeom
  210. else:
  211. multiGeom = multiGeom.combine(tempGeom)
  212. return multiGeom
  213. def dtSpatialindex(layer):
  214. """Creates a spatial index for the passed vector layer.
  215. """
  216. idx = QgsSpatialIndex()
  217. for ft in layer.getFeatures():
  218. idx.addFeature(ft)
  219. return idx
  220. def dtDeleteRings(poly):
  221. outGeom = QgsGeometry.fromPolygonXY(poly)
  222. if len(poly) > 1:
  223. # we have rings
  224. rings = dtExtractRings(outGeom)
  225. for aRing in rings:
  226. outGeom = outGeom.combine(aRing)
  227. return outGeom
  228. def dtGetDefaultAttributeMap(layer):
  229. attributeMap = {}
  230. dp = layer.dataProvider()
  231. for i in range(len(layer.fields())):
  232. attributeMap[i] = dp.defaultValue(i)
  233. return attributeMap
  234. def dtGetHighlightSettings():
  235. '''highlight a geom in a layer with highlight color from settings'''
  236. s = QtCore.QSettings()
  237. s.beginGroup("Map/highlight")
  238. buffer = s.value("buffer", "0.5")
  239. hexColor = s.value("color", "#ff0000")
  240. colorAlpha = s.value("colorAlpha", "128")
  241. minWidth = s.value("minWidth", "1")
  242. s.endGroup()
  243. color = QtGui.QColor()
  244. color.setNamedColor(hexColor)
  245. fillColor = QtGui.QColor()
  246. r, g, b, a = color.getRgb()
  247. fillColor.setRgb(r, g, b, int(colorAlpha))
  248. return [color, fillColor, float(buffer), float(minWidth)]