class CentroidDecorator(QgsMapCanvasItem): """ Decorates centroids of features with population statistics """ def __init__(self, canvas: QgsMapCanvas, electorate_layer: QgsVectorLayer, meshblock_layer: QgsVectorLayer, task: str): """ Constructor :param canvas: map canvas :param electorate_layer: electorates layer :param meshblock_layer: meshblocks layer :param task: current task """ super().__init__(canvas) self.canvas = canvas self.electorate_layer = electorate_layer self.meshblock_layer = meshblock_layer self.task = task self.text_format = QgsTextFormat() # self.text_format.shadow().setEnabled(True) self.text_format.background().setEnabled(True) self.text_format.background().setSize(QSizeF(1, 0)) self.text_format.background().setOffset(QPointF(0, -0.7)) self.text_format.background().setRadii(QSizeF(1, 1)) self.image = None def redraw(self): """ Forces a redraw of the cached image """ self.image = None def paint(self, painter, option, widget): # pylint: disable=missing-docstring, unused-argument, too-many-locals if self.image is not None: painter.drawImage(0, 0, self.image) return image_size = self.canvas.mapSettings().outputSize() self.image = QImage(image_size.width(), image_size.height(), QImage.Format_ARGB32) self.image.fill(0) image_painter = QPainter(self.image) render_context = QgsRenderContext.fromQPainter(image_painter) image_painter.setRenderHint(QPainter.Antialiasing, True) rect = self.canvas.mapSettings().visibleExtent() request = QgsFeatureRequest() request.setFilterRect(rect) request.setFilterExpression( QgsExpression.createFieldEqualityExpression('type', self.task)) for f in self.electorate_layer.getFeatures(request): # pole, dist = f.geometry().clipped(rect).poleOfInaccessibility(rect.width() / 30) pixel = self.toCanvasCoordinates( f.geometry().clipped(rect).centroid().asPoint()) calc = QgsAggregateCalculator(self.meshblock_layer) calc.setFilter('staged_electorate={}'.format(f['electorate_id'])) estimated_pop, ok = calc.calculate(QgsAggregateCalculator.Sum, 'offline_pop_{}'.format( self.task.lower())) # pylint: disable=unused-variable text_string = [ '{}'.format(f['name']), '{}'.format(int(estimated_pop)) ] QgsTextRenderer().drawText(QPointF(pixel.x(), pixel.y()), 0, QgsTextRenderer.AlignCenter, text_string, render_context, self.text_format) image_painter.end() painter.drawImage(0, 0, self.image)
class CentroidDecorator(QgsMapCanvasItem): """ Decorates centroids of features with population statistics """ def __init__(self, canvas: QgsMapCanvas, electorate_layer: QgsVectorLayer, meshblock_layer: QgsVectorLayer, task: str, quota: int): """ Constructor :param canvas: map canvas :param electorate_layer: electorates layer :param meshblock_layer: meshblocks layer :param task: current task :param quota: target quota """ super().__init__(canvas) self.canvas = canvas self.electorate_layer = electorate_layer self.meshblock_layer = meshblock_layer self.task = task self.text_format = QgsTextFormat() # self.text_format.shadow().setEnabled(True) self.text_format.background().setEnabled(True) self.text_format.background().setSize(QSizeF(1, 0)) self.text_format.background().setOffset(QPointF(0, -0.7)) self.text_format.background().setRadii(QSizeF(1, 1)) self.image = None self.quota = quota self.original_populations = {} self.new_populations = {} def redraw(self, handler): """ Forces a redraw of the cached image """ self.image = None if not self.original_populations: # first run, get initial estimates request = QgsFeatureRequest() request.setFilterExpression(QgsExpression.createFieldEqualityExpression('type', self.task)) request.setFlags(QgsFeatureRequest.NoGeometry) for f in self.electorate_layer.getFeatures(request): estimated_pop = f.attribute(handler.stats_nz_pop_field_index) if estimated_pop is None or estimated_pop == NULL: # otherwise just use existing estimated pop as starting point estimated_pop = f.attribute(handler.estimated_pop_idx) self.original_populations[f.id()] = estimated_pop # step 1: get all electorate features corresponding to affected electorates electorate_features = {f[handler.electorate_layer_field]: f for f in handler.get_affected_districts( [handler.electorate_layer_field, handler.stats_nz_pop_field, 'estimated_pop'], needs_geometry=False)} self.new_populations = {} for district in handler.pending_affected_districts.keys(): # pylint: disable=consider-iterating-dictionary # use stats nz pop as initial estimate, if available estimated_pop = electorate_features[district].attribute(handler.stats_nz_pop_field_index) if estimated_pop is None or estimated_pop == NULL: # otherwise just use existing estimated pop as starting point estimated_pop = electorate_features[district].attribute(handler.estimated_pop_idx) # add new bits estimated_pop = handler.grow_population_with_added_meshblocks(district, estimated_pop) # minus lost bits estimated_pop = handler.shrink_population_by_removed_meshblocks(district, estimated_pop) self.new_populations[electorate_features[district].id()] = estimated_pop def paint(self, painter, option, widget): # pylint: disable=missing-docstring, unused-argument, too-many-locals if self.image is not None: painter.drawImage(0, 0, self.image) return image_size = self.canvas.mapSettings().outputSize() self.image = QImage(image_size.width(), image_size.height(), QImage.Format_ARGB32) self.image.fill(0) image_painter = QPainter(self.image) render_context = QgsRenderContext.fromQPainter(image_painter) image_painter.setRenderHint(QPainter.Antialiasing, True) rect = self.canvas.mapSettings().visibleExtent() request = QgsFeatureRequest() request.setFilterRect(rect) request.setFilterExpression(QgsExpression.createFieldEqualityExpression('type', self.task)) for f in self.electorate_layer.getFeatures(request): # pole, dist = f.geometry().clipped(rect).poleOfInaccessibility(rect.width() / 30) pixel = self.toCanvasCoordinates(f.geometry().clipped(rect).centroid().asPoint()) estimated_pop = self.new_populations[f.id()] if f.id() in self.new_populations else \ self.original_populations[f.id()] variance = LinzElectoralDistrictRegistry.get_variation_from_quota_percent(self.quota, estimated_pop) text_string = ['{}'.format(f['name']), '{}'.format(int(estimated_pop)), '{}{}%'.format('+' if variance > 0 else '', variance)] QgsTextRenderer().drawText(QPointF(pixel.x(), pixel.y()), 0, QgsTextRenderer.AlignCenter, text_string, render_context, self.text_format) image_painter.end() painter.drawImage(0, 0, self.image)