123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- """
- ***************************************************************************
- RectanglesOvalsDiamondsVariable.py
- ---------------------
- Date : April 2016
- Copyright : (C) 2016 by Alexander Bruy
- Email : alexander dot bruy at gmail dot 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. *
- * *
- ***************************************************************************
- """
- __author__ = 'Alexander Bruy'
- __date__ = 'August 2012'
- __copyright__ = '(C) 2012, Victor Olaya'
- import math
- from qgis.PyQt.QtCore import QCoreApplication
- from qgis.core import (NULL,
- QgsWkbTypes,
- QgsFeature,
- QgsFeatureSink,
- QgsGeometry,
- QgsPointXY,
- QgsProcessing,
- QgsProcessingException,
- QgsProcessingAlgorithm,
- QgsProcessingParameterField,
- QgsProcessingParameterFeatureSource,
- QgsProcessingParameterEnum,
- QgsProcessingParameterNumber,
- QgsProcessingParameterFeatureSink)
- from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
- class RectanglesOvalsDiamondsVariable(QgisAlgorithm):
- INPUT = 'INPUT'
- SHAPE = 'SHAPE'
- WIDTH = 'WIDTH'
- HEIGHT = 'HEIGHT'
- ROTATION = 'ROTATION'
- SEGMENTS = 'SEGMENTS'
- OUTPUT = 'OUTPUT'
- def group(self):
- return self.tr('Vector geometry')
- def groupId(self):
- return 'vectorgeometry'
- def __init__(self):
- super().__init__()
- def flags(self):
- return super().flags() | QgsProcessingAlgorithm.FlagDeprecated
- def initAlgorithm(self, config=None):
- self.shapes = [self.tr('Rectangles'), self.tr('Diamonds'), self.tr('Ovals')]
- self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
- self.tr('Input layer'),
- [QgsProcessing.TypeVectorPoint]))
- self.addParameter(QgsProcessingParameterEnum(self.SHAPE,
- self.tr('Buffer shape'), options=self.shapes))
- self.addParameter(QgsProcessingParameterField(self.WIDTH,
- self.tr('Width field'),
- parentLayerParameterName=self.INPUT,
- type=QgsProcessingParameterField.Numeric))
- self.addParameter(QgsProcessingParameterField(self.HEIGHT,
- self.tr('Height field'),
- parentLayerParameterName=self.INPUT,
- type=QgsProcessingParameterField.Numeric))
- self.addParameter(QgsProcessingParameterField(self.ROTATION,
- self.tr('Rotation field'),
- parentLayerParameterName=self.INPUT,
- type=QgsProcessingParameterField.Numeric,
- optional=True))
- self.addParameter(QgsProcessingParameterNumber(self.SEGMENTS,
- self.tr('Number of segments'),
- minValue=1,
- defaultValue=36))
- self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
- self.tr('Output'),
- type=QgsProcessing.TypeVectorPolygon))
- def name(self):
- return 'rectanglesovalsdiamondsvariable'
- def displayName(self):
- return self.tr('Rectangles, ovals, diamonds (variable)')
- def processAlgorithm(self, parameters, context, feedback):
- source = self.parameterAsSource(parameters, self.INPUT, context)
- if source is None:
- raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))
- shape = self.parameterAsEnum(parameters, self.SHAPE, context)
- width_field = self.parameterAsString(parameters, self.WIDTH, context)
- height_field = self.parameterAsString(parameters, self.HEIGHT, context)
- rotation_field = self.parameterAsString(parameters, self.ROTATION, context)
- segments = self.parameterAsInt(parameters, self.SEGMENTS, context)
- (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
- source.fields(), QgsWkbTypes.Polygon, source.sourceCrs())
- if sink is None:
- raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))
- width = source.fields().lookupField(width_field)
- height = source.fields().lookupField(height_field)
- rotation = source.fields().lookupField(rotation_field)
- if shape == 0:
- self.rectangles(sink, source, width, height, rotation, feedback)
- elif shape == 1:
- self.diamonds(sink, source, width, height, rotation, feedback)
- else:
- self.ovals(sink, source, width, height, rotation, segments, feedback)
- return {self.OUTPUT: dest_id}
- def rectangles(self, sink, source, width, height, rotation, feedback):
- ft = QgsFeature()
- features = source.getFeatures()
- total = 100.0 / source.featureCount() if source.featureCount() else 0
- if rotation >= 0:
- for current, feat in enumerate(features):
- if feedback.isCanceled():
- break
- if not feat.hasGeometry():
- continue
- w = feat[width]
- h = feat[height]
- angle = feat[rotation]
- # block 0/NULL width or height, but allow 0 as angle value
- if not w or not h:
- feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty '
- 'width or height. '
- 'Skipping…').format(feat.id()))
- continue
- if angle is NULL:
- feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty '
- 'angle. '
- 'Skipping…').format(feat.id()))
- continue
- xOffset = w / 2.0
- yOffset = h / 2.0
- phi = angle * math.pi / 180
- point = feat.geometry().asPoint()
- x = point.x()
- y = point.y()
- points = [(-xOffset, -yOffset), (-xOffset, yOffset), (xOffset, yOffset), (xOffset, -yOffset)]
- polygon = [[QgsPointXY(i[0] * math.cos(phi) + i[1] * math.sin(phi) + x,
- -i[0] * math.sin(phi) + i[1] * math.cos(phi) + y) for i in points]]
- ft.setGeometry(QgsGeometry.fromPolygonXY(polygon))
- ft.setAttributes(feat.attributes())
- sink.addFeature(ft, QgsFeatureSink.FastInsert)
- feedback.setProgress(int(current * total))
- else:
- for current, feat in enumerate(features):
- if feedback.isCanceled():
- break
- if not feat.hasGeometry():
- continue
- w = feat[width]
- h = feat[height]
- if not w or not h:
- feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty '
- 'width or height. '
- 'Skipping…').format(feat.id()))
- continue
- xOffset = w / 2.0
- yOffset = h / 2.0
- point = feat.geometry().asPoint()
- x = point.x()
- y = point.y()
- points = [(-xOffset, -yOffset), (-xOffset, yOffset), (xOffset, yOffset), (xOffset, -yOffset)]
- polygon = [[QgsPointXY(i[0] + x, i[1] + y) for i in points]]
- ft.setGeometry(QgsGeometry.fromPolygonXY(polygon))
- ft.setAttributes(feat.attributes())
- sink.addFeature(ft, QgsFeatureSink.FastInsert)
- feedback.setProgress(int(current * total))
- def diamonds(self, sink, source, width, height, rotation, feedback):
- features = source.getFeatures()
- ft = QgsFeature()
- total = 100.0 / source.featureCount() if source.featureCount() else 0
- if rotation >= 0:
- for current, feat in enumerate(features):
- if feedback.isCanceled():
- break
- if not feat.hasGeometry():
- continue
- w = feat[width]
- h = feat[height]
- angle = feat[rotation]
- # block 0/NULL width or height, but allow 0 as angle value
- if not w or not h:
- feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty '
- 'width or height. '
- 'Skipping…').format(feat.id()))
- continue
- if angle is NULL:
- feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty '
- 'angle. '
- 'Skipping…').format(feat.id()))
- continue
- xOffset = w / 2.0
- yOffset = h / 2.0
- phi = angle * math.pi / 180
- point = feat.geometry().asPoint()
- x = point.x()
- y = point.y()
- points = [(0.0, -yOffset), (-xOffset, 0.0), (0.0, yOffset), (xOffset, 0.0)]
- polygon = [[QgsPointXY(i[0] * math.cos(phi) + i[1] * math.sin(phi) + x,
- -i[0] * math.sin(phi) + i[1] * math.cos(phi) + y) for i in points]]
- ft.setGeometry(QgsGeometry.fromPolygonXY(polygon))
- ft.setAttributes(feat.attributes())
- sink.addFeature(ft, QgsFeatureSink.FastInsert)
- feedback.setProgress(int(current * total))
- else:
- for current, feat in enumerate(features):
- if feedback.isCanceled():
- break
- if not feat.hasGeometry():
- continue
- w = feat[width]
- h = feat[height]
- if not w or not h:
- feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty '
- 'width or height. '
- 'Skipping…').format(feat.id()))
- continue
- xOffset = w / 2.0
- yOffset = h / 2.0
- point = feat.geometry().asPoint()
- x = point.x()
- y = point.y()
- points = [(0.0, -yOffset), (-xOffset, 0.0), (0.0, yOffset), (xOffset, 0.0)]
- polygon = [[QgsPointXY(i[0] + x, i[1] + y) for i in points]]
- ft.setGeometry(QgsGeometry.fromPolygonXY(polygon))
- ft.setAttributes(feat.attributes())
- sink.addFeature(ft, QgsFeatureSink.FastInsert)
- feedback.setProgress(int(current * total))
- def ovals(self, sink, source, width, height, rotation, segments, feedback):
- features = source.getFeatures()
- ft = QgsFeature()
- total = 100.0 / source.featureCount() if source.featureCount() else 0
- if rotation >= 0:
- for current, feat in enumerate(features):
- if feedback.isCanceled():
- break
- if not feat.hasGeometry():
- continue
- w = feat[width]
- h = feat[height]
- angle = feat[rotation]
- # block 0/NULL width or height, but allow 0 as angle value
- if not w or not h:
- feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty '
- 'width or height. '
- 'Skipping…').format(feat.id()))
- continue
- if angle == NULL:
- feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty '
- 'angle. '
- 'Skipping…').format(feat.id()))
- continue
- xOffset = w / 2.0
- yOffset = h / 2.0
- phi = angle * math.pi / 180
- point = feat.geometry().asPoint()
- x = point.x()
- y = point.y()
- points = [
- (xOffset * math.cos(t), yOffset * math.sin(t))
- for t in [(2 * math.pi) / segments * i for i in range(segments)]
- ]
- polygon = [[QgsPointXY(i[0] * math.cos(phi) + i[1] * math.sin(phi) + x,
- -i[0] * math.sin(phi) + i[1] * math.cos(phi) + y) for i in points]]
- ft.setGeometry(QgsGeometry.fromPolygonXY(polygon))
- ft.setAttributes(feat.attributes())
- sink.addFeature(ft, QgsFeatureSink.FastInsert)
- feedback.setProgress(int(current * total))
- else:
- for current, feat in enumerate(features):
- if feedback.isCanceled():
- break
- if not feat.hasGeometry():
- continue
- w = feat[width]
- h = feat[height]
- if not w or not h:
- feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty '
- 'width or height. '
- 'Skipping…').format(feat.id()))
- continue
- xOffset = w / 2.0
- yOffset = h / 2.0
- point = feat.geometry().asPoint()
- x = point.x()
- y = point.y()
- points = [
- (xOffset * math.cos(t), yOffset * math.sin(t))
- for t in [(2 * math.pi) / segments * i for i in range(segments)]
- ]
- polygon = [[QgsPointXY(i[0] + x, i[1] + y) for i in points]]
- ft.setGeometry(QgsGeometry.fromPolygonXY(polygon))
- ft.setAttributes(feat.attributes())
- sink.addFeature(ft, QgsFeatureSink.FastInsert)
- feedback.setProgress(int(current * total))
|