def setup_hebesatz(self): ''' assessment rate parameters ''' if self.hebesatz_params: self.hebesatz_params.close() layout = self.ui.grundsteuer_hebesatz_param_group.layout() clear_layout(layout) self.hebesatz_params = Params( layout, help_file='einnahmen_grundsteuer_hebesatz.txt') self.hebesatz_params.hebesatz = Param( self.grst_settings.Hebesatz_GrStB, SpinBox(maximum=999, step=10), label='Hebesatz GrSt B Projektgemeinde', unit='v.H.') def save(): self.changed.emit() self.grst_settings.Hebesatz_GrStB = \ self.hebesatz_params.hebesatz.value self.grst_settings.save() self.hebesatz_params.show(title='Hebesatz bearbeiten') self.hebesatz_params.changed.connect(save)
def setup_weights(self): ''' set up the parameters to weight the transfer nodes ''' self.set_status() if self.weight_params: self.weight_params.close() layout = self.ui.weights_group.layout() clear_layout(layout) self.weight_params = Params(parent=layout, button_label='Gewichtungen verändern', help_file='verkehr_gewichtungen.txt') dependency = SumDependency(100) for node in self.transfer_nodes: perc = round(node.weight) param = Param(perc, Slider(maximum=100, lockable=True), label=node.name, unit='%') self.weight_params.add(param, name=node.name) dependency.add(param) def save(): for node in self.transfer_nodes: node.weight = self.weight_params[node.name].value node.save() self.traffic_load.table.truncate() self.canvas.refreshAllLayers() self.set_status() self.weight_params.changed.connect(save) self.weight_params.show()
def setup_params(self): if self.params: self.params.close() layout = self.ui.gewerbesteuer_hebesatz_param_group.layout() clear_layout(layout) self.params = Params(layout, help_file='einnahmen_gewerbesteuer_hebesätze.txt') self.params.add(Title('Hebesätze', bold=False)) for gemeinde in sorted(self.gemeinden, key=lambda x: x.GEN): spinbox = SpinBox(minimum=0, maximum=999, step=1) param = Param(gemeinde.Hebesatz_GewSt, spinbox, label=f' - {gemeinde.GEN}', unit='v.H.') self.params.add(param, name=gemeinde.AGS) def save(): self.changed.emit() for gemeinde in self.gemeinden: param = self.params[gemeinde.AGS] gemeinde.Hebesatz_GewSt = param.value gemeinde.save() self.params.show(title='Hebesätze Gewerbesteuer bearbeiten', scrollable=True) self.params.changed.connect(save)
def setup_line_params(self): ''' set up the parameters to edit the lengths of the line measures ''' layout = self.ui.mengen_params_group.layout() clear_layout(layout) if len(self.line_elements) == 0: self.init_lines() self.line_params = Params( layout, help_file='infrastruktur_linienelemente.txt') for element in self.line_elements: param = Param(int(element.length), Slider(maximum=10000), label=element.Netzelement, unit='m') self.line_params.add( param, name=f'netzelement_{element.IDNetzelement}') def save(): for element in self.line_elements: param = self.line_params[f'netzelement_{element.IDNetzelement}'] element.length = param.value element.save() self.line_params.show(title=self.ui.mengen_params_group.title()) self.line_params.changed.connect(save) last_row = self.line_params.layout.children()[-1] button = QPushButton() button.setText('aus Zeichnung übernehmen') last_row.insertWidget(0, button) button.clicked.connect(self.apply_drawing)
def setup_rohmiete(self): ''' gross rent parameters ''' tou = self.areas.values('nutzungsart') if self.grst_settings.is_new_bundesland \ or not Nutzungsart.WOHNEN.value in tou: self.ui.grundsteuer_rohmiete_param_group.setVisible(False) return self.ui.grundsteuer_rohmiete_param_group.setVisible(True) if self.rohmiete_params: self.rohmiete_params.close() layout = self.ui.grundsteuer_rohmiete_param_group.layout() clear_layout(layout) self.rohmiete_params = Params( layout, help_file='einnahmen_grundsteuer_rohmieten.txt') self.rohmiete_params.add( Title('Rohmiete 1964 in Euro pro Monat', bold=False)) self.rohmiete_params.efh = Param(self.grst_settings.EFH_Rohmiete / 100, DoubleSpinBox(minimum=0.3, maximum=5, step=0.05), label=f' - Einfamilienhaus', unit='€/m²') self.rohmiete_params.dhh = Param(self.grst_settings.DHH_Rohmiete / 100, DoubleSpinBox(minimum=0.3, maximum=5, step=0.05), label=f' - Doppelhaus', unit='€/m²') self.rohmiete_params.rhw = Param(self.grst_settings.RHW_Rohmiete / 100, DoubleSpinBox(minimum=0.3, maximum=5, step=0.05), label=f' - Reihenhaus', unit='€/m²') self.rohmiete_params.mfh = Param(self.grst_settings.MFH_Rohmiete / 100, DoubleSpinBox(minimum=0.3, maximum=5, step=0.05), label=f' - Mehrfamilienhaus', unit='€/m²') def save(): self.changed.emit() self.grst_settings.EFH_Rohmiete = round( self.rohmiete_params.efh.value * 100) self.grst_settings.DHH_Rohmiete = round( self.rohmiete_params.dhh.value * 100) self.grst_settings.RHW_Rohmiete = round( self.rohmiete_params.rhw.value * 100) self.grst_settings.MFH_Rohmiete = round( self.rohmiete_params.mfh.value * 100) self.grst_settings.save() self.rohmiete_params.show(title='Rohmieten bearbeiten') self.rohmiete_params.changed.connect(save)
def setup_kostenaufteilung(self, net_id): ''' set up parameters to edit shares for a specific type of infrastructure ''' self.net_id = net_id ui_group = self.ui.kostenaufteilung_params_group net_name = self.netzelemente.filter(IDNetz=net_id)[0].Netz ui_group.setTitle(net_name) layout = ui_group.layout() clear_layout(layout) self.params = Params( layout, help_file='infrastruktur_kostenaufteilung.txt') field_names = ['Anteil_GSB', 'Anteil_GEM', 'Anteil_ALL'] labels = ['Kostenanteil der Grunstücksbesitzer/innen', 'Kostenanteil der Gemeinde', 'Netznutzer/innen und Tarifkundschaft'] def preset_changed(c, p): preset = c.get_data() if not preset: return for field_name in field_names: param = self.params.get(f'{p.Kostenphase}_{field_name}') param.input.value = preset[field_name] for i, phase in enumerate(self.kostenphasen): dependency = SumDependency(100) self.params.add(Title(phase.Kostenphase)) feature = self.kostenaufteilung.get( IDKostenphase=phase.IDKostenphase, IDNetz=net_id) preset_combo, options = self.create_presets( net_id, phase.IDKostenphase) param = Param(0, preset_combo, label='Vorschlagswerte') param.hide_in_overview = True self.params.add(param, name=f'{phase.Kostenphase}_presets') for j, field_name in enumerate(field_names): label = labels[j] slider = Slider(maximum=100, lockable=True) param = Param(feature[field_name], slider, label=label, unit='%') self.params.add( param, name=f'{phase.Kostenphase}_{field_name}') dependency.add(param) slider.changed.connect( lambda b, c=preset_combo, o=options: c.set_value(o[0])) if i != len(self.kostenphasen) - 1: self.params.add(Seperator(margin=0)) preset_combo.changed.connect( lambda b, c=preset_combo, p=phase: preset_changed(c, p)) self.params.show(title='Kostenaufteilung festlegen') self.params.changed.connect(lambda: self.save(net_id))
def setup_bauvolumen(self): ''' construction volume parameters ''' tou = self.areas.values('nutzungsart') if not (Nutzungsart.GEWERBE.value in tou or Nutzungsart.EINZELHANDEL.value in tou): self.ui.grundsteuer_bauvolumen_param_group.setVisible(False) # set to 0 as a precaution to not put some old values into # the calculation self.grst_settings.Bueroflaeche = 0 self.grst_settings.Verkaufsraeume = 0 self.grst_settings.save() return self.ui.grundsteuer_bauvolumen_param_group.setVisible(True) if self.bauvolumen_params: self.bauvolumen_params.close() layout = self.ui.grundsteuer_bauvolumen_param_group.layout() clear_layout(layout) self.bauvolumen_params = Params( layout, help_file='einnahmen_grundsteuer_bauvolumen.txt') self.bauvolumen_params.add( Title( 'Gewerbe / Einzelhandel: Voraussichtliches ' 'Bauvolumen\n(Brutto-Grundfläche, BGF)', bold=False)) self.bauvolumen_params.bueroflaeche = Param( self.grst_settings.Bueroflaeche, SpinBox(minimum=0, maximum=99999, step=10), label=f' - Bürofläche', unit='m²') self.bauvolumen_params.verkaufsraeume = Param( self.grst_settings.Verkaufsraeume, SpinBox(minimum=0, maximum=99999, step=10), label=f' - Hallen und Verkaufsräume', unit='m²') def save(): self.changed.emit() self.grst_settings.Bueroflaeche = \ self.bauvolumen_params.bueroflaeche.value self.grst_settings.Verkaufsraeume = \ self.bauvolumen_params.verkaufsraeume.value self.grst_settings.save() self.bauvolumen_params.show( title='Voraussichtliches Bauvolumen bearbeiten') self.bauvolumen_params.changed.connect(save)
def setup_params(self): ''' set up the parameters for editing the mean residential area per appartment and the net share of residential building land in the active area ''' anteil = self.wohnbauland_anteile.get(id_teilflaeche=self.area.id) value = anteil.nettoflaeche if anteil else 85 clear_layout(self.layout) self.params = Params( self.layout, help_file='flaecheninanspruchnahme_wohnbauland_wohnflaeche.txt' ) self.params.add(Title('Anteil Nettowohnbauland')) self.params.nettoflaeche = Param( int(value), Slider(maximum=100), label='Anteil des Nettowohnbaulandes (= Summe aller\n' 'Wohnbaugrundstücke) an der Gesamtfläche der\n' 'ausgewählten Teilfläche', unit='%' ) self.params.add(Seperator()) self.params.add(Title('Durchschnittliche Wohnfläche je Wohnung')) for bt in self.gebaeudetypen_base.features(): param_name = bt.param_we feature = self.wohnflaeche.get(id_gebaeudetyp=bt.id, id_teilflaeche=self.area.id) # default value on first time value = bt.Wohnfl_m2_pro_WE if not feature \ else feature.mean_wohnflaeche self.params.add(Param( value, Slider(maximum=200), label=f'... in {bt.display_name}', unit='m²'), name=param_name ) self.params.changed.connect(self.save) self.params.show( title='Annahmen für Wohnungsdichte und Wohnflächendichte') self.save()
def setup_type(self): ''' set up basic parameters (name, type of use) ''' layout = self.ui.parameter_group.layout() clear_layout(layout) self.params = Params(layout, help_file='definitionen_flaechen.txt', ) self.params.name = Param(self.area.name, LineEdit(width=300), label='Name') self.params.add(Seperator(margin=0)) ha = round(self.area.geom.area()) / 10000 self.area.area = ha self.params.area = Param(ha, label='Größe', unit='ha') self.params.typ = Param( self.types[self.area.nutzungsart][0], ComboBox([t[0] for t in self.types], width=300), label='Nutzungsart' ) # user changed type of use def type_changed(): name = self.params.name.value type_labels = [t[0] for t in self.types] tou_id = type_labels.index(self.params.typ.value) self.area.nutzungsart = tou_id tou_label = self.types[tou_id][0] self.ui.area_combo.setItemText( self.ui.area_combo.currentIndex(), f'{name} ({tou_label})' ) self.area.name = name self.area.save() # update connector names connector = self.connectors.get(id_teilflaeche=self.area.id) connector.name_teilflaeche = self.area.name connector.save() if self.typ: self.typ.clear(self.area) self.setup_type_params() self.canvas.refreshAllLayers() Traffic.reset() self.params.changed.connect(type_changed) self.params.show(title='Teilfläche definieren')
def setup_params(self): ''' set up the parameter for setting the percentages of ground cover in status quo and the scenario ''' self.params_nullfall = Params( self.ui.param_nullfall_tab.layout(), help_file='oekologie_bodenbedeckung_nullfall.txt') self.params_planfall = Params( self.ui.param_planfall_tab.layout(), help_file='oekologie_bodenbedeckung_planfall.txt') clear_layout(self.ui.param_nullfall_tab.layout()) clear_layout(self.ui.param_planfall_tab.layout()) def apply_nf(): self.apply_drawing(False) def apply_pf(): self.apply_drawing(True) for params, prefix in [(self.params_nullfall, 'nullfall'), (self.params_planfall, 'planfall')]: planfall = prefix == 'planfall' dependency = SumDependency(100) for bb_typ in self.bb_types.features(): bb_id = bb_typ.IDBodenbedeckung feature = self.anteile.get(IDBodenbedeckung=bb_id, planfall=planfall) value = feature.anteil if feature else 0 slider = Slider(maximum=100, width=200, lockable=True) param = Param(int(value), slider, label=bb_typ.name, unit='%') dependency.add(param) params.add(param, name=f'{prefix}_{bb_id}') params.changed.connect(lambda p=prefix: self.save(p)) params.show(title='Flächenanteile der Bodenbedeckung für die ' f'Analyse: {prefix.capitalize()}') last_row = params.layout.children()[-1] button = QPushButton() button.setText('aus Zeichnung übernehmen') last_row.insertWidget(0, button) # workaround: lambda with argument didn't seem to work here (weird) #button.clicked.connect(lambda p=planfall: self.apply_drawing(p)) func = apply_pf if planfall else apply_nf button.clicked.connect(func)
def setup_ways(self): ''' set up paramaters to set ways ''' if self.ways_params: self.ways_params.close() if len(self.ways) == 0: self.calculate_ways() layout = self.ui.ways_group.layout() clear_layout(layout) self.ways_params = Params(parent=layout, button_label='Annahmen verändern', help_file='verkehr_wege.txt') for i, way in enumerate(self.ways): name = Nutzungsart(way.nutzungsart).name.capitalize() self.ways_params.add(Title(name, fontsize=8)) self.ways_params[f'{name}_gesamt'] = Param( way.wege_gesamt, SpinBox(), label='Gesamtanzahl der Wege pro Werktag (Hin- und Rückwege)') self.ways_params[f'{name}_miv'] = Param( way.miv_anteil, SpinBox(maximum=100), label='Anteil der von Pkw-Fahrenden gefahrenen Wegen', unit='%') if i != len(self.ways) - 1: self.ways_params.add(Seperator(margin=0)) def save(): for way in self.ways: name = Nutzungsart(way.nutzungsart).name.capitalize() way.miv_anteil = self.ways_params[f'{name}_miv'].value way.wege_gesamt = self.ways_params[f'{name}_gesamt'].value way.save() self.traffic_load.table.truncate() self.canvas.refreshAllLayers() self.set_status() self.ways_params.changed.connect(save) self.ways_params.show()
def setup_node_params(self, node): ''' set up the parameters of a single transfer node ''' if self.node_params: self.node_params.close() layout = self.ui.transfer_node_parameter_group.layout() clear_layout(layout) if not node: self.ui.transfer_node_parameter_group.setVisible(False) return self.ui.transfer_node_parameter_group.setVisible(True) #self.ui.transfer_node_parameter_group.setTitle(node.name) self.node_params = Params(layout, help_file='verkehr_knoten.txt') self.node_params.name = Param(node.name, LineEdit(width=300), label='Name') def save(): node.name = self.node_params.name.value #self.ui.transfer_node_parameter_group.setTitle(node.name) node.save() self.canvas.refreshAllLayers() # lazy way to update the combo box self.fill_node_combo(select=node) self.setup_weights() self.node_params.show(title='Herkunfts-/Zielpunkt bearbeiten') self.node_params.changed.connect(save) last_row = self.node_params.layout.children()[-1] button = QPushButton() icon_path = 'iconset_mob/20190619_iconset_mob_delete_1.png' icon = QIcon(os.path.join(self.project.settings.IMAGE_PATH, icon_path)) button.setText('Punkt entfernen') button.setIcon(icon) button.setToolTip( '<p><span style=" font-weight:600;">' 'Herkunfts-/Zielpunkt entfernen</span>' '</p><p>Löscht den aktuell gewählten Herkunfts-/Zielpunkt. ' '<br/>Dieser Schritt kann nicht rückgängig gemacht werden. </p>') last_row.insertWidget(0, button) button.clicked.connect(lambda: self.remove_node(node))
def setup_params(self, center): ''' set up the parameters to edit the given center ''' if self.params: self.params.close() layout = self.ui.center_parameter_group.layout() clear_layout(layout) if not center: self.ui.center_parameter_group.setVisible(False) return self.ui.center_parameter_group.setVisible(True) self.params = Params(layout, help_file='standortkonkurrenz_zentren.txt') self.params.name = Param(center.name, LineEdit(width=300), label='Name') def save(): center.name = self.params.name.value center.save() self.canvas.refreshAllLayers() # lazy way to update the combo box self.fill_combo(select=center) self.params.show(title='Zentrum bearbeiten') self.params.changed.connect(save) last_row = self.params.layout.children()[-1] button = QPushButton() icon_path = 'iconset_mob/20190619_iconset_mob_delete_1.png' icon = QIcon(os.path.join(self.project.settings.IMAGE_PATH, icon_path)) button.setText('Zentrum entfernen') button.setIcon(icon) button.setToolTip( '<p><span style=" font-weight:600;">Zentrum entfernen</span>' '</p><p>Löscht das gewählte Zentrum. </p>') last_row.insertWidget(0, button) button.clicked.connect(lambda: self.remove_center(center))
def setup_net_element(self, net_element_id): ''' set up parameters to edit the costs per element and phase ''' self.net_element_id = net_element_id ui_group = self.ui.kostenkennwerte_params_group net_element_name = self.netzelemente.get( IDNetzelement=net_element_id).Netzelement ui_group.setTitle(net_element_name) layout = ui_group.layout() clear_layout(layout) net_element = self.kostenkennwerte.get(IDNetzelement=net_element_id) self.params = Params( layout, help_file='infrastruktur_kostenkennwerte.txt') self.params.euro_EH = Param( net_element.Euro_EH, DoubleSpinBox(), unit='€', label='Kosten der erstmaligen Herstellung \n' 'pro laufenden Meter' ) self.params.euro_BU = Param( net_element.Cent_BU / 100, DoubleSpinBox(), unit='€', label='Jährliche Kosten für Betrieb und Unterhaltung \n' 'pro laufenden Meter und Jahr' ) self.params.euro_EN = Param( net_element.Euro_EN, DoubleSpinBox(), unit='€', label='Kosten der Erneuerung \n' 'pro laufenden Meter und Erneuerungszyklus' ) self.params.lebensdauer = Param( net_element.Lebensdauer, SpinBox(minimum=1, maximum=1000), label='Lebensdauer: Jahre zwischen den Erneuerungszyklen', unit='Jahr(e)' ) self.params.show() self.params.changed.connect(lambda: self.save(net_element_id))
def setup_sachwert(self): ''' asset value parameters ''' tou = self.areas.values('nutzungsart') if not self.grst_settings.is_new_bundesland\ or not Nutzungsart.WOHNEN.value in tou: self.ui.grundsteuer_sachwert_param_group.setVisible(False) return if self.sachwert_params: self.sachwert_params.close() self.ui.grundsteuer_sachwert_param_group.setVisible(True) layout = self.ui.grundsteuer_sachwert_param_group.layout() clear_layout(layout) self.sachwert_params = Params( layout, help_file='einnahmen_grundsteuer_sachwertverfahren.txt') self.sachwert_params.add(Title('Sachwertverfahren', bold=False)) self.sachwert_params.bodenwert = Param( self.grst_settings.Bodenwert_SWV / 100, DoubleSpinBox(minimum=0.3, maximum=5, step=0.05), label=f' - Bodenwert 1935 pro m²', unit='€/m²') self.sachwert_params.flaeche = Param( self.grst_settings.qm_Grundstueck_pro_WE_EFH, SpinBox(minimum=300, maximum=2000, step=1), label=f' - mittl. Größe Einfamilienhausgrundstücke', unit='m²') def save(): self.changed.emit() self.grst_settings.Bodenwert_SWV = round( self.sachwert_params.bodenwert.value * 100) self.grst_settings.qm_Grundstueck_pro_WE_EFH = \ self.sachwert_params.flaeche.value self.grst_settings.save() self.sachwert_params.show(title='Sachwertverfahren bearbeiten') self.sachwert_params.changed.connect(save)
def setup_params(self, area): ''' set the parameters according to the data of the given area ''' self.area = area clear_layout(self.layout) self.params = Params(self.layout, help_file='definitionen_einzelhandel.txt') self.params.add(Title('Verkaufsfläche')) for sortiment in self.sortimente_base.features(): feature = self.verkaufsflaechen.get(id_sortiment=sortiment.id, id_teilflaeche=self.area.id) value = feature.verkaufsflaeche_qm if feature else 0 self.params.add(Param( value, Slider(maximum=20000), label=f'{sortiment.Name_Sortiment_ProjektCheck}', unit='m²'), name=sortiment.param_vfl ) self.params.changed.connect(self.save) self.params.show( title='Einzelhandel: Bezugszeitraum und Maß der baulichen Nutzung')
class Ecology(Domain): ''' domain-widget for the analysis of the ecological impact of the planning areas ''' MAX_RATING = 10 ui_label = 'Ökologie' ui_file = 'domain_04-Oeko.ui' ui_icon = ('images/iconset_mob/' '20190619_iconset_mob_nature_conservation_2.png') layer_group = 'Wirkungsbereich 4 - Fläche und Ökologie/Ökologie' # ProjektCheck geoserver geoserver = settings.GEOSERVER_URL + '/wms?' # IÖR wms service ioer = 'https://monitor.ioer.de/cgi-bin/wms?' # ProjektCheck layers nature_layers = [ ('Naturschutzgebiete', f'url={geoserver}&layers=nsg_2017'), ('Nationalparke', f'url={geoserver}&layers=nlp2019'), ('Nationale Naturmonumente', f'url={geoserver}&layers=nnm_2019'), ('FFH-Gebiete', f'url={geoserver}&layers=ffh_de_2018'), ('RAMSAR-Gebiete', f'url={geoserver}&layers=ramsar2013'), ('Vogelschutzgebiete', f'url={geoserver}&layers=spa_de_2018') ] landscape_layers = [ ('Landschaftsschutzgebiete', f'url={geoserver}&layers=lsg_2017'), ('Biosphärenreservate', f'url={geoserver}&layers=bio2019'), ('Naturparke', f'url={geoserver}&layers=naturparke2019') ] # IÖR layers spaces_layers = [('Unzerschnittene Freiräume > 100m²', f'url={ioer}MAP=U04RG_wms&layers=U04RG_2014_100m'), ('Unzerschnittene Freiräume > 50m²', f'url={ioer}MAP=U03RG_wms&layers=U03RG_2014_100m'), ('Anteil Freiraumfläche an Gebietsfläche', f'url={ioer}MAP=F01RG_wms&layers=F01RG_2018_100m')] wood_layers = [('Unzerschnittene Wälder > 50m²', f'url={ioer}MAP=U07RG_wms&layers=U07RG_2014_100m'), ('Waldgebiete', f'url={ioer}MAP=O06RG_wms&layers=O06RG_2016_100m')] def setupUi(self): ''' set up user interactions and drawing tools ''' self.setup_layers() self.setup_drawing_tools() self.ui.toggle_drawing_button.clicked.connect(self.add_output) self.output_nullfall = None self.output_planfall = None def toggle(): for tool in self._tools: tool.set_active(False) self.add_output() self.ui.drawing_tab_widget.currentChanged.connect(toggle) self.ui.calculate_rating_button.clicked.connect(self.calculate_rating) self.ui.import_nullfall_button.clicked.connect(self.import_nullfall) for prefix in ['nullfall', 'planfall']: button = getattr(self.ui, f'{prefix}_remove_drawing_button') button.clicked.connect(lambda b, p=prefix: self.clear_drawing( planfall=p == 'planfall')) button = getattr(self.ui, f'{prefix}_apply_type_button') button.clicked.connect(lambda b, p=prefix: self.fill_area( self.get_selected_type(p), planfall=p == 'planfall')) button = getattr(self.ui, f'{prefix}_remove_type_button') button.clicked.connect(lambda b, p=prefix: self.remove_type( self.get_selected_type(p), planfall=p == 'planfall')) button = getattr(self.ui, f'{prefix}_analyse_drawing_button') button.clicked.connect( lambda b, p=prefix: self.show_drawing_analysis(planfall=p == 'planfall')) self.ui.power_lines_button.clicked.connect(self.add_power_lines) self.ui.power_lines_button.setCheckable(False) pdf_manual_path = os.path.join(self.settings.HELP_PATH, 'Anleitung_Oekologie.pdf') self.ui.manual_button.clicked.connect( lambda: open_file(pdf_manual_path)) pdf_rating_path = os.path.join( self.settings.HELP_PATH, 'Erlaeuterung_Kennwerte_Leistungsfaehigkeit_Boden.pdf') self.ui.rating_help_button.clicked.connect( lambda: open_file(pdf_rating_path)) def hide_widgets(): self.ui.toggle_drawing_button.setChecked(False) self.ui.drawing_tab_widget.setVisible(False) self.ui.rating_groupbox.collapsedStateChanged.connect(hide_widgets) hide_widgets() def add_power_lines(self): ''' add power-lines layer ''' group = (f'{self.project.groupname}/{self.layer_group}') layername = '51005_ax_leitung' url = (f'url={self.geoserver}&layers={layername}' f'&crs=EPSG:{self.settings.EPSG}' '&format=image/png&dpiMode=7&styles') layer = TileLayer(url, groupname=group) layer.draw('Hochspannungsleitungen') def load_content(self): ''' load data ''' super().load_content() areas = Teilflaechen.features() self.area = None for area in areas: if not self.area: self.area = area.geom else: self.area = self.area.combine(area.geom) self.boden_nullfall = BodenbedeckungNullfall.features(create=True) self.boden_planfall = BodenbedeckungPlanfall.features(create=True) self.anteile = BodenbedeckungAnteile.features(create=True) self.bb_types = self.basedata.get_table('Bodenbedeckung', 'Flaeche_und_Oekologie') self.faktoren = self.basedata.get_table('Faktoren', 'Flaeche_und_Oekologie') self.output_nullfall = ProjectLayer.from_table( self.boden_nullfall.table, groupname=self.layer_group, prepend=True) self.output_planfall = ProjectLayer.from_table( self.boden_planfall.table, groupname=self.layer_group, prepend=True) self.setup_params() def setup_params(self): ''' set up the parameter for setting the percentages of ground cover in status quo and the scenario ''' self.params_nullfall = Params( self.ui.param_nullfall_tab.layout(), help_file='oekologie_bodenbedeckung_nullfall.txt') self.params_planfall = Params( self.ui.param_planfall_tab.layout(), help_file='oekologie_bodenbedeckung_planfall.txt') clear_layout(self.ui.param_nullfall_tab.layout()) clear_layout(self.ui.param_planfall_tab.layout()) def apply_nf(): self.apply_drawing(False) def apply_pf(): self.apply_drawing(True) for params, prefix in [(self.params_nullfall, 'nullfall'), (self.params_planfall, 'planfall')]: planfall = prefix == 'planfall' dependency = SumDependency(100) for bb_typ in self.bb_types.features(): bb_id = bb_typ.IDBodenbedeckung feature = self.anteile.get(IDBodenbedeckung=bb_id, planfall=planfall) value = feature.anteil if feature else 0 slider = Slider(maximum=100, width=200, lockable=True) param = Param(int(value), slider, label=bb_typ.name, unit='%') dependency.add(param) params.add(param, name=f'{prefix}_{bb_id}') params.changed.connect(lambda p=prefix: self.save(p)) params.show(title='Flächenanteile der Bodenbedeckung für die ' f'Analyse: {prefix.capitalize()}') last_row = params.layout.children()[-1] button = QPushButton() button.setText('aus Zeichnung übernehmen') last_row.insertWidget(0, button) # workaround: lambda with argument didn't seem to work here (weird) #button.clicked.connect(lambda p=planfall: self.apply_drawing(p)) func = apply_pf if planfall else apply_nf button.clicked.connect(func) def setup_layers(self): ''' add ProjektCheck geoserver and IÖR layers ''' def add_layer_from_dict(layers, parent_group): for name, url in layers: self.add_wms_layer(name, url, parent_group=parent_group) self.ui.nature_button.setCheckable(False) self.ui.nature_button.clicked.connect(lambda: add_layer_from_dict( self.nature_layers, parent_group='Natur- und Artenschutz')) self.ui.landscape_button.setCheckable(False) self.ui.landscape_button.clicked.connect(lambda: add_layer_from_dict( self.landscape_layers, parent_group='Landschaftsschutz')) self.ui.spaces_100_button.setCheckable(False) name_s100, url_s100 = self.spaces_layers[0] self.ui.spaces_100_button.clicked.connect( lambda: self.add_wms_layer(name_s100, url_s100)) self.ui.spaces_50_button.setCheckable(False) name_s50, url_s50 = self.spaces_layers[1] self.ui.spaces_50_button.clicked.connect( lambda: self.add_wms_layer(name_s50, url_s50)) self.ui.spaces_button.setCheckable(False) name_s, url_s = self.spaces_layers[2] self.ui.spaces_button.clicked.connect( lambda: self.add_wms_layer(name_s, url_s)) self.ui.woods_50_button.setCheckable(False) name_w50, url_w50 = self.wood_layers[0] self.ui.woods_50_button.clicked.connect( lambda: self.add_wms_layer(name_w50, url_w50)) self.ui.woods_button.setCheckable(False) name_w, url_w = self.wood_layers[1] self.ui.woods_button.clicked.connect( lambda: self.add_wms_layer(name_w, url_w)) def setup_drawing_tools(self): ''' set up the tools for drawing the ground cover in status quo and the scenario ''' self._tools = [] self.drawing_tools = { 'draw_builtup_button': 1, 'draw_water_button': 2, 'draw_plates_button': 3, 'draw_trees_button': 4, 'draw_perennial_button': 5, 'draw_meadow_button': 6, 'draw_lawn_button': 7, 'draw_cover_button': 8, 'draw_concrete_button': 9, 'draw_field_button': 10, 'draw_paving_button': 11 } for prefix in ['nullfall', 'planfall']: is_planfall = prefix == 'planfall' for button_name, floor_id in self.drawing_tools.items(): button = getattr(self.ui, f'{prefix}_{button_name}') check = getattr(self.ui, f'{prefix}_in_area_only_check') tool = PolygonMapTool(button, canvas=self.canvas, draw_markers=True, line_style=Qt.DotLine) tool.drawn.connect( lambda geom, i=floor_id, p=is_planfall, c=check: self. add_geom(geom, i, in_area_only=c.isChecked(), planfall=p)) self._tools.append(tool) def add_geom(self, geom, typ, unite=True, in_area_only=True, difference=True, planfall=True): ''' add a geometry to the database belonging to a specific ground cover type ''' if typ is None or not geom.isGeosValid(): return if in_area_only: geom = geom.intersection(self.area) if not geom.isGeosValid(): geom = geom.makeValid() if geom.isEmpty() or geom.isNull(): return features = self.boden_planfall if planfall else self.boden_nullfall if not unite: features.add(geom=geom, IDBodenbedeckung=typ, area=geom.area()) # merge with existing geometries of same type else: ex_feat = features.get(IDBodenbedeckung=typ) if not ex_feat: features.add(geom=geom, IDBodenbedeckung=typ, area=geom.area()) else: merged = ex_feat.geom.combine(geom) if not merged.isGeosValid(): merged = merged.makeValid() merged = remove_junk(merged) # ignore geometry if it can not be merged if not merged or merged.isEmpty() or merged.isNull(): return ex_feat.geom = merged ex_feat.area = ex_feat.geom.area() ex_feat.save() # cut existing geometries of a different type at same place if difference: marked_for_deletion = [] for feature in features: if feature.IDBodenbedeckung == typ: continue difference = feature.geom.difference(geom) if not difference.isGeosValid(): difference = difference.makeValid() difference = remove_junk(difference) if (not difference or difference.isNull() or difference.isEmpty()): marked_for_deletion.append(feature) continue feature.geom = difference feature.area = difference.area() feature.save() for feature in marked_for_deletion: feature.delete() self.canvas.refreshAllLayers() if len(features) == 1: self.add_output(redraw=True) def fill_area(self, typ, planfall=True): #self.clear_drawing(planfall=planfall) self.add_geom(self.area, typ, planfall=planfall) def remove_type(self, typ, planfall=True): ''' remove all geometries of a specific ground cover type from database ''' if not typ: return features = self.boden_planfall if planfall else self.boden_nullfall # ToDo: filter would be better but messes up original filter atm for feature in features: if feature.IDBodenbedeckung == typ: feature.delete() self.canvas.refreshAllLayers() def save(self, prefix): ''' save ground cover shares set by user for status quo ('nullfall') or the scenario ('planfall') ''' planfall = prefix == 'planfall' params = self.params_planfall if planfall else self.params_nullfall for bb_typ in self.bb_types.features(): bb_id = bb_typ.IDBodenbedeckung feature = self.anteile.get(IDBodenbedeckung=bb_id, planfall=planfall) if not feature: feature = self.anteile.add(IDBodenbedeckung=bb_id) feature.planfall = planfall feature.anteil = params.get(f'{prefix}_{bb_id}').value feature.save() def import_nullfall(self): ''' import drawing of status quo into the scenario ''' if len(self.boden_planfall) > 0: reply = QMessageBox.question( self.ui, 'Nullfall in Planfall importieren', 'Achtung: die existierende Zeichnung für den Planfall ' 'wird beim Import des Nullfalls gelöscht.', QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Cancel: return self.boden_planfall.delete() for feature in self.boden_nullfall: self.boden_planfall.add(geom=feature.geom, IDBodenbedeckung=feature.IDBodenbedeckung, area=feature.geom.area()) self.add_output() def get_selected_type(self, prefix): ''' currently selected ground cover type ''' for button_name, typ in self.drawing_tools.items(): button = getattr(self.ui, f'{prefix}_{button_name}') if button.isChecked(): return typ return None def analyse_shares(self, planfall=True): ''' calculate the share per ground cover type out of the drawing ''' features = self.boden_planfall if planfall else self.boden_nullfall df = features.to_pandas() grouped = df.groupby('IDBodenbedeckung') grouped_sums = grouped['area'].sum() sum_area = df['area'].sum() shares = (grouped_sums * 100 / sum_area).round() if sum_area > 0 \ else grouped_sums return shares def apply_drawing(self, planfall=True): ''' calculate ground cover shares and write them to database ''' shares = self.analyse_shares(planfall) params = self.params_planfall if planfall else self.params_nullfall prefix = 'planfall' if planfall else 'nullfall' for bb_typ in self.bb_types.features(): bb_id = bb_typ.IDBodenbedeckung params.get(f'{prefix}_{bb_id}').value = int(shares.get(bb_id) or 0) self.save(prefix) def show_drawing_analysis(self, planfall=True): ''' open shares of ground cover in dialog with table ''' shares = self.analyse_shares(planfall) l = 'Planfall' if planfall else 'Nullfall' dialog = Dialog(title='Flächenanteile der Bodenbedeckung in ' f'der Zeichnung für den {l}') layout = QVBoxLayout() dialog.setLayout(layout) table_widget = QTableWidget() layout.addWidget(table_widget) table_widget.setColumnCount(2) table_widget.setHorizontalHeaderItem( 0, QTableWidgetItem('Bodenbedeckungstyp')) table_widget.setHorizontalHeaderItem(1, QTableWidgetItem('Anteil')) types = self.bb_types.features() table_widget.setRowCount(len(types)) for i, bb_typ in enumerate(types): share = shares.get(bb_typ.IDBodenbedeckung) or 0 table_widget.setItem(i, 0, QTableWidgetItem(bb_typ.name)) table_widget.setItem(i, 1, QTableWidgetItem(f'{share}%')) table_widget.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) table_widget.resizeColumnsToContents() dialog.showEvent = lambda e: dialog.adjustSize() dialog.show() def clear_drawing(self, planfall=True): ''' remove all drawn elements ''' l = 'Planfall' if planfall else 'Nullfall' reply = QMessageBox.question( self.ui, 'Zeichnung löschen', f'Sollen alle gezeichneten Flächen für den {l} entfernt werden?', QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.No: return features = self.boden_planfall if planfall else self.boden_nullfall output = self.output_planfall if planfall \ else self.output_nullfall layer = output.layer # remove selection, so that qgis is free to remove them from canvas layer.removeSelection() features.delete() self.canvas.refreshAllLayers() def add_output(self, redraw=False): ''' add drawings as layer ''' planfall = self.ui.drawing_tab_widget.currentIndex() == 1 label = 'Bodenbedeckung ' label += 'Planfall' if planfall else 'Nullfall' output = self.output_planfall if planfall else self.output_nullfall style = 'flaeche_oekologie_bodenbedeckung_planfall.qml' if planfall \ else 'flaeche_oekologie_bodenbedeckung_nullfall.qml' output.draw(label=label, style_file=style, redraw=redraw) setattr(self, 'output_planfall' if planfall else 'output_nullfall', output) disabled_out = self.output_nullfall if planfall \ else self.output_planfall if disabled_out: disabled_out.set_visibility(False) def add_wms_layer(self, name, url, parent_group=None): ''' add a wms layer ''' group = (f'{self.project.groupname}/{self.layer_group}') if parent_group: group += f'/{parent_group}' url = (f'{url}&crs=EPSG:{self.settings.EPSG}' '&format=image/png&dpiMode=7&styles') layer = TileLayer(url, groupname=group) layer.draw(name, toggle_if_exists=True) def calculate_rating(self): ''' calculate the ratings based on the ground covers for status quo and the scenario. Plot the results in diagrams ''' df_factors = self.faktoren.to_pandas() df_shares = self.anteile.to_pandas() df_merged = df_shares.merge(df_factors, on='IDBodenbedeckung') def rating(df, columns): df_rating = df.multiply(df['anteil'] / 100, axis='index') df_rating = df_rating[columns] df_rating = df_rating.sum(axis=0) rating = df_rating * self.MAX_RATING ## divide the domain (0..1) into n + 1 bins ## -> n is the max. rating value #bins = np.linspace(0, 1, self.MAX_RATING+1) #rating = np.digitize(df_rating, bins) - 1 return rating.round(1) columns = df_factors.columns[3:] df_merged_nf = df_merged[df_merged['planfall'] == False] df_merged_pf = df_merged[df_merged['planfall'] == True] rating_nf = rating(df_merged_nf, columns) rating_pf = rating(df_merged_pf, columns) rating_delta = rating_pf - rating_nf categories = [c.split('_')[0].capitalize() for c in columns] columns = [c.split('_')[1] for c in columns] columns = [ c.replace('ae', 'ä').replace('ue', 'ü').replace('oe', 'ö').capitalize() for c in columns ] diagram = Leistungskennwerte( nullfall=rating_nf, planfall=rating_pf, columns=columns, categories=categories, title='Leistungskennwerte im Nullfall und Planfall', max_rating=self.MAX_RATING) diagram.draw() diagram = LeistungskennwerteDelta( delta=rating_delta, columns=columns, categories=categories, title='Beeinträchtigung durch Planungsvorhaben (= Veränderung der ' '\nLeistungskennwerte im Planfall gegenüber dem Nullfall)', max_rating=self.MAX_RATING) diagram.draw(offset_x=100, offset_y=100) def close(self): ''' close parameters and drawing tools ''' if hasattr(self, 'params_nullfall'): self.params_nullfall.close() if hasattr(self, 'params_planfall'): self.params_planfall.close() for tool in self._tools: tool.set_active(False) super().close()
def setup_params(self, market): ''' set up the parameters to edit the given scenario market ''' # ToDo: that's mostly the same as in EditNullfallMarkets, # might be merged if self.params: self.params.close() layout = self.param_group.layout() clear_layout(layout) if not market: self.param_group.setVisible(False) return self.param_group.setVisible(True) self.params = Params( layout, help_file='standortkonkurrenz_geplante_maerkte.txt') self.params.name = Param(market.name, LineEdit(width=300), label='Name') self.params.add(Seperator(margin=0)) # 'nicht aufgeführt' (kette 0) is first, rest alphabetical order ketten = sorted(self.ketten, key=lambda k: k.name.lower() if k.name != 'nicht aufgeführt' else '') chain_ids = [typ.id_kette for typ in ketten] chain_labels = [kette.name for kette in ketten] chain_combo = ComboBox(chain_labels, data=chain_ids, width=300) value = self.ketten.get(id_kette=market.id_kette).name self.params.kette = Param(value, chain_combo, label='Anbieter') type_ids = [typ.id_betriebstyp for typ in self.typen] type_labels = [self.detailed_type_label(i) for i in type_ids if i > 0] type_combo = ComboBox(type_labels, data=type_ids, width=300) self.params.typ = Param(self.detailed_type_label( market.id_betriebstyp_planfall), type_combo, label='Neue Märkte', value_label=market.betriebstyp_planfall) def save(): market.name = self.params.name.value id_bt = type_combo.get_data() bt = self.typen.get(id_betriebstyp=id_bt).name market.id_betriebstyp_planfall = id_bt market.betriebstyp_planfall = bt market.id_kette = chain_combo.get_data() market.kette = self.ketten.get(id_kette=market.id_kette).name vkfl = self.market_tool.betriebstyp_to_vkfl( market.id_betriebstyp_planfall, market.id_kette) market.vkfl_planfall = vkfl market.save() self.canvas.refreshAllLayers() # lazy way to update the combo box self.fill_combo(select=market) self.changed.emit() self.params.show(title='Neuen Markt im Planfall bearbeiten') self.params.changed.connect(save) # markets on project areas can not be deleted if market.id_teilflaeche < 0: last_row = self.params.layout.children()[-1] button = QPushButton() icon_path = 'iconset_mob/20190619_iconset_mob_delete_1.png' icon = QIcon( os.path.join(self.project.settings.IMAGE_PATH, icon_path)) button.setText('Markt entfernen') button.setIcon(icon) button.setToolTip( '<p><span style=" font-weight:600;">Markt entfernen</span>' '</p><p>Löscht den aktuell gewählten Markt. ' '<br/>Dieser Schritt kann nicht rückgängig gemacht ' 'werden. </p>') last_row.insertWidget(0, button) button.clicked.connect(lambda: self.remove_market(market))
class EinwohnerMigration(Migration): ''' migration of inhabitants ''' def __init__(self, project, ui, layer_group, canvas): super().__init__(project, ui, layer_group, canvas) self.ui.migration_inhabitants_button.clicked.connect(self.calculate) def load_content(self): super().load_content() self.wanderung = EinwohnerWanderung.features(create=True) self.ui.einwohner_parameter_group.setVisible(False) if len(self.wanderung) == 0: self.ui.recalculate_inhabitants_check.setChecked(True) self.ui.recalculate_inhabitants_check.setVisible(False) self.ui.einwohner_parameter_group.setVisible(False) else: self.setup_params() def calculate(self): ''' calculate the migration ''' if not self.ui.recalculate_inhabitants_check.isChecked(): self.add_layer(toggle_if_exists=True) return sum_ew = sum(x or 0 for x in self.areas.values('ew')) if sum_ew == 0: QMessageBox.warning( self.ui, 'Fehler', 'Es wurden keine definierten Teilflächen mit ' 'der Nutzungsart "Wohnen" gefunden.') return job = EwMigrationCalculation(self.project) def on_close(): self.changed.emit() if not self.dialog.success: self.wanderung.table.truncate() self.ui.recalculate_inhabitants_check.setVisible(False) self.ui.recalculate_inhabitants_check.setChecked(True) return self.ui.recalculate_inhabitants_check.setVisible(True) self.ui.recalculate_inhabitants_check.setChecked(False) self.add_layer() self.setup_params() self.dialog = ProgressDialog(job, parent=self.ui, on_close=on_close) self.dialog.show() def setup_params(self): ''' set up migration settings for each muncipality in study area ''' if self.params: self.params.close() self.ui.einwohner_parameter_group.setVisible(True) layout = self.ui.einwohner_parameter_group.layout() clear_layout(layout) self.params = Params(layout, help_file='einnahmen_einwohner_wanderung.txt') self.df_wanderung = self.wanderung.to_pandas() randsummen = self.project.basedata.get_table('Wanderung_Randsummen', 'Einnahmen').features() factor_inner = randsummen.get(IDWanderungstyp=1).Anteil_Wohnen factor_outer = randsummen.get(IDWanderungstyp=2).Anteil_Wohnen project_ags = self.project_frame.ags project_gem = self.gemeinden.get(AGS=project_ags) wanderung = self.wanderung.get(AGS=project_ags) sum_ew = sum(x or 0 for x in self.areas.values('ew')) def update_salden(ags_changed): param = self.params[ags_changed] fixed = param.is_locked saldo = param.input.value idx = self.df_wanderung['AGS'] == ags_changed if fixed: fixed_fortzug = self.df_wanderung[np.invert(idx) & self.df_wanderung['fixed'] == True]['fortzug'].sum() # the rest of "fortzüge" that can be applied to this row min_value = fixed_fortzug - (sum_ew * factor_inner) saldo = max(saldo, min_value) zuzug = self.df_wanderung[idx]['zuzug'].values[0] self.df_wanderung.loc[idx, 'fortzug'] = zuzug - saldo self.df_wanderung.loc[idx, 'fixed'] = fixed self.df_wanderung.loc[idx, 'saldo'] = saldo self.df_wanderung = MigrationCalculation.calculate_saldi( self.df_wanderung, factor_inner, project_ags) for gemeinde_ags in self.df_wanderung['AGS'].values: param = self.params[gemeinde_ags] row = self.df_wanderung[self.df_wanderung['AGS'] == gemeinde_ags] param.input.blockSignals(True) param.input.value = row['saldo'].values[0] param.input.blockSignals(False) self.params.add(Title('Standortgemeinde des Projekts', bold=False)) spinbox = DoubleSpinBox(minimum=0, maximum=1000, step=1, lockable=True, locked=wanderung.fixed, reversed_lock=True) project_saldo = Param(wanderung.saldo, spinbox, repr_format='%+.2f', label=f' - {project_gem.GEN}', unit='Ew') self.params.add(project_saldo, name=project_ags) spinbox.changed.connect(lambda o: update_salden(project_ags)) spinbox.locked.connect(lambda o: update_salden(project_ags)) self.params.add(Seperator()) self.params.add(Title('Region um Standortgemeinde', bold=False)) for gemeinde in sorted(self.gemeinden, key=lambda x: x.GEN): ags = gemeinde.AGS if ags == project_ags: continue wanderung = self.wanderung.get(AGS=ags) if not wanderung: continue spinbox = DoubleSpinBox(minimum=-1000, maximum=0, step=1, lockable=True, locked=wanderung.fixed, reversed_lock=True) param = Param(wanderung.saldo, spinbox, label=f' - {gemeinde.GEN}', unit='Ew') self.params.add(param, name=ags) spinbox.changed.connect(lambda o, a=ags: update_salden(a)) spinbox.locked.connect(lambda o, a=ags: update_salden(a)) self.params.add(Seperator()) self.params.add( Param(-factor_outer * sum_ew, label='Restliches Bundesgebiet / Ausland', unit='Ew')) def save(): self.wanderung.update_pandas(self.df_wanderung) self.changed.emit() self.canvas.refreshAllLayers() self.params.show(title='Geschätzte Salden (Einwohner) bearbeiten', scrollable=True) self.params.changed.connect(save) def add_layer(self, toggle_if_exists=False): ''' show layer with migration of inhabitants ''' self.output = ProjectLayer.from_table(self.wanderung.table, groupname=self.layer_group) self.output.draw(label='Wanderungssalden Einwohner', style_file='einnahmen_einwohnerwanderung.qml', uncheck_siblings=True, redraw=not toggle_if_exists, toggle_if_exists=toggle_if_exists) if self.output.tree_layer.isVisible(): self.output.zoom_to()
class ChangeMarkets(EditMarkets): ''' control markets already existing in status quo but are changed in the scenario ''' layer_filter = ('id_betriebstyp_nullfall != id_betriebstyp_planfall ' 'and id_betriebstyp_nullfall > 0') layer_style = 'standortkonkurrenz_veraenderte_maerkte.qml' filter_args = {'id_betriebstyp_nullfall__gt': 0} market_label = 'Veränderte Märkte im Bestand' suffix = 'nullfall' show_change = True def __init__(self, nullfall_edit, combobox, select_button, param_group, canvas, project, remove_button=None, layer_group=''): super().__init__(combobox, select_button, param_group, canvas, project, remove_button=remove_button, layer_group=layer_group) self.nullfall_edit = nullfall_edit def add_layer(self, zoom_to=False, toggle_if_exists=False): ''' add the nullfall layer in addition to layer showing the changed markets ''' super().add_layer(toggle_if_exists=toggle_if_exists) self.nullfall_edit.add_layer(zoom_to=zoom_to, toggle_if_exists=toggle_if_exists) self.select_tool.set_layer(self.nullfall_edit.output.layer) def setup_params(self, market): ''' set up the parameters to change attributes of the given status quo market in the scenario ''' if self.params: self.params.close() layout = self.param_group.layout() clear_layout(layout) if not market: self.param_group.setVisible(False) return self.param_group.setVisible(True) self.params = Params( layout, help_file='standortkonkurrenz_veraenderte_maerkte.txt') self.params.name = Param(market.name, label='Name') self.params.add(Seperator(margin=0)) self.params.kette = Param(market.kette, label='Anbieter') self.params.nullfall = Param( market.betriebstyp_nullfall, label='Betriebstyp im Nullfall', ) closed_label = 'Markt geschlossen' type_ids = [typ.id_betriebstyp for typ in self.typen] type_labels = [] for tid in type_ids: type_labels.append(closed_label if tid == 0 else self.detailed_type_label(tid)) type_combo = ComboBox(type_labels, data=type_ids, width=300) typ = market.id_betriebstyp_planfall self.params.planfall = Param( closed_label if typ == 0 else self.detailed_type_label(typ), type_combo, label='Betriebstyp im Planfall', value_label=closed_label if typ == 0 else market.betriebstyp_planfall) close_check = Checkbox() self.params.gets_closed = Param(typ == 0, close_check, label='Markt im Planfall schließen') self.params.gets_closed.hide_in_overview = True def closed_toggled(checked): if checked: type_combo.set_value(closed_label) close_check.changed.connect(closed_toggled) def type_changed(value): close_check.set_value(value == closed_label) type_combo.changed.connect(type_changed) def save(): id_bt = type_combo.get_data() bt = self.typen.get(id_betriebstyp=id_bt).name market.id_betriebstyp_planfall = id_bt market.betriebstyp_planfall = bt vkfl = self.market_tool.betriebstyp_to_vkfl( market.id_betriebstyp_planfall, market.id_kette) market.vkfl_planfall = vkfl market.save() self.canvas.refreshAllLayers() # lazy way to update the combo box self.fill_combo(select=market) self.changed.emit() self.params.show(title='Markt im Planfall verändern') self.params.changed.connect(save) def remove_markets(self): ''' reset all changes made to status quo markets in the scenario ''' reply = QMessageBox.question( self.param_group, f'Veränderungen zurücksetzen', 'Möchten Sie alle Veränderungen der bestehenden Märkte ' 'zurücksetzen?', QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: for market in self.markets.filter(**self.filter_args): market.id_betriebstyp_planfall = market.id_betriebstyp_nullfall market.betriebstyp_planfall = market.betriebstyp_nullfall market.save() self.canvas.refreshAllLayers() self.fill_combo()
class Gewerbesteuer(QObject): ''' parameters and calculation of business tax ''' changed = pyqtSignal() def __init__(self, project, ui, layer_group): super().__init__() self.layer_group = layer_group self.project = project self.ui = ui self.ui.calc_gewerbesteuer_button.clicked.connect(self.calculate) self.params = None def load_content(self): self.gemeinden = Gemeindebilanzen.features(project=self.project) self.setup_params() def setup_params(self): if self.params: self.params.close() layout = self.ui.gewerbesteuer_hebesatz_param_group.layout() clear_layout(layout) self.params = Params(layout, help_file='einnahmen_gewerbesteuer_hebesätze.txt') self.params.add(Title('Hebesätze', bold=False)) for gemeinde in sorted(self.gemeinden, key=lambda x: x.GEN): spinbox = SpinBox(minimum=0, maximum=999, step=1) param = Param(gemeinde.Hebesatz_GewSt, spinbox, label=f' - {gemeinde.GEN}', unit='v.H.') self.params.add(param, name=gemeinde.AGS) def save(): self.changed.emit() for gemeinde in self.gemeinden: param = self.params[gemeinde.AGS] gemeinde.Hebesatz_GewSt = param.value gemeinde.save() self.params.show(title='Hebesätze Gewerbesteuer bearbeiten', scrollable=True) self.params.changed.connect(save) def calculate(self): ''' calculate the business tax ''' if len(BeschaeftigtenWanderung.features()) == 0: QMessageBox.warning( self.ui, 'Fehler', 'Bitte führen Sie zunächst die Schätzung der Wanderungssalden ' '(Beschäftigte) durch.') return job = GewerbesteuerCalculation(self.project) def on_success(r): self.add_layer() self.changed.emit() self.dialog = ProgressDialog(job, parent=self.ui, on_success=on_success, auto_close=True) self.dialog.show() def add_layer(self): ''' show business tax layer ''' self.output = ProjectLayer.from_table(self.gemeinden.table, groupname=self.layer_group) self.output.draw(label='Gewerbesteuer', style_file='einnahmen_gewerbesteuer.qml', filter="gewerbesteuer != 'NULL'", uncheck_siblings=True, redraw=False) self.output.zoom_to() def close(self): if self.params: self.params.close()
def setup_params(self, market): ''' set up the parameters to change attributes of the given status quo market in the scenario ''' if self.params: self.params.close() layout = self.param_group.layout() clear_layout(layout) if not market: self.param_group.setVisible(False) return self.param_group.setVisible(True) self.params = Params( layout, help_file='standortkonkurrenz_veraenderte_maerkte.txt') self.params.name = Param(market.name, label='Name') self.params.add(Seperator(margin=0)) self.params.kette = Param(market.kette, label='Anbieter') self.params.nullfall = Param( market.betriebstyp_nullfall, label='Betriebstyp im Nullfall', ) closed_label = 'Markt geschlossen' type_ids = [typ.id_betriebstyp for typ in self.typen] type_labels = [] for tid in type_ids: type_labels.append(closed_label if tid == 0 else self.detailed_type_label(tid)) type_combo = ComboBox(type_labels, data=type_ids, width=300) typ = market.id_betriebstyp_planfall self.params.planfall = Param( closed_label if typ == 0 else self.detailed_type_label(typ), type_combo, label='Betriebstyp im Planfall', value_label=closed_label if typ == 0 else market.betriebstyp_planfall) close_check = Checkbox() self.params.gets_closed = Param(typ == 0, close_check, label='Markt im Planfall schließen') self.params.gets_closed.hide_in_overview = True def closed_toggled(checked): if checked: type_combo.set_value(closed_label) close_check.changed.connect(closed_toggled) def type_changed(value): close_check.set_value(value == closed_label) type_combo.changed.connect(type_changed) def save(): id_bt = type_combo.get_data() bt = self.typen.get(id_betriebstyp=id_bt).name market.id_betriebstyp_planfall = id_bt market.betriebstyp_planfall = bt vkfl = self.market_tool.betriebstyp_to_vkfl( market.id_betriebstyp_planfall, market.id_kette) market.vkfl_planfall = vkfl market.save() self.canvas.refreshAllLayers() # lazy way to update the combo box self.fill_combo(select=market) self.changed.emit() self.params.show(title='Markt im Planfall verändern') self.params.changed.connect(save)
class EditCenters: ''' controls centers drawn by users, centers will be evaluated seperately during the calculation of the "Projektwirkung" ''' def __init__(self, ui, canvas, project, layer_group=''): self.ui = ui self.canvas = canvas self.project = project self.drawing_tool = None self.params = None self.output = None self.select_tool = None self.project = project self.layer_group = layer_group def setupUi(self): self.drawing_tool = PolygonMapTool(self.ui.draw_center_button, canvas=self.canvas) self.drawing_tool.drawn.connect(self.add_center) self.ui.draw_center_button.clicked.connect(self.add_layer) self.ui.centers_combo.currentIndexChanged.connect( lambda idx: self.toggle_center(self.ui.centers_combo.currentData(), center_on_point=True)) self.select_tool = FeaturePicker(self.ui.select_center_button, canvas=self.canvas) self.select_tool.feature_picked.connect(self.select_center) self.ui.select_center_button.clicked.connect(lambda: self.add_layer()) self.ui.center_parameter_group.setVisible(False) def load_content(self): self.centers = Centers.features() self.fill_combo() def fill_combo(self, select=None): ''' fill the center combobox with all drawn centers ''' self.ui.centers_combo.blockSignals(True) self.ui.centers_combo.clear() self.ui.centers_combo.addItem('nichts ausgewählt') idx = 0 centers = self.centers.filter(nutzerdefiniert=1) for i, center in enumerate(centers): self.ui.centers_combo.addItem(center.name, center) if select and center.id == select.id: idx = i + 1 if idx: self.ui.centers_combo.setCurrentIndex(idx) self.ui.centers_combo.blockSignals(False) self.toggle_center(self.ui.centers_combo.currentData()) def add_center(self, geom): ''' add a new center with default name and given geometry ''' center = self.centers.add(nutzerdefiniert=1, name='unbenanntes Zentrum', geom=geom) self.canvas.refreshAllLayers() self.fill_combo(select=center) def toggle_center(self, center, center_on_point=False): ''' change active center ''' if self.output and self.output.layer and center: self.output.layer.removeSelection() self.output.layer.select(center.id) if center_on_point: center_canvas(self.canvas, center.geom.centroid().asPoint(), self.output.layer.crs()) self.setup_params(center) def add_layer(self, toggle_if_exists=False): ''' show the centers in a layer ''' self.output = ProjectLayer.from_table(self.centers.table, groupname=self.layer_group) self.output.draw(label='Zentren', style_file='standortkonkurrenz_zentren.qml', filter='nutzerdefiniert=1', redraw=not toggle_if_exists, toggle_if_exists=toggle_if_exists) self.select_tool.set_layer(self.output.layer) def setup_params(self, center): ''' set up the parameters to edit the given center ''' if self.params: self.params.close() layout = self.ui.center_parameter_group.layout() clear_layout(layout) if not center: self.ui.center_parameter_group.setVisible(False) return self.ui.center_parameter_group.setVisible(True) self.params = Params(layout, help_file='standortkonkurrenz_zentren.txt') self.params.name = Param(center.name, LineEdit(width=300), label='Name') def save(): center.name = self.params.name.value center.save() self.canvas.refreshAllLayers() # lazy way to update the combo box self.fill_combo(select=center) self.params.show(title='Zentrum bearbeiten') self.params.changed.connect(save) last_row = self.params.layout.children()[-1] button = QPushButton() icon_path = 'iconset_mob/20190619_iconset_mob_delete_1.png' icon = QIcon(os.path.join(self.project.settings.IMAGE_PATH, icon_path)) button.setText('Zentrum entfernen') button.setIcon(icon) button.setToolTip( '<p><span style=" font-weight:600;">Zentrum entfernen</span>' '</p><p>Löscht das gewählte Zentrum. </p>') last_row.insertWidget(0, button) button.clicked.connect(lambda: self.remove_center(center)) def remove_center(self, center): ''' remove the given center from the database ''' if not center: return reply = QMessageBox.question( self.ui, 'Zentrum entfernen', f'Soll das Zentrum "{center.name}" entfernt werden?\n', QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: center.delete() self.canvas.refreshAllLayers() self.fill_combo() def select_center(self, feature): ''' select and highlight given center ''' if not self.output or not self.output.layer: return self.output.layer.removeSelection() self.output.layer.select(feature.id()) fid = feature.id() for idx in range(len(self.ui.centers_combo)): center = self.ui.centers_combo.itemData(idx) if center and fid == center.id: break self.ui.centers_combo.setCurrentIndex(idx) def close(self): ''' close tools and parameters ''' if self.drawing_tool: self.drawing_tool.set_active(False) if self.select_tool: self.select_tool.set_active(False) if self.params: self.params.close()
class LandUse(Domain): ''' domain-widget calculating and visualizing the residential density and the integrated position of the planning area ''' ui_label = 'Flächeninanspruchnahme' ui_file = 'domain_05-Fl.ui' ui_icon = "images/iconset_mob/20190619_iconset_mob_domain_landuse_1.png" layer_group = ('Wirkungsbereich 4 - Fläche und Ökologie/' 'Flächeninanspruchnahme') def setupUi(self): self.ui.area_combo.currentIndexChanged.connect( lambda: self.change_area()) self.layout = self.ui.parameter_group.layout() self.ui.calculate_density_button.clicked.connect( self.calculate_wohndichte) self.ui.calculate_areadensity_button.clicked.connect( self.calculate_wohnflaechendichte) self.ui.calculate_integration_button.clicked.connect( self.calculate_integration) self.bordertool = LineMapTool( self.ui.draw_border_button, canvas=self.canvas, line_width=3)# , color='#33ccff') self.bordertool.drawn.connect(self.add_border) self.ui.remove_drawing_button.clicked.connect(self.remove_borders) pdf_path = os.path.join( self.settings.HELP_PATH, 'Anleitung_Flaecheninanspruchnahme.pdf') self.ui.manual_button.clicked.connect(lambda: open_file(pdf_path)) def load_content(self): super().load_content() self.gebaeudetypen_base = self.basedata.get_table( 'Wohnen_Gebaeudetypen', 'Definition_Projekt' ) self.areas = Teilflaechen.features() self.residential_areas = Teilflaechen.features().filter( nutzungsart=Nutzungsart.WOHNEN.value) self.wohnbauland_anteile = WohnbaulandAnteile.features(create=True) self.wohnflaeche = WohnflaecheGebaeudetyp.features(create=True) self.borders = GrenzeSiedlungskoerper.features(create=True) self.wohneinheiten = Wohneinheiten.features(create=True) self.rahmendaten = Projektrahmendaten.features()[0] self.wohndichte_kreis = self.basedata.get_table( 'Wohndichte_Wohnflaechendichte_Kreise', 'Flaeche_und_Oekologie') self.wohndichte_raumtyp = self.basedata.get_table( 'Wohndichte_Wohnflaechendichte_RaumTypen', 'Flaeche_und_Oekologie') self.raumtypen = self.basedata.get_table( 'RaumTypen', 'Flaeche_und_Oekologie') self.ui.area_combo.blockSignals(True) self.ui.area_combo.clear() for area in self.residential_areas: self.ui.area_combo.addItem(area.name, area) self.ui.area_combo.blockSignals(False) self.add_border_output() self.change_area() self.area_union = None # ToDo: fix filter side effects self.areas.filter() for area in self.areas: self.area_union = area.geom if not self.area_union \ else self.area_union.combine(area.geom) # buffer to fill gaps self.area_union = self.area_union.buffer(0.1, 6) self.bordertool.set_snap_geometry(self.area_union) def setup_params(self): ''' set up the parameters for editing the mean residential area per appartment and the net share of residential building land in the active area ''' anteil = self.wohnbauland_anteile.get(id_teilflaeche=self.area.id) value = anteil.nettoflaeche if anteil else 85 clear_layout(self.layout) self.params = Params( self.layout, help_file='flaecheninanspruchnahme_wohnbauland_wohnflaeche.txt' ) self.params.add(Title('Anteil Nettowohnbauland')) self.params.nettoflaeche = Param( int(value), Slider(maximum=100), label='Anteil des Nettowohnbaulandes (= Summe aller\n' 'Wohnbaugrundstücke) an der Gesamtfläche der\n' 'ausgewählten Teilfläche', unit='%' ) self.params.add(Seperator()) self.params.add(Title('Durchschnittliche Wohnfläche je Wohnung')) for bt in self.gebaeudetypen_base.features(): param_name = bt.param_we feature = self.wohnflaeche.get(id_gebaeudetyp=bt.id, id_teilflaeche=self.area.id) # default value on first time value = bt.Wohnfl_m2_pro_WE if not feature \ else feature.mean_wohnflaeche self.params.add(Param( value, Slider(maximum=200), label=f'... in {bt.display_name}', unit='m²'), name=param_name ) self.params.changed.connect(self.save) self.params.show( title='Annahmen für Wohnungsdichte und Wohnflächendichte') self.save() def save(self): ''' save the current settings of the parameters ''' feature = self.wohnbauland_anteile.get(id_teilflaeche=self.area.id) # ToDo: get_or_create if not feature: feature = self.wohnbauland_anteile.add(id_teilflaeche=self.area.id) feature.nettoflaeche = self.params.nettoflaeche.value feature.save() for bt in self.gebaeudetypen_base.features(): feature = self.wohnflaeche.get(id_gebaeudetyp=bt.id, id_teilflaeche=self.area.id) if not feature: feature = self.wohnflaeche.add( id_gebaeudetyp=bt.id, id_teilflaeche=self.area.id) feature.mean_wohnflaeche = getattr( self.params, bt.param_we).value feature.save() def change_area(self): ''' set currently selected area as active area ''' self.area = self.ui.area_combo.itemData( self.ui.area_combo.currentIndex()) if not self.area: return output = ProjectLayer.find('Umriss des Plangebiets') if output: layer = output[0].layer() layer.removeSelection() layer.select(self.area.id) self.setup_params() def calculate_wohndichte(self): ''' calculate residential density and show chart of results ''' if not self.area: return # calculation for area anteil = self.wohnbauland_anteile.get(id_teilflaeche=self.area.id) netto_wb = (self.area.geom.area() / 10000) * (anteil.nettoflaeche / 100) wohndichte = round(self.area.we_gesamt / netto_wb, 1) \ if netto_wb > 0 else 0 # get data to compare to kreis, kreisname, kreistyp, typname = self.get_kreis_data() wohndichte_kreis = kreis.Wohndichte_WE_pro_ha_Nettowohnbauland wohndichte_kreistyp = kreistyp.Wohndichte_WE_pro_ha_Nettowohnbauland # chart values = [wohndichte, wohndichte_kreis, wohndichte_kreistyp] labels = [f'Teilfläche "{self.area.name}"', f'Kreis "{kreisname}"', typname] colors = ['#70ad47', '#385723', '#385723'] custom_legend={ f'Teilfläche "{self.area.name}"': '#70ad47', 'Vergleichswerte': '#385723' } chart = BarChart( values, labels=labels, title=f'Teilfläche "{self.area.name}": ' 'Wohneinheiten pro Hektar Nettowohnbauland', y_label='Wohneinheiten pro Hektar Nettowohnbauland', colors=colors, custom_legend=custom_legend ) chart.draw() def calculate_wohnflaechendichte(self): ''' calculate density of living areas and show results as chart ''' if not self.area: return # calculation for area anteil = self.wohnbauland_anteile.get(id_teilflaeche=self.area.id) wohneinheiten = self.wohneinheiten.filter(id_teilflaeche=self.area.id) wohnflaeche = self.wohnflaeche.filter(id_teilflaeche=self.area.id) df_wohneinheiten = wohneinheiten.to_pandas() df_wohnflaeche = wohnflaeche.to_pandas() df_merged = df_wohneinheiten.merge(df_wohnflaeche, on='id_gebaeudetyp') wohnflaeche_gesamt = (df_merged['mean_wohnflaeche'] * df_merged['we']).sum() netto_wb = (self.area.geom.area() / 10000) * (anteil.nettoflaeche / 100) wohnflaechendichte = round(wohnflaeche_gesamt / netto_wb)\ if netto_wb > 0 else 0 # get data to compare to kreis, kreisname, kreistyp, typname = self.get_kreis_data() wohndichte_kreis = \ round(kreis.Wohnflaechendichte_qm_Wohnfl_pro_ha_Nettowohnbauland) wohndichte_kreistyp = \ round(kreistyp.Wohnflaechendichte_qm_Wohnfl_pro_ha_Nettowohnbauland) # chart values = [wohnflaechendichte, wohndichte_kreis, wohndichte_kreistyp] values = [round(v) for v in values] labels = [f'Teilfläche "{self.area.name}"', f'Kreis {kreisname}', typname] colors = ['#70ad47', '#385723', '#385723'] custom_legend={ f'Teilfläche "{self.area.name}"': '#70ad47', 'Vergleichswerte': '#385723' } chart = BarChart( values, labels=labels, title=f'Teilfläche "{self.area.name}": ' 'Wohnfläche (m²) pro Hektar Nettowohnbauland', colors=colors, custom_legend=custom_legend, y_label='Quadratmeter Wohnfläche pro Hektar Nettowohnbauland' ) chart.draw() def get_kreis_data(self) -> tuple: ''' get comparable data of muncipality of planning area ''' ags5 = str(self.rahmendaten.ags)[0:5] kreis = self.wohndichte_kreis.features().get(AGS5=ags5) kreistyp_id = kreis.Siedlungsstruktureller_Kreistyp kreistyp = self.wohndichte_raumtyp.features().get( Siedlungsstruktureller_Kreistyp=kreistyp_id) kreisname = kreis.Kreis_kreisfreie_Stadt.split(',')[0] typname = self.raumtypen.features().get(ID=kreistyp_id).Name return kreis, kreisname, kreistyp, typname def add_border(self, geom): ''' add a geometry to the drawn border ''' self.borders.add(geom=geom) # workaround: layer style is not applied correctly # with empty features -> redraw on first geometry if len(self.borders) == 1: self.add_border_output() self.canvas.refreshAllLayers() def remove_borders(self): ''' remove drawn border ''' self.borders.delete() self.canvas.refreshAllLayers() def add_border_output(self): ''' add layer to visualize drawn border ''' self.output_border = ProjectLayer.from_table( self.borders.table, groupname=self.layer_group, prepend=True) self.output_border.draw( label='Grenze Siedlunskörper', style_file='flaeche_oekologie_grenze_siedlungskoerper.qml' ) def calculate_integration(self): ''' calculate integration shares from drawing and show results in a pie chart ''' area_outer_border = self.area_union.length() drawn_borders = sum([line.geom.length() for line in self.borders]) shared_border = round(100 * drawn_borders / area_outer_border) shared_border = min(shared_border, 100) values = [shared_border, 100 - shared_border] labels = ['Anteil der Plangebietsgrenze,\n' 'die an bestehende Siedlungs-\n' 'flächen angrenzt', 'Anteil der Plangebietsgrenze,\n' 'die nicht an bestehende\n' 'Siedlungsflächen angrenzt',] chart = PieChart(values, labels=labels, title=f'{self.project.name}: Lage zu bestehenden ' 'Siedlungsflächen', decimals=0) chart.draw() def close(self): ''' close parameters and drawing tools ''' # ToDo: implement this in project (collecting all used workscpaces) output = ProjectLayer.find('Umriss des Plangebiets') if output: layer = output[0].layer() layer.removeSelection() if hasattr(self, 'params'): self.params.close() self.bordertool.set_active(False) super().close()
class Grundsteuer(QObject): ''' parameters and calculation of property tax ''' changed = pyqtSignal() def __init__(self, project, ui, layer_group): super().__init__() self.project = project self.ui = ui self.layer_group = layer_group self.ui.calc_grundsteuer_button.clicked.connect(self.calculate) self.hebesatz_params = None self.rohmiete_params = None self.sachwert_params = None self.bauvolumen_params = None def load_content(self): self.project_frame = Projektrahmendaten.features( project=self.project)[0] self.gemeinden = Gemeindebilanzen.features(project=self.project) self.grst_settings = GrundsteuerSettings.features(create=True) if len(self.grst_settings) == 0: self.init_grst_base_settings() self.grst_settings = self.grst_settings[0] self.areas = Teilflaechen.features(project=self.project) self.ew_wanderung = EinwohnerWanderung.features() self.svb_wanderung = BeschaeftigtenWanderung.features() self.setup_hebesatz() self.setup_rohmiete() self.setup_sachwert() self.setup_bauvolumen() def init_grst_base_settings(self): ''' initialize the parameters of the base settings ''' gemeinden = self.project.basedata.get_table( 'bkg_gemeinden', 'Basisdaten_deutschland').features() gem = gemeinden.get(AGS=self.project_frame.ags) is_new_bundesland = int(self.project_frame.ags) >= 11000000 attrs = { 'Hebesatz_GrStB': gem.Hebesatz_GrStB, 'is_new_bundesland': is_new_bundesland } startwerte = self.project.basedata.get_table( 'GrSt_Startwerte_Rohmieten_Bodenwert', 'Einnahmen').features() gem_typ_startwerte = startwerte.get(Gemeindetyp=gem.Gemeindetyp) common_fields = set([f.name for f in startwerte.fields()]).intersection( [f.name for f in self.grst_settings.fields()]) for field in common_fields: attrs[field] = gem_typ_startwerte[field] self.grst_settings.add(**attrs) def setup_hebesatz(self): ''' assessment rate parameters ''' if self.hebesatz_params: self.hebesatz_params.close() layout = self.ui.grundsteuer_hebesatz_param_group.layout() clear_layout(layout) self.hebesatz_params = Params( layout, help_file='einnahmen_grundsteuer_hebesatz.txt') self.hebesatz_params.hebesatz = Param( self.grst_settings.Hebesatz_GrStB, SpinBox(maximum=999, step=10), label='Hebesatz GrSt B Projektgemeinde', unit='v.H.') def save(): self.changed.emit() self.grst_settings.Hebesatz_GrStB = \ self.hebesatz_params.hebesatz.value self.grst_settings.save() self.hebesatz_params.show(title='Hebesatz bearbeiten') self.hebesatz_params.changed.connect(save) def setup_rohmiete(self): ''' gross rent parameters ''' tou = self.areas.values('nutzungsart') if self.grst_settings.is_new_bundesland \ or not Nutzungsart.WOHNEN.value in tou: self.ui.grundsteuer_rohmiete_param_group.setVisible(False) return self.ui.grundsteuer_rohmiete_param_group.setVisible(True) if self.rohmiete_params: self.rohmiete_params.close() layout = self.ui.grundsteuer_rohmiete_param_group.layout() clear_layout(layout) self.rohmiete_params = Params( layout, help_file='einnahmen_grundsteuer_rohmieten.txt') self.rohmiete_params.add( Title('Rohmiete 1964 in Euro pro Monat', bold=False)) self.rohmiete_params.efh = Param(self.grst_settings.EFH_Rohmiete / 100, DoubleSpinBox(minimum=0.3, maximum=5, step=0.05), label=f' - Einfamilienhaus', unit='€/m²') self.rohmiete_params.dhh = Param(self.grst_settings.DHH_Rohmiete / 100, DoubleSpinBox(minimum=0.3, maximum=5, step=0.05), label=f' - Doppelhaus', unit='€/m²') self.rohmiete_params.rhw = Param(self.grst_settings.RHW_Rohmiete / 100, DoubleSpinBox(minimum=0.3, maximum=5, step=0.05), label=f' - Reihenhaus', unit='€/m²') self.rohmiete_params.mfh = Param(self.grst_settings.MFH_Rohmiete / 100, DoubleSpinBox(minimum=0.3, maximum=5, step=0.05), label=f' - Mehrfamilienhaus', unit='€/m²') def save(): self.changed.emit() self.grst_settings.EFH_Rohmiete = round( self.rohmiete_params.efh.value * 100) self.grst_settings.DHH_Rohmiete = round( self.rohmiete_params.dhh.value * 100) self.grst_settings.RHW_Rohmiete = round( self.rohmiete_params.rhw.value * 100) self.grst_settings.MFH_Rohmiete = round( self.rohmiete_params.mfh.value * 100) self.grst_settings.save() self.rohmiete_params.show(title='Rohmieten bearbeiten') self.rohmiete_params.changed.connect(save) def setup_sachwert(self): ''' asset value parameters ''' tou = self.areas.values('nutzungsart') if not self.grst_settings.is_new_bundesland\ or not Nutzungsart.WOHNEN.value in tou: self.ui.grundsteuer_sachwert_param_group.setVisible(False) return if self.sachwert_params: self.sachwert_params.close() self.ui.grundsteuer_sachwert_param_group.setVisible(True) layout = self.ui.grundsteuer_sachwert_param_group.layout() clear_layout(layout) self.sachwert_params = Params( layout, help_file='einnahmen_grundsteuer_sachwertverfahren.txt') self.sachwert_params.add(Title('Sachwertverfahren', bold=False)) self.sachwert_params.bodenwert = Param( self.grst_settings.Bodenwert_SWV / 100, DoubleSpinBox(minimum=0.3, maximum=5, step=0.05), label=f' - Bodenwert 1935 pro m²', unit='€/m²') self.sachwert_params.flaeche = Param( self.grst_settings.qm_Grundstueck_pro_WE_EFH, SpinBox(minimum=300, maximum=2000, step=1), label=f' - mittl. Größe Einfamilienhausgrundstücke', unit='m²') def save(): self.changed.emit() self.grst_settings.Bodenwert_SWV = round( self.sachwert_params.bodenwert.value * 100) self.grst_settings.qm_Grundstueck_pro_WE_EFH = \ self.sachwert_params.flaeche.value self.grst_settings.save() self.sachwert_params.show(title='Sachwertverfahren bearbeiten') self.sachwert_params.changed.connect(save) def setup_bauvolumen(self): ''' construction volume parameters ''' tou = self.areas.values('nutzungsart') if not (Nutzungsart.GEWERBE.value in tou or Nutzungsart.EINZELHANDEL.value in tou): self.ui.grundsteuer_bauvolumen_param_group.setVisible(False) # set to 0 as a precaution to not put some old values into # the calculation self.grst_settings.Bueroflaeche = 0 self.grst_settings.Verkaufsraeume = 0 self.grst_settings.save() return self.ui.grundsteuer_bauvolumen_param_group.setVisible(True) if self.bauvolumen_params: self.bauvolumen_params.close() layout = self.ui.grundsteuer_bauvolumen_param_group.layout() clear_layout(layout) self.bauvolumen_params = Params( layout, help_file='einnahmen_grundsteuer_bauvolumen.txt') self.bauvolumen_params.add( Title( 'Gewerbe / Einzelhandel: Voraussichtliches ' 'Bauvolumen\n(Brutto-Grundfläche, BGF)', bold=False)) self.bauvolumen_params.bueroflaeche = Param( self.grst_settings.Bueroflaeche, SpinBox(minimum=0, maximum=99999, step=10), label=f' - Bürofläche', unit='m²') self.bauvolumen_params.verkaufsraeume = Param( self.grst_settings.Verkaufsraeume, SpinBox(minimum=0, maximum=99999, step=10), label=f' - Hallen und Verkaufsräume', unit='m²') def save(): self.changed.emit() self.grst_settings.Bueroflaeche = \ self.bauvolumen_params.bueroflaeche.value self.grst_settings.Verkaufsraeume = \ self.bauvolumen_params.verkaufsraeume.value self.grst_settings.save() self.bauvolumen_params.show( title='Voraussichtliches Bauvolumen bearbeiten') self.bauvolumen_params.changed.connect(save) def calculate(self): ''' calculate the property tax ''' job = GrundsteuerCalculation(self.project) def on_success(r): self.add_layer() self.changed.emit() self.dialog = ProgressDialog(job, parent=self.ui, on_success=on_success, auto_close=True) self.dialog.show() def add_layer(self): ''' show property tax layer ''' self.output = ProjectLayer.from_table(self.gemeinden.table, groupname=self.layer_group) self.output.draw(label='Grundsteuer', style_file='einnahmen_grundsteuer.qml', filter="grundsteuer != 'NULL'", uncheck_siblings=True, redraw=False) self.output.zoom_to() def close(self): if self.hebesatz_params: self.hebesatz_params.close() if self.rohmiete_params: self.rohmiete_params.close() if self.sachwert_params: self.sachwert_params.close() if self.bauvolumen_params: self.bauvolumen_params.close()
def setup_params(self): ''' set up migration settings for each muncipality in study area ''' if self.params: self.params.close() self.ui.svb_parameter_group.setVisible(True) layout = self.ui.svb_parameter_group.layout() clear_layout(layout) if len(self.wanderung) == 0: self.ui.svb_parameter_group.setVisible(False) return self.ui.svb_parameter_group.setVisible(True) self.params = Params(layout, help_file='einnahmen_beschaeftigte_wanderung.txt') self.df_wanderung = self.wanderung.to_pandas() randsummen = self.project.basedata.get_table('Wanderung_Randsummen', 'Einnahmen').features() factor_inner = randsummen.get(IDWanderungstyp=1).Anteil_Gewerbe factor_outer = randsummen.get(IDWanderungstyp=2).Anteil_Gewerbe factor_neu = randsummen.get(IDWanderungstyp=3).Anteil_Gewerbe project_ags = self.project_frame.ags project_gem = self.gemeinden.get(AGS=project_ags) wanderung = self.wanderung.get(AGS=project_ags) sum_ap = sum(x or 0 for x in self.areas.values('ap_gesamt')) # ToDo: this is exactly the same as in EinwohnerMigration def update_salden(ags_changed): param = self.params[ags_changed] fixed = param.is_locked saldo = param.input.value idx = self.df_wanderung['AGS'] == ags_changed if fixed: fixed_fortzug = self.df_wanderung[np.invert(idx) & self.df_wanderung['fixed'] == True]['fortzug'].sum() # the rest of "fortzüge" that can be applied to this row min_value = fixed_fortzug - (sum_ap * factor_inner) saldo = max(saldo, min_value) zuzug = self.df_wanderung[idx]['zuzug'].values[0] self.df_wanderung.loc[idx, 'fortzug'] = zuzug - saldo self.df_wanderung.loc[idx, 'fixed'] = fixed self.df_wanderung.loc[idx, 'saldo'] = saldo self.df_wanderung = MigrationCalculation.calculate_saldi( self.df_wanderung, factor_inner, project_ags) for gemeinde_ags in self.df_wanderung['AGS'].values: param = self.params[gemeinde_ags] row = self.df_wanderung[self.df_wanderung['AGS'] == gemeinde_ags] param.input.blockSignals(True) param.input.value = row['saldo'].values[0] param.input.blockSignals(False) self.params.add(Title('Standortgemeinde des Projekts', bold=False)) spinbox = DoubleSpinBox(minimum=0, maximum=1000, step=1, lockable=True, locked=wanderung.fixed, reversed_lock=True) project_saldo = Param(wanderung.saldo, spinbox, repr_format='%+.2f', label=f' - {project_gem.GEN}', unit='SvB') self.params.add(project_saldo, name=project_ags) spinbox.changed.connect(lambda o: update_salden(project_ags)) spinbox.locked.connect(lambda o: update_salden(project_ags)) self.params.add( Param(factor_neu * sum_ap, label='davon neu geschaffene Arbeitsplätze')) self.params.add(Seperator()) self.params.add(Title('Region um Standortgemeinde', bold=False)) for gemeinde in sorted(self.gemeinden, key=lambda x: x.GEN): ags = gemeinde.AGS if ags == project_ags: continue wanderung = self.wanderung.get(AGS=ags) if not wanderung: continue spinbox = DoubleSpinBox(minimum=-1000, maximum=0, step=1, lockable=True, locked=wanderung.fixed, reversed_lock=True) param = Param(wanderung.saldo, spinbox, label=f' - {gemeinde.GEN}', unit='SvB') self.params.add(param, name=ags) spinbox.changed.connect(lambda o, a=ags: update_salden(a)) spinbox.locked.connect(lambda o, a=ags: update_salden(a)) self.params.add(Seperator()) self.params.add( Param(-factor_outer * sum_ap, label='Restliches Bundesgebiet / Ausland', unit='SvB')) def save(): self.wanderung.update_pandas(self.df_wanderung) self.canvas.refreshAllLayers() self.params.show(title='Geschätzte Salden (Beschäftigte) bearbeiten', scrollable=True) self.params.changed.connect(save)
def setup_point_params(self, point: 'Feature'): ''' set up the parameters in the UI to edit the given point measure ''' if self.point_params: self.point_params.close() ui_group = self.ui.point_parameter_group layout = ui_group.layout() clear_layout(layout) if not point: return self.point_params = Params( layout, help_file='infrastruktur_punktmassnahme.txt') self.point_params.bezeichnung = Param( point.bezeichnung, LineEdit(width=300), label='Bezeichnung') punktelemente = list(self.netzelemente.filter(Typ='Punkt')) type_names = [p.Netzelement for p in punktelemente] typ = self.netzelemente.get(IDNetzelement=point.IDNetzelement) type_combo = ComboBox( ['nicht gesetzt'] + type_names, data=[None] + list(punktelemente), width=300) self.point_params.typ = Param( typ.Netzelement if typ else 'nicht gesetzt', type_combo, label='Erschließungsnetz' ) self.point_params.add(Seperator(margin=0)) self.point_params.euro_EH = Param( point.Euro_EH, DoubleSpinBox(), unit='€', label='Kosten der erstmaligen Herstellung' ) self.point_params.euro_BU = Param( point.Cent_BU / 100, DoubleSpinBox(), unit='€', label='Jährliche Kosten für Betrieb und Unterhaltung' ) self.point_params.euro_EN = Param( point.Euro_EN, DoubleSpinBox(), unit='€', label='Erneuerungskosten nach Ablauf der Lebensdauer' ) self.point_params.lebensdauer = Param( point.Lebensdauer, SpinBox(minimum=1, maximum=1000), label='Technische oder wirtschaftliche \n' 'Lebensdauer bis zur Erneuerung', unit='Jahr(e)' ) def save(): point.bezeichnung = self.point_params.bezeichnung.value typ = type_combo.get_data() point.IDNetzelement = typ.IDNetzelement if typ else 0 point.IDNetz = typ.IDNetz if typ else 0 point.Lebensdauer = self.point_params.lebensdauer.value point.Euro_EH = self.point_params.euro_EH.value point.Euro_EN = self.point_params.euro_EN.value point.Cent_BU = self.point_params.euro_BU.value * 100 point.save() # lazy way to update the combo box self.fill_points_combo(select=point) self.point_params.show() self.point_params.changed.connect(save) last_row = self.point_params.layout.children()[-1] button = QPushButton() icon_path = 'iconset_mob/20190619_iconset_mob_delete_1.png' icon = QIcon(os.path.join(self.project.settings.IMAGE_PATH, icon_path)) button.setText('Maßnahme entfernen') button.setIcon(icon) button.setToolTip( '<p><span style=" font-weight:600;">Maßnahme entfernen</span>' '</p><p>Löscht die aktuell gewählte Maßnahme. ' '<br/>Dieser Schritt kann nicht rückgängig gemacht werden. </p>') last_row.insertWidget(0, button) button.clicked.connect(self.remove_point)
class Kostentraeger: ''' parameters and calculations of shares of infrastructural costs between different payers ''' def __init__(self, ui, project): self.ui = ui self.project = project self.ui.kostentraeger_button.clicked.connect( self.calculate_kostentraeger) self.default_kostenaufteilung = self.project.basedata.get_table( 'Kostenaufteilung_Startwerte', 'Kosten') self.kostenphasen = self.project.basedata.get_table( 'Kostenphasen', 'Kosten').features() self.aufteilungsregeln = self.project.basedata.get_table( 'Aufteilungsregeln', 'Kosten').features() self.applyable_aufteilungsregeln = self.project.basedata.get_table( 'Aufteilungsregeln_zu_Netzen_und_Phasen', 'Kosten').features() self.netzelemente = self.project.basedata.get_table( 'Netze_und_Netzelemente', 'Kosten', fields=['IDNetz', 'Netz'] ).features() df_netzelemente = self.netzelemente.to_pandas() del df_netzelemente['fid'] df_netzelemente.drop_duplicates(inplace=True) for i, (index, row) in enumerate(df_netzelemente.iterrows()): net_id = row['IDNetz'] net_name = row['Netz'] radio = QRadioButton(net_name) self.ui.kostenaufteilung_radio_grid.addWidget(radio, i // 2, i % 2) if i == 0: self.net_id = net_id radio.setChecked(True) radio.toggled.connect( lambda b, i=net_id: self.setup_kostenaufteilung(i)) def load_content(self): self.kostenaufteilung = Kostenaufteilung.features( create=True, project=self.project) # initialize empty project 'kostenaufteilungen' with the default ones if len(self.kostenaufteilung) == 0: for default in self.default_kostenaufteilung.features(): rule = self.aufteilungsregeln.get( IDAufteilungsregel=default.IDKostenregel) self.kostenaufteilung.add( Anteil_GSB=rule.Anteil_GSB, Anteil_GEM=rule.Anteil_GEM, Anteil_ALL=rule.Anteil_ALL, IDNetz=default.IDNetz, IDKostenphase=default.IDKostenphase ) self.setup_kostenaufteilung(self.net_id) def calculate_kostentraeger(self): ''' calculations of cost shares ''' job = KostentraegerAuswerten(self.project) def on_close(): if not self.dialog.success: return # the years originate from gesamtkosten calculation diagram = KostentraegerDiagramm(project=self.project, years=GesamtkostenErmitteln.years) diagram.draw() self.dialog = ProgressDialog(job, parent=self.ui, on_close=on_close, auto_close=True) self.dialog.show() def setup_kostenaufteilung(self, net_id): ''' set up parameters to edit shares for a specific type of infrastructure ''' self.net_id = net_id ui_group = self.ui.kostenaufteilung_params_group net_name = self.netzelemente.filter(IDNetz=net_id)[0].Netz ui_group.setTitle(net_name) layout = ui_group.layout() clear_layout(layout) self.params = Params( layout, help_file='infrastruktur_kostenaufteilung.txt') field_names = ['Anteil_GSB', 'Anteil_GEM', 'Anteil_ALL'] labels = ['Kostenanteil der Grunstücksbesitzer/innen', 'Kostenanteil der Gemeinde', 'Netznutzer/innen und Tarifkundschaft'] def preset_changed(c, p): preset = c.get_data() if not preset: return for field_name in field_names: param = self.params.get(f'{p.Kostenphase}_{field_name}') param.input.value = preset[field_name] for i, phase in enumerate(self.kostenphasen): dependency = SumDependency(100) self.params.add(Title(phase.Kostenphase)) feature = self.kostenaufteilung.get( IDKostenphase=phase.IDKostenphase, IDNetz=net_id) preset_combo, options = self.create_presets( net_id, phase.IDKostenphase) param = Param(0, preset_combo, label='Vorschlagswerte') param.hide_in_overview = True self.params.add(param, name=f'{phase.Kostenphase}_presets') for j, field_name in enumerate(field_names): label = labels[j] slider = Slider(maximum=100, lockable=True) param = Param(feature[field_name], slider, label=label, unit='%') self.params.add( param, name=f'{phase.Kostenphase}_{field_name}') dependency.add(param) slider.changed.connect( lambda b, c=preset_combo, o=options: c.set_value(o[0])) if i != len(self.kostenphasen) - 1: self.params.add(Seperator(margin=0)) preset_combo.changed.connect( lambda b, c=preset_combo, p=phase: preset_changed(c, p)) self.params.show(title='Kostenaufteilung festlegen') self.params.changed.connect(lambda: self.save(net_id)) def create_presets(self, net_id: int, phase_id: int) -> tuple: ''' create a combobox with presets for shares for a specific phase and type of infrastructure element ''' applyable_rules = self.applyable_aufteilungsregeln.filter( IDNetz=net_id, IDPhase=phase_id) rules = [] for applyable_rule in applyable_rules: rule_id = applyable_rule.IDAufteilungsregel rule = self.aufteilungsregeln.get(IDAufteilungsregel=rule_id) rules.append(rule) options = (['Aufteilungsregel wählen'] + [rule.Aufteilungsregel for rule in rules]) preset_combo = ComboBox(options, [None] + rules) preset_combo.input.model().item(0).setEnabled(False) return preset_combo, options def save(self, net_id): ''' write the current settings of paramaters for a specific type of infrastructure element ''' for phase in self.kostenphasen: feature = self.kostenaufteilung.get( IDKostenphase=phase.IDKostenphase, IDNetz=net_id) for field_name in ['Anteil_GSB', 'Anteil_GEM', 'Anteil_ALL']: param = self.params[f'{phase.Kostenphase}_{field_name}'] feature[field_name] = param.value feature.save()
class InfrastructureDrawing: ''' drawing tools and amounts of infrastructure ''' def __init__(self, ui, project, canvas, layer_group): self.ui = ui self.layer_group = layer_group self.project = project self.canvas = canvas self.ui.show_lines_button.clicked.connect( lambda: self.draw_output('line', toggle_if_exists=True)) self.ui.show_lines_button.setCheckable(False) self.ui.show_points_button.clicked.connect( lambda: self.draw_output('point', toggle_if_exists=True)) self.ui.show_points_button.setCheckable(False) self.ui.points_combo.currentIndexChanged.connect( lambda idx: self.toggle_point( self.ui.points_combo.currentData())) self.ui.infrastrukturmengen_button.clicked.connect( self.infrastrukturmengen) self.setup_tools() self.point_params = None self.line_params = None def load_content(self): self.netzelemente = self.project.basedata.get_table( 'Netze_und_Netzelemente', 'Kosten' ).features() self.drawn_lines = ErschliessungsnetzLinienZeichnung.features( create=True) self.line_elements = ErschliessungsnetzLinien.features( create=True) self.points = ErschliessungsnetzPunkte.features(create=True) self.output_lines = ProjectLayer.from_table( self.drawn_lines.table, groupname=self.layer_group, prepend=True) self.output_points = ProjectLayer.from_table( self.points.table, groupname=self.layer_group, prepend=True) self.fill_points_combo() self.setup_line_params() def fill_points_combo(self, select: 'Feature'=None): ''' fill the combobox with all drawn point measures, preselect given point measure ''' self.ui.points_combo.blockSignals(True) self.ui.points_combo.clear() points = [point for point in self.points] points.sort(key=lambda x: x.IDNetzelement) self.ui.points_combo.addItem('nichts ausgewählt') idx = 0 for i, point in enumerate(points): typ = self.netzelemente.get(IDNetzelement=point.IDNetzelement) self.ui.points_combo.addItem( f'{point.bezeichnung} ({typ.Netzelement if typ else "-"})', point ) if select and point.id == select.id: idx = i + 1 if idx: self.ui.points_combo.setCurrentIndex(idx) self.ui.points_combo.blockSignals(False) self.toggle_point() def setup_tools(self): ''' set up the drawing tools ''' self._tools = [] self.line_tools = { self.ui.anliegerstrasse_innere_button: 11, self.ui.sammelstrasse_innere_button: 12, self.ui.anliegerstrasse_aeussere_button: 21, self.ui.sammelstrasse_aeussere_button: 22, self.ui.kanal_trennsystem_button: 31, self.ui.kanal_mischsystem_button: 32, self.ui.kanal_schmutzwasser_button: 33, self.ui.trinkwasserleitung_button: 41, self.ui.stromleitung_button: 51 } for button, net_id in self.line_tools.items(): button.clicked.connect(lambda: self.draw_output('line')) tool = LineMapTool(button, canvas=self.canvas) tool.drawn.connect( lambda geom, i=net_id: self.add_geom(geom, i, geom_typ='line')) self._tools.append(tool) self.select_lines_tool = FeaturePicker( self.ui.select_lines_button, canvas=self.canvas) self.ui.select_lines_button.clicked.connect( lambda: self.draw_output('line')) self.select_lines_tool.feature_picked.connect(self.select_line) self.ui.remove_lines_button.clicked.connect( self.remove_selected_lines) self.ui.remove_drawing_button.clicked.connect( self.remove_drawing) self._tools.append(self.select_lines_tool) self.draw_point_tool = MapClickedTool( self.ui.add_point_button, canvas=self.canvas, target_epsg=self.project.settings.EPSG) self.draw_point_tool.map_clicked.connect(self.add_point) self.select_point_tool = FeaturePicker( self.ui.select_point_button, canvas=self.canvas) self.select_point_tool.feature_picked.connect(self.select_point) self.ui.select_point_button.clicked.connect( lambda: self.draw_output('point')) self.ui.add_point_button.clicked.connect( lambda: self.draw_output('point')) self._tools.append(self.draw_point_tool) def add_geom(self, geom: 'QgsGeometry', net_id: int, geom_typ: str='line') -> 'Feature': ''' add a geometry to the database with the id of network element and the type ('line' or 'point') ''' features = self.drawn_lines if geom_typ == 'line' \ else self.points typ = self.netzelemente.get(IDNetzelement=net_id) feature = features.add(IDNetzelement=net_id, IDNetz=typ.IDNetz if typ else 0, geom=geom) if geom_typ == 'line': feature.length = geom.length() feature.save() # workaround: if layer had no data before it needs to be readded to show # sth, refresh doesn't work if len(features) == 1: self.draw_output(geom_typ, redraw=True) self.canvas.refreshAllLayers() return feature def draw_output(self, geom_typ: str='line', redraw: bool=False, toggle_if_exists: bool=False): ''' add either the point measures or line measures as a layer depending on given geom_type ('line' or 'point') ''' label = 'Erschließungsnetz' if geom_typ == 'point': label += ' - punktuelle Maßnahmen' output = self.output_lines if geom_typ == 'line' else self.output_points style = 'kosten_erschliessungsnetze_{}elemente.qml'.format( 'linien' if geom_typ == 'line' else 'punkt') output.draw(label=label, style_file=style, redraw=redraw, toggle_if_exists=toggle_if_exists) tool = self.select_lines_tool if geom_typ == 'line' \ else self.select_point_tool tool.set_layer(output.layer) def select_line(self, feature: 'Feature'): ''' select the line measure in output layer ''' layer = self.output_lines.layer selected = [f.id() for f in layer.selectedFeatures()] if feature.id() not in selected: layer.select(feature.id()) else: layer.removeSelection() layer.selectByIds([fid for fid in selected if fid != feature.id()]) def remove_selected_lines(self): ''' remove all line measures selected in the line output layer ''' layer = self.output_lines.layer if not layer: return for qf in layer.selectedFeatures(): feat = self.drawn_lines.get(id=qf.id()) if feat: feat.delete() self.canvas.refreshAllLayers() def remove_drawing(self): ''' remove all line measures ''' reply = QMessageBox.question( self.ui, 'Zeichnung löschen', f'Sollen alle gezeichneten Linienelemente gelöscht werden?', QMessageBox.Yes, QMessageBox.No ) if reply == QMessageBox.No: return self.drawn_lines.delete() self.canvas.refreshAllLayers() def add_point(self, geom: 'QgsGeometry'): ''' add a point measure ''' point = self.add_geom(geom, 0, geom_typ='point') point.bezeichnung = 'unbenannte Maßnahme' point.save() self.fill_points_combo(select=point) def remove_point(self): ''' remove point measure currently selected in the point combobox ''' point = self.ui.points_combo.currentData() if not point: return reply = QMessageBox.question( self.ui, 'Maßnahme entfernen', f'Soll die punktuelle Maßnahme "{point.bezeichnung}" ' 'entfernt werden?\n', QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: point.delete() self.fill_points_combo() def select_point(self, feature: 'Feature'): ''' select the point measure in output layer ''' if not self.output_points.layer: return self.output_points.layer.removeSelection() self.output_points.layer.select(feature.id()) fid = feature.id() for idx in range(len(self.ui.points_combo)): point = self.ui.points_combo.itemData(idx) if point and fid == point.id: break self.ui.points_combo.setCurrentIndex(idx) def toggle_point(self, point: 'Feature'=None): ''' toggle selection of point measure, draws layers and groups for displaying the point measure ''' if self.output_points.layer: self.output_points.layer.removeSelection() if not point: point = self.ui.points_combo.currentData() self.setup_point_params(point) if not point: self.ui.point_parameter_group.setVisible(False) return self.ui.point_parameter_group.setVisible(True) self.draw_output('point') self.output_points.layer.select(point.id) self.setup_point_params(point) def setup_point_params(self, point: 'Feature'): ''' set up the parameters in the UI to edit the given point measure ''' if self.point_params: self.point_params.close() ui_group = self.ui.point_parameter_group layout = ui_group.layout() clear_layout(layout) if not point: return self.point_params = Params( layout, help_file='infrastruktur_punktmassnahme.txt') self.point_params.bezeichnung = Param( point.bezeichnung, LineEdit(width=300), label='Bezeichnung') punktelemente = list(self.netzelemente.filter(Typ='Punkt')) type_names = [p.Netzelement for p in punktelemente] typ = self.netzelemente.get(IDNetzelement=point.IDNetzelement) type_combo = ComboBox( ['nicht gesetzt'] + type_names, data=[None] + list(punktelemente), width=300) self.point_params.typ = Param( typ.Netzelement if typ else 'nicht gesetzt', type_combo, label='Erschließungsnetz' ) self.point_params.add(Seperator(margin=0)) self.point_params.euro_EH = Param( point.Euro_EH, DoubleSpinBox(), unit='€', label='Kosten der erstmaligen Herstellung' ) self.point_params.euro_BU = Param( point.Cent_BU / 100, DoubleSpinBox(), unit='€', label='Jährliche Kosten für Betrieb und Unterhaltung' ) self.point_params.euro_EN = Param( point.Euro_EN, DoubleSpinBox(), unit='€', label='Erneuerungskosten nach Ablauf der Lebensdauer' ) self.point_params.lebensdauer = Param( point.Lebensdauer, SpinBox(minimum=1, maximum=1000), label='Technische oder wirtschaftliche \n' 'Lebensdauer bis zur Erneuerung', unit='Jahr(e)' ) def save(): point.bezeichnung = self.point_params.bezeichnung.value typ = type_combo.get_data() point.IDNetzelement = typ.IDNetzelement if typ else 0 point.IDNetz = typ.IDNetz if typ else 0 point.Lebensdauer = self.point_params.lebensdauer.value point.Euro_EH = self.point_params.euro_EH.value point.Euro_EN = self.point_params.euro_EN.value point.Cent_BU = self.point_params.euro_BU.value * 100 point.save() # lazy way to update the combo box self.fill_points_combo(select=point) self.point_params.show() self.point_params.changed.connect(save) last_row = self.point_params.layout.children()[-1] button = QPushButton() icon_path = 'iconset_mob/20190619_iconset_mob_delete_1.png' icon = QIcon(os.path.join(self.project.settings.IMAGE_PATH, icon_path)) button.setText('Maßnahme entfernen') button.setIcon(icon) button.setToolTip( '<p><span style=" font-weight:600;">Maßnahme entfernen</span>' '</p><p>Löscht die aktuell gewählte Maßnahme. ' '<br/>Dieser Schritt kann nicht rückgängig gemacht werden. </p>') last_row.insertWidget(0, button) button.clicked.connect(self.remove_point) def init_lines(self): ''' initialize the line table in the database ''' line_elements = self.netzelemente.filter(Typ='Linie') df_line_elements = line_elements.to_pandas() del(df_line_elements['fid']) self.line_elements.update_pandas(df_line_elements) # reset filter ToDo: fix filtering self.netzelemente.filter() def setup_line_params(self): ''' set up the parameters to edit the lengths of the line measures ''' layout = self.ui.mengen_params_group.layout() clear_layout(layout) if len(self.line_elements) == 0: self.init_lines() self.line_params = Params( layout, help_file='infrastruktur_linienelemente.txt') for element in self.line_elements: param = Param(int(element.length), Slider(maximum=10000), label=element.Netzelement, unit='m') self.line_params.add( param, name=f'netzelement_{element.IDNetzelement}') def save(): for element in self.line_elements: param = self.line_params[f'netzelement_{element.IDNetzelement}'] element.length = param.value element.save() self.line_params.show(title=self.ui.mengen_params_group.title()) self.line_params.changed.connect(save) last_row = self.line_params.layout.children()[-1] button = QPushButton() button.setText('aus Zeichnung übernehmen') last_row.insertWidget(0, button) button.clicked.connect(self.apply_drawing) def apply_drawing(self): ''' analyse the drawing and apply the lengths of line measures to the line parameters ''' df_drawing = self.drawn_lines.to_pandas() for element in self.line_elements: param = self.line_params[f'netzelement_{element.IDNetzelement}'] drawn_elements = df_drawing[ df_drawing['IDNetzelement']==element.IDNetzelement] length = int(drawn_elements['length'].sum()) param.value = length element.length = length element.save() def infrastrukturmengen(self): ''' show the infrastructure amounts as diagrams ''' diagram = NetzlaengenDiagramm(project=self.project) diagram.draw() diagram = MassnahmenKostenDiagramm(project=self.project) diagram.draw(offset_x=100, offset_y=100) def close(self): ''' close parameters and tools ''' for tool in self._tools: tool.set_active(False) if self.point_params: self.point_params.close() if self.line_params: self.line_params.close()
class Gesamtkosten: ''' edit cost parameters and calculation of costs ''' def __init__(self, ui, project): self.ui = ui self.project = project self.ui.gesamtkosten_button.clicked.connect(self.calculate_gesamtkosten) self.netzelemente = self.project.basedata.get_table( 'Netze_und_Netzelemente', 'Kosten' ).features().filter(Typ='Linie') for i, netzelement in enumerate(self.netzelemente): net_element_id = netzelement.IDNetzelement radio = QRadioButton(netzelement.Netzelement) self.ui.kostenkennwerte_radio_grid.addWidget(radio, i // 2, i % 2) if i == 0: self.net_element_id = net_element_id radio.setChecked(True) radio.toggled.connect( lambda b, i=net_element_id: self.setup_net_element(i) ) def load_content(self): self.kostenkennwerte = KostenkennwerteLinienelemente.features( create=True) if len(self.kostenkennwerte) == 0: init_kostenkennwerte(self.project) self.setup_net_element(self.net_element_id) def calculate_gesamtkosten(self): ''' calculate total costs of infrastructure and show diagram of results ''' job = GesamtkostenErmitteln(self.project) def on_close(): if not self.dialog.success: return diagram = GesamtkostenDiagramm(project=self.project, years=GesamtkostenErmitteln.years) diagram.draw() self.dialog = ProgressDialog(job, parent=self.ui, on_close=on_close, auto_close=True) self.dialog.show() def setup_net_element(self, net_element_id): ''' set up parameters to edit the costs per element and phase ''' self.net_element_id = net_element_id ui_group = self.ui.kostenkennwerte_params_group net_element_name = self.netzelemente.get( IDNetzelement=net_element_id).Netzelement ui_group.setTitle(net_element_name) layout = ui_group.layout() clear_layout(layout) net_element = self.kostenkennwerte.get(IDNetzelement=net_element_id) self.params = Params( layout, help_file='infrastruktur_kostenkennwerte.txt') self.params.euro_EH = Param( net_element.Euro_EH, DoubleSpinBox(), unit='€', label='Kosten der erstmaligen Herstellung \n' 'pro laufenden Meter' ) self.params.euro_BU = Param( net_element.Cent_BU / 100, DoubleSpinBox(), unit='€', label='Jährliche Kosten für Betrieb und Unterhaltung \n' 'pro laufenden Meter und Jahr' ) self.params.euro_EN = Param( net_element.Euro_EN, DoubleSpinBox(), unit='€', label='Kosten der Erneuerung \n' 'pro laufenden Meter und Erneuerungszyklus' ) self.params.lebensdauer = Param( net_element.Lebensdauer, SpinBox(minimum=1, maximum=1000), label='Lebensdauer: Jahre zwischen den Erneuerungszyklen', unit='Jahr(e)' ) self.params.show() self.params.changed.connect(lambda: self.save(net_element_id)) def save(self, net_element_id): ''' write the current values of the parameters to database ''' net_element = self.kostenkennwerte.get(IDNetzelement=net_element_id) net_element.Euro_EH = self.params.euro_EH.value net_element.Lebensdauer = self.params.lebensdauer.value net_element.Cent_BU = self.params.euro_BU.value * 100 net_element.Euro_EN = self.params.euro_EN.value net_element.save()