Esempio n. 1
0
    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)
Esempio n. 2
0
    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()
Esempio n. 3
0
    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)
Esempio n. 5
0
    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))
Esempio n. 7
0
    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)
Esempio n. 8
0
    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()
Esempio n. 9
0
    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')
Esempio n. 10
0
    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)
Esempio n. 11
0
    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()
Esempio n. 12
0
    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))
Esempio n. 13
0
    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))
Esempio n. 15
0
    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)
Esempio n. 16
0
    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')
Esempio n. 17
0
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()
Esempio n. 18
0
    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))
Esempio n. 19
0
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()
Esempio n. 20
0
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()
Esempio n. 21
0
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()
Esempio n. 22
0
    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)
Esempio n. 23
0
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()
Esempio n. 24
0
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()
Esempio n. 25
0
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()
Esempio n. 26
0
    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()