Esempio n. 1
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. 2
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()
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()
Esempio n. 4
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. 5
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()
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()
Esempio n. 7
0
class EditPlanfallMarkets(EditMarkets):
    '''
    planfall (scenario) market control
    '''
    layer_filter = 'id_betriebstyp_nullfall = 0'
    layer_style = 'standortkonkurrenz_geplante_maerkte.qml'
    filter_args = {'id_betriebstyp_nullfall': 0}
    market_label = 'Geplante Märkte'
    suffix = 'planfall'

    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))

    def add_market(self, geom, name='unbenannter geplanter Markt'):
        '''
        add a scenario market to the database
        '''
        market = self.markets.add(
            name=name,
            id_betriebstyp_nullfall=0,
            betriebstyp_nullfall=self.typen.get(id_betriebstyp=0).name,
            id_betriebstyp_planfall=1,
            betriebstyp_planfall=self.typen.get(id_betriebstyp=1).name,
            id_kette=0,
            kette=self.ketten.get(id_kette=0).name,
            geom=geom)
        crs = QgsCoordinateReferenceSystem(
            f'EPSG:{self.project.settings.EPSG}')
        ags = get_ags([market], self.basedata, source_crs=crs)[0]
        market.AGS = ags.AGS
        vkfl = self.market_tool.betriebstyp_to_vkfl(
            market.id_betriebstyp_planfall, market.id_kette)
        market.vkfl_planfall = vkfl
        market.save()
        self.changed.emit()
        # workaround: if layer had no data before it needs to be readded to show
        # sth, refresh doesn't work
        if len(self.markets) == 1:
            self.add_layer()
        self.canvas.refreshAllLayers()
        self.fill_combo(select=market)
Esempio n. 8
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. 9
0
class Traffic(Domain):
    '''
    domain-widget for calculating and visualizing the additional traffic load
    '''

    ui_label = 'Verkehr im Umfeld'
    ui_file = 'domain_03-ViU.ui'
    ui_icon = "images/iconset_mob/20190619_iconset_mob_domain_traffic_6.png"

    layer_group = "Wirkungsbereich 3 - Verkehr im Umfeld"

    @classmethod
    def reset(cls, project=None):
        '''
        remove existing results
        '''
        if not project:
            project = cls.project_manager.active_project
        RouteLinks.features(project=project, create=True).table.truncate()
        Itineraries.features(project=project, create=True).table.truncate()
        TrafficLoadLinks.features(project=project,
                                  create=True).table.truncate()
        Ways.features(project=project, create=True).table.truncate()

    def setupUi(self):
        self.node_output = None
        self.itinerary_output = None
        self.ways_params = None
        self.weight_params = None
        self.node_params = None
        self.select_tool = FeaturePicker(self.ui.select_transfer_node_button,
                                         canvas=self.canvas)
        self.select_tool.feature_picked.connect(self.select_node)

        self.drag_tool = FeatureDragger(self.ui.move_transfer_node_button,
                                        canvas=self.canvas)
        self.drag_tool.feature_dragged.connect(self.move_node)

        self.ui.select_transfer_node_button.clicked.connect(
            lambda: self.draw_nodes())
        self.ui.move_transfer_node_button.clicked.connect(
            lambda: self.draw_nodes())

        self.ui.transfer_node_combo.currentIndexChanged.connect(
            lambda idx: self.toggle_node(self.ui.transfer_node_combo.
                                         currentData(),
                                         center_on_point=True))
        self.ui.transfer_node_parameter_group.setVisible(False)

        self.add_node_tool = MapClickedTool(
            self.ui.add_transfer_node_button,
            canvas=self.canvas,
            target_epsg=self.project.settings.EPSG)
        self.add_node_tool.map_clicked.connect(self.add_node)
        self.ui.add_transfer_node_button.clicked.connect(
            lambda: self.draw_nodes())

        self.ui.calc_transfer_nodes_button.clicked.connect(
            self.calculate_nodes)
        self.ui.calculate_traffic_button.clicked.connect(
            self.calculate_traffic)

        self.ui.remove_transfer_nodes_button.clicked.connect(self.remove_nodes)

        pdf_path = os.path.join(self.settings.HELP_PATH,
                                'Anleitung_Verkehr_im_Umfeld.pdf')
        self.ui.manual_button.clicked.connect(lambda: open_file(pdf_path))

    def load_content(self):
        super().load_content()
        output = ProjectLayer.find('Projektdefinition')
        if output:
            output[0].setItemVisibilityChecked(True)
        self.areas = Teilflaechen.features(project=self.project)
        self.links = RouteLinks.features(project=self.project, create=True)
        self.traffic_load = TrafficLoadLinks.features(project=self.project,
                                                      create=True)
        self.transfer_nodes = TransferNodes.features(project=self.project,
                                                     create=True)
        self.itineraries = Itineraries.features(project=self.project,
                                                create=True)
        self.ways = Ways.features(project=self.project, create=True)
        self.connectors = Connectors.features(project=self.project,
                                              create=True)

        self.draw_nodes()
        self.fill_node_combo()
        self.setup_ways()
        self.setup_weights()

    def fill_node_combo(self, select: 'Feature' = None):
        '''
        set up node selection
        '''
        self.ui.transfer_node_combo.blockSignals(True)
        self.ui.transfer_node_combo.clear()
        self.ui.transfer_node_combo.addItem('nichts ausgewählt')
        idx = 0
        for i, node in enumerate(self.transfer_nodes):
            self.ui.transfer_node_combo.addItem(node.name, node)
            if select and node.id == select.id:
                idx = i + 1
        if idx:
            self.ui.transfer_node_combo.setCurrentIndex(idx)
        self.ui.transfer_node_combo.blockSignals(False)
        self.toggle_node(self.ui.transfer_node_combo.currentData())

    def toggle_node(self, node, center_on_point=False):
        '''
        set up given transfer node
        '''
        if node and self.node_output and self.node_output.layer:
            self.node_output.layer.removeSelection()
            self.node_output.layer.select(node.id)
            if center_on_point:
                center_canvas(self.canvas, node.geom.asPoint(),
                              self.node_output.layer.crs())
        self.setup_node_params(node)

    def add_node(self, geom, name=None):
        '''
        add a transfer node to the database
        '''
        if self.itinerary_output:
            self.itinerary_output.remove()
        # equal share of weight for new node
        weight = 100 / (len(self.transfer_nodes) + 1)
        # reduce weights of other nodes by weight of new node
        d = (100 - weight) / 100
        for node in self.transfer_nodes:
            node.weight = round(node.weight * d)
            node.save()
        # distribute delta caused by rounding errors
        weight = 100 - sum(self.transfer_nodes.values('weight'))
        node = self.transfer_nodes.add(name=name, geom=geom, weight=weight)
        if not name:
            node.name = f'Herkunfts-/Zielpunkt {node.id + 1}'
            node.save()
        # workaround: if layer had no data before it needs to be readded to show
        # sth, refresh doesn't work
        if len(self.transfer_nodes) == 1:
            self.draw_nodes()
        self.canvas.refreshAllLayers()
        self.fill_node_combo(select=node)
        self.links.table.truncate()
        self.traffic_load.table.truncate()
        self.setup_weights()

    def move_node(self, feature_id: int, point: 'QgsPointXY'):
        '''
        move transfer node with given id to given point
        '''
        node = self.transfer_nodes.get(id=feature_id)
        node.geom = point
        node.save()
        self.drag_tool.reset()
        self.canvas.refreshAllLayers()
        self.links.table.truncate()
        self.traffic_load.table.truncate()
        self.set_status()

    def select_node(self, feature):
        '''
        select and highlight given transfer node feature
        '''
        if not self.node_output or not self.node_output.layer:
            return
        self.node_output.layer.removeSelection()
        self.node_output.layer.select(feature.id())
        fid = feature.id()
        for idx in range(len(self.ui.transfer_node_combo)):
            node = self.ui.transfer_node_combo.itemData(idx)
            if node and fid == node.id:
                break
        self.ui.transfer_node_combo.setCurrentIndex(idx)

    def setup_node_params(self, node):
        '''
        set up the parameters of a single transfer node
        '''
        if self.node_params:
            self.node_params.close()
        layout = self.ui.transfer_node_parameter_group.layout()
        clear_layout(layout)
        if not node:
            self.ui.transfer_node_parameter_group.setVisible(False)
            return
        self.ui.transfer_node_parameter_group.setVisible(True)
        #self.ui.transfer_node_parameter_group.setTitle(node.name)
        self.node_params = Params(layout, help_file='verkehr_knoten.txt')
        self.node_params.name = Param(node.name,
                                      LineEdit(width=300),
                                      label='Name')

        def save():
            node.name = self.node_params.name.value
            #self.ui.transfer_node_parameter_group.setTitle(node.name)
            node.save()
            self.canvas.refreshAllLayers()
            # lazy way to update the combo box
            self.fill_node_combo(select=node)
            self.setup_weights()

        self.node_params.show(title='Herkunfts-/Zielpunkt bearbeiten')
        self.node_params.changed.connect(save)

        last_row = self.node_params.layout.children()[-1]
        button = QPushButton()
        icon_path = 'iconset_mob/20190619_iconset_mob_delete_1.png'
        icon = QIcon(os.path.join(self.project.settings.IMAGE_PATH, icon_path))
        button.setText('Punkt entfernen')
        button.setIcon(icon)
        button.setToolTip(
            '<p><span style=" font-weight:600;">'
            'Herkunfts-/Zielpunkt entfernen</span>'
            '</p><p>Löscht den aktuell gewählten Herkunfts-/Zielpunkt. '
            '<br/>Dieser Schritt kann nicht rückgängig gemacht werden. </p>')
        last_row.insertWidget(0, button)
        button.clicked.connect(lambda: self.remove_node(node))

    def remove_node(self, node):
        '''
        remove given transfer node
        '''
        if not node:
            return
        reply = QMessageBox.question(
            self.ui.transfer_node_parameter_group,
            'Herkunfts-/Zielpunkt entfernen', f'Soll der Punkt "{node.name}" '
            'entfernt werden?', QMessageBox.Yes, QMessageBox.No)
        if reply == QMessageBox.Yes:
            if self.itinerary_output:
                self.itinerary_output.remove()
            # workaround: refresg node if weight was changed in the meantime
            node = self.transfer_nodes.get(id=node.id)
            # distribute removed weight
            d = (100 - node.weight) / 100
            node.delete()
            if d > 0:
                for node in self.transfer_nodes:
                    node.weight = round(node.weight / d)
                    node.save()
            # distribute delta caused by rounding errors
            delta = 100 - sum(self.transfer_nodes.values('weight'))
            first = self.transfer_nodes[0]
            first.weight = max(first.weight + delta, 0)
            self.links.table.truncate()
            self.traffic_load.table.truncate()
            self.canvas.refreshAllLayers()
            self.fill_node_combo()
            self.setup_weights()

    def remove_nodes(self):
        '''
        remove all transfer nodes
        '''
        if len(self.transfer_nodes) == 0:
            return
        reply = QMessageBox.question(
            self.ui.transfer_node_parameter_group,
            'Herkunfts-/Zielpunkte entfernen',
            f'Sollen alle Herkunfts-/Zielpunkte entfernt werden?',
            QMessageBox.Yes, QMessageBox.No)
        if reply == QMessageBox.Yes:
            if self.itinerary_output:
                self.itinerary_output.remove()
            self.transfer_nodes.table.truncate()
            self.links.table.truncate()
            self.traffic_load.table.truncate()
            self.canvas.refreshAllLayers()
            self.fill_node_combo()
            self.setup_weights()

    def setup_weights(self):
        '''
        set up the parameters to weight the transfer nodes
        '''
        self.set_status()

        if self.weight_params:
            self.weight_params.close()

        layout = self.ui.weights_group.layout()
        clear_layout(layout)
        self.weight_params = Params(parent=layout,
                                    button_label='Gewichtungen verändern',
                                    help_file='verkehr_gewichtungen.txt')

        dependency = SumDependency(100)
        for node in self.transfer_nodes:
            perc = round(node.weight)
            param = Param(perc,
                          Slider(maximum=100, lockable=True),
                          label=node.name,
                          unit='%')
            self.weight_params.add(param, name=node.name)
            dependency.add(param)

        def save():
            for node in self.transfer_nodes:
                node.weight = self.weight_params[node.name].value
                node.save()
            self.traffic_load.table.truncate()
            self.canvas.refreshAllLayers()
            self.set_status()

        self.weight_params.changed.connect(save)
        self.weight_params.show()

    def setup_ways(self):
        '''
        set up paramaters to set ways
        '''

        if self.ways_params:
            self.ways_params.close()

        if len(self.ways) == 0:
            self.calculate_ways()

        layout = self.ui.ways_group.layout()
        clear_layout(layout)
        self.ways_params = Params(parent=layout,
                                  button_label='Annahmen verändern',
                                  help_file='verkehr_wege.txt')

        for i, way in enumerate(self.ways):
            name = Nutzungsart(way.nutzungsart).name.capitalize()
            self.ways_params.add(Title(name, fontsize=8))
            self.ways_params[f'{name}_gesamt'] = Param(
                way.wege_gesamt,
                SpinBox(),
                label='Gesamtanzahl der Wege pro Werktag (Hin- und Rückwege)')
            self.ways_params[f'{name}_miv'] = Param(
                way.miv_anteil,
                SpinBox(maximum=100),
                label='Anteil der von Pkw-Fahrenden gefahrenen Wegen',
                unit='%')
            if i != len(self.ways) - 1:
                self.ways_params.add(Seperator(margin=0))

        def save():
            for way in self.ways:
                name = Nutzungsart(way.nutzungsart).name.capitalize()
                way.miv_anteil = self.ways_params[f'{name}_miv'].value
                way.wege_gesamt = self.ways_params[f'{name}_gesamt'].value
                way.save()
            self.traffic_load.table.truncate()
            self.canvas.refreshAllLayers()
            self.set_status()

        self.ways_params.changed.connect(save)
        self.ways_params.show()

    def calculate_ways(self):
        '''
        calculate and store the additional ways per type of use of the areas
        '''
        # get ways per type of use
        ways_tou = {}
        self.ways.table.truncate()
        for area in self.areas:
            if area.nutzungsart == 0:
                continue
            entry = ways_tou.get(area.nutzungsart)
            if not entry:
                entry = ways_tou[area.nutzungsart] = [0, 0]
            entry[0] += area.wege_gesamt
            entry[1] += area.wege_miv
        for tou, (wege_gesamt, wege_miv) in ways_tou.items():
            if wege_gesamt == 0:
                continue
            miv_anteil = round(100 * wege_miv / wege_gesamt)  # \
            # if wege_gesamt > 0 else 0
            self.ways.add(wege_gesamt=wege_gesamt,
                          nutzungsart=tou,
                          miv_anteil=miv_anteil)

    def set_status(self):
        '''
        sets visibility and active status of certain ui elements depending on
        current state of calculations
        '''
        has_nodes = len(self.transfer_nodes) != 0
        calc_done = len(self.traffic_load) != 0
        self.ui.calculate_traffic_button.setEnabled(has_nodes)
        self.ui.settings_frame.setVisible(has_nodes)
        button_text = 'Straßenverkehrsbelastung anzeigen' if calc_done \
            else 'Straßenverkehrsbelastung berechnen'
        self.ui.calculate_traffic_button.setText(button_text)

    def calculate_nodes(self):
        '''
        calculate the traffic nodes. resets all nodes and results
        '''
        if len(self.transfer_nodes) > 0:
            reply = QMessageBox.question(
                self.ui.transfer_node_parameter_group,
                'Herkunfts-/Zielpunkt entfernen',
                'Die Berechnung der Herkunfts-/Zielpunkte setzt alle '
                'vorhandenen Punkte und Berechnungen zurück.\n'
                'Wollen Sie die Berechnung fortsetzen?', QMessageBox.Yes,
                QMessageBox.No)
            if reply == QMessageBox.No:
                return
        self.transfer_nodes.table.truncate()
        self.links.table.truncate()
        self.traffic_load.table.truncate()
        distance = self.ui.distance_input.value()
        job = TransferNodeCalculation(self.project, distance=distance)

        def on_success(res):
            self.draw_nodes()
            self.draw_itineraries(zoom_to=True)

        def on_close():
            self.fill_node_combo()
            self.setup_weights()
            self.canvas.refreshAllLayers()

        dialog = ProgressDialog(job,
                                parent=self.ui,
                                on_success=on_success,
                                on_close=on_close)
        dialog.show()

    def calculate_traffic(self):
        '''
        calculate the traffic load
        '''
        max_dist = getattr(self.settings, 'MAX_AREA_DISTANCE', None)
        points = [c.geom.asPoint() for c in self.connectors]
        xs = [p.x() for p in points]
        ys = [p.y() for p in points]
        if max_dist is not None:
            distances = []
            for i in range(len(points)):
                for j in range(i):
                    dist = np.linalg.norm(
                        np.subtract((xs[i], ys[i]), (xs[j], ys[j])))
                    distances.append(dist)
            if distances and max(distances) > 2 * max_dist:
                QMessageBox.warning(
                    self.ui, 'Hinweis',
                    'Der Abstand zwischen den Anbindungspunkten ist zu groß. '
                    'Er darf für die Schätzung der Verkehrsbelastung jeweils '
                    f'nicht größer als {2 * max_dist} m sein!')
                return

        if len(self.traffic_load) == 0:
            recalculate = len(self.links) == 0
            job = Routing(self.project, recalculate=recalculate)

            def on_success(res):
                if self.itinerary_output:
                    self.itinerary_output.remove()
                self.draw_nodes()
                self.draw_traffic(zoom_to=True)
                self.setup_weights()

            dialog = ProgressDialog(job, parent=self.ui, on_success=on_success)
            dialog.show()
        else:
            self.draw_traffic(zoom_to=True, toggle_if_exists=True)

    def draw_nodes(self, zoom_to=False):
        '''
        show layer visualizing the transfer nodes
        '''
        self.node_output = ProjectLayer.from_table(self.transfer_nodes.table,
                                                   groupname=self.layer_group)
        self.node_output.draw(label='Herkunfts-/Zielpunkte',
                              style_file='verkehr_zielpunkte.qml',
                              prepend=True)
        self.select_tool.set_layer(self.node_output.layer)
        self.drag_tool.set_layer(self.node_output.layer)
        if zoom_to:
            self.node_output.zoom_to()

    def draw_itineraries(self, zoom_to=False):
        '''
        show layer visualizing the itineraries used for determining the
        transfer nodes
        '''
        self.itinerary_output = ProjectLayer.from_table(
            self.itineraries.table, groupname=self.layer_group)
        self.itinerary_output.draw(label='Zulaufstrecken',
                                   expanded=False,
                                   style_file='verkehr_kuerzeste_Wege.qml')
        if zoom_to:
            self.itinerary_output.zoom_to()

    def draw_traffic(self, zoom_to=False, toggle_if_exists=False):
        '''
        show layer visualizing the additional traffic load
        '''

        output = ProjectLayer.from_table(self.traffic_load.table,
                                         groupname=self.layer_group)
        output.draw(label='Zusätzliche PKW-Fahrten',
                    style_file='verkehr_links_zusaetzliche_PKW-Fahrten.qml',
                    filter=f'trips > 0',
                    toggle_if_exists=toggle_if_exists,
                    redraw=not toggle_if_exists)
        if zoom_to and output.tree_layer.isVisible():
            output.zoom_to()

    def close(self):
        if self.node_params:
            self.node_params.close()
        if self.ways_params:
            self.ways_params.close()
        if self.weight_params:
            self.weight_params.close()
        self.select_tool.set_active(False)
        self.drag_tool.set_active(False)
        self.add_node_tool.set_active(False)
        super().close()
Esempio n. 10
0
class ProjectDefinitions(Domain):
    '''
    domain-widget for the basic setup of the project areas (type of use etc.)
    '''
    ui_label = 'Projektdefinition'
    ui_file = 'definitions.ui'
    layer_group = 'Projektdefinition'

    def setupUi(self):
        '''
        set up possible user interactions and the sub-domains
        '''
        self.tou_output = None
        self.ui.area_combo.currentIndexChanged.connect(
            lambda: self.change_area())

        self.connector_setter = TrafficConnectors(
            self.ui, self.canvas, self.project)
        type_layout = self.ui.type_parameter_group.layout()
        # ToDo: somehow generate this (resp. assign index) from the enum
        #  preferably store labels and id in a base table
        self.types = [
            ('Nutzung noch nicht definiert', None),
            ('Wohnen', Wohnen(self.project, type_layout)),
            ('Gewerbe', Gewerbe(self.project, type_layout)),
            ('Einzelhandel', Einzelhandel(self.project, type_layout))
        ]
        self.typ = None

        pdf_path = os.path.join(
            self.settings.HELP_PATH, 'Anleitung_Projektdefinition.pdf')
        self.ui.manual_button.clicked.connect(lambda: open_file(pdf_path))

    def load_content(self):
        '''
        load the areas and data
        '''
        super().load_content()
        self.areas = Teilflaechen.features()
        self.connectors = Connectors.features()
        self.projektrahmendaten = Projektrahmendaten.features()[0]
        created = self.projektrahmendaten.datum.replace('"', '')
        self.ui.date_label.setText(created or '-')
        version = self.projektrahmendaten.basisdaten_version
        version_date = self.projektrahmendaten.basisdaten_datum
        self.ui.basedata_label.setText(
            f'v{version} (Stand: {version_date})' if version else '-')
        self.ui.area_combo.blockSignals(True)
        self.ui.area_combo.clear()
        for area in self.areas:
            tou_label = self.types[area.nutzungsart][0]
            self.ui.area_combo.addItem(f'{area.name} ({tou_label})', area)
        self.ui.area_combo.blockSignals(False)
        self.show_outputs()
        self.change_area()

    def change_area(self):
        '''
        change selected area and reload parameters
        '''
        self.area = self.ui.area_combo.itemData(
            self.ui.area_combo.currentIndex())

        layer = self.tou_output.layer
        if layer:
            layer.removeSelection()
            layer.select(self.area.id)

        self.connector_setter.load_content(self.area)

        self.setup_type()
        self.setup_type_params()

    def show_outputs(self, zoom=False):
        '''
        show the definition layers (planning areas with type of use)
        '''
        table = Teilflaechen.get_table()
        self.tou_output = ProjectLayer.from_table(
            table, groupname=self.layer_group)
        self.tou_output.draw(label='Nutzungen des Plangebiets',
                            style_file='definitions.qml', redraw=False)
        if zoom:
            self.tou_output.zoom_to()
        output = ProjectLayer.from_table(table, groupname='Hintergrund',
                                         prepend=False)
        output.draw(label='Umriss des Plangebiets', style_file='areas.qml')
        self.connector_setter.show_connectors()

    def setup_type(self):
        '''
        set up basic parameters (name, type of use)
        '''
        layout = self.ui.parameter_group.layout()
        clear_layout(layout)
        self.params = Params(layout,
                             help_file='definitionen_flaechen.txt', )
        self.params.name = Param(self.area.name, LineEdit(width=300),
                                 label='Name')

        self.params.add(Seperator(margin=0))

        ha = round(self.area.geom.area()) / 10000
        self.area.area = ha
        self.params.area = Param(ha, label='Größe', unit='ha')

        self.params.typ = Param(
            self.types[self.area.nutzungsart][0],
            ComboBox([t[0] for t in self.types], width=300),
            label='Nutzungsart'
        )

        # user changed type of use
        def type_changed():
            name = self.params.name.value
            type_labels = [t[0] for t in self.types]
            tou_id = type_labels.index(self.params.typ.value)
            self.area.nutzungsart = tou_id
            tou_label = self.types[tou_id][0]
            self.ui.area_combo.setItemText(
                self.ui.area_combo.currentIndex(),
                f'{name} ({tou_label})'
            )
            self.area.name = name
            self.area.save()
            # update connector names
            connector = self.connectors.get(id_teilflaeche=self.area.id)
            connector.name_teilflaeche = self.area.name
            connector.save()
            if self.typ:
                self.typ.clear(self.area)
            self.setup_type_params()
            self.canvas.refreshAllLayers()
            Traffic.reset()
        self.params.changed.connect(type_changed)
        self.params.show(title='Teilfläche definieren')

    def setup_type_params(self):
        '''
        set up detailled parameters depending on current type of use
        '''
        tou_label = self.types[self.area.nutzungsart][0]
        title = f'Maß der baulichen Nutzung ({tou_label})'
        self.ui.type_parameter_group.setTitle(title)

        clear_layout(self.ui.type_parameter_group.layout())
        if self.typ:
            self.typ.close()
        self.typ = self.types[self.area.nutzungsart][1]
        if self.typ is None:
            self.ui.type_parameter_group.setVisible(False)
            return
        self.ui.type_parameter_group.setVisible(True)
        self.typ.setup_params(self.area)
        self.typ.params.changed.connect(lambda: self.canvas.refreshAllLayers())

    def close(self):
        '''
        close sub-domains and parameters
        '''
        self.connector_setter.close()
        if self.tou_output and self.tou_output.layer:
            self.tou_output.layer.removeSelection()
        if hasattr(self, 'params'):
            self.params.close()
        if self.typ:
            self.typ.close()
        super().close()
Esempio n. 11
0
class Einzelhandel:
    '''
    sub-domain of project area definitions. Set up the structure of a project
    area with retail trade as the type of use
    '''
    def __init__(self, project, layout):
        self.project = project
        self.basedata = project.basedata
        self.sortimente_base = self.basedata.get_table(
            'Einzelhandel_Sortimente', 'Definition_Projekt'
        )
        self.verkaufsflaechen = Verkaufsflaechen.features(create=True)
        self.layout = layout
        self.markets = Markets.features(create=True)

    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')

    def save(self):
        '''
        write the current parameter values to the database. Create/change
        supermarket based on the settings for area of food retailing.
        '''
        vkfl_sum = 0
        vkfl_lebensmittel = 0
        # id of food
        id_lm = 1
        for sortiment in self.sortimente_base.features():
            feature = self.verkaufsflaechen.get(id_sortiment=sortiment.id,
                                                id_teilflaeche=self.area.id)
            if not feature:
                feature = self.verkaufsflaechen.add(
                    id_sortiment=sortiment.id, id_teilflaeche=self.area.id)
            vkfl = getattr(self.params, sortiment.param_vfl).value
            feature.verkaufsflaeche_qm = vkfl
            vkfl_sum += vkfl
            if sortiment.id == id_lm:
                vkfl_lebensmittel += vkfl
            feature.name_sortiment = sortiment.Name_Sortiment_ProjektCheck
            feature.save()

        self.area.vf_gesamt = vkfl_sum
        self.area.save()

        market = self.markets.get(id_teilflaeche=self.area.id)
        if vkfl_lebensmittel > 0:
            if not market:
                centroid = self.area.geom.centroid().asPoint()
                name = f'Neuer Lebensmittelmarkt auf Fläche "{self.area.name}"'
                market = self.markets.add(
                    id_teilflaeche=self.area.id,
                    name = name,
                    geom=centroid,
                    kette= 'Anbieter unbekannt'
                )
                gem = get_ags([market], self.project.basedata)[0]
                market.AGS = gem.AGS
                market.save()

            sm = Supermarket(0, 0, 0, name='a', kette='b',
                             vkfl=vkfl_lebensmittel)
            market_tool = ReadMarketsWorker(self.project)
            sm = market_tool.vkfl_to_betriebstyp([sm])[0]
            market.id_betriebstyp_planfall = sm.id_betriebstyp
            market.betriebstyp_planfall = sm.betriebstyp
            market.vkfl_planfall = vkfl_lebensmittel
            market.save()
            SupermarketsCompetition.remove_results()
        else:
            if market:
                market.delete()
                SupermarketsCompetition.remove_results()

        self.set_ways(self.area)
        Traffic.reset()
        MunicipalTaxRevenue.reset_gewerbe_einzelhandel()

    def clear(self, area):
        '''
        clear all data related to retail trade as the type of use of the given
        area
        '''
        # remove existing market
        market = self.markets.get(id_teilflaeche=area.id)
        if market:
            market.delete()
            SupermarketsCompetition.remove_results()
        self.verkaufsflaechen.filter(id_teilflaeche=area.id).delete()
        area.vf_gesamt = None
        area.save()
        MunicipalTaxRevenue.reset_gewerbe_einzelhandel()

    def set_ways(self, area):
        '''
        calculate and store the daily ways done by the employees and customers
        of the area
        '''
        df_verkaufsflaechen = self.verkaufsflaechen.filter(
            id_teilflaeche=area.id).to_pandas()
        default_branche = self.basedata.get_table(
            'Gewerbe_Branchen', 'Definition_Projekt').features().get(
                ID_Branche_ProjektCheck=0)
        df_sortimente = self.sortimente_base.to_pandas()
        df_sortimente.rename(
            columns={'ID_Sortiment_ProjektCheck': 'id_sortiment'}, inplace=True)

        joined = df_verkaufsflaechen.merge(df_sortimente, on='id_sortiment',
                                           how='left')

        n_ways = (joined['verkaufsflaeche_qm'] *
                  joined['Besucher_je_qm_Vfl'] *
                  joined['Wege_je_Besucher'])
        n_ways_miv = n_ways * joined['Anteil_Pkw_Fahrer'] / 100

        n_job_ways = (joined['verkaufsflaeche_qm'] *
                      joined['AP_je_qm_Vfl'] *
                      default_branche.Wege_je_Beschäftigten)
        n_job_miv = n_job_ways * default_branche.Anteil_Pkw_Fahrer / 100

        area.wege_gesamt = int(n_ways.sum() + n_job_ways.sum())
        area.wege_miv = int(n_ways_miv.sum() + n_job_miv.sum())

        area.save()

    def close(self):
        '''
        close parameters
        '''
        if hasattr(self, 'params'):
            self.params.close()
Esempio n. 12
0
class Gewerbe:
    '''
    sub-domain of project area definitions. Set up the structure of a project
    area with industry as the type of use
    '''
    # Default Gewerbegebietstyp
    DEFAULT_INDUSTRY_ID = 2
    # analysis period
    BETRACHTUNGSZEITRAUM_JAHRE = 15

    def __init__(self, project, layout):
        self.layout = layout
        self.gewerbeanteile = Gewerbeanteile.features(create=True)
        self.ap_nach_jahr = ApProJahr.features(create=True)
        self.projektrahmendaten = Projektrahmendaten.features()
        self.basedata = project.basedata

        self.branchen = list(self.basedata.get_table(
            'Gewerbe_Branchen', 'Definition_Projekt'
        ).features().filter(ID_Branche_ProjektCheck__gt=0))

        presets = self.basedata.get_table(
            'Vorschlagswerte_Branchenstruktur', 'Definition_Projekt'
        )
        self.df_presets_base = presets.to_pandas()

        density = self.basedata.get_table(
            'Dichtekennwerte_Gewerbe', 'Definition_Projekt'
        )
        self.df_density_base = density.to_pandas()

        industry_types = self.basedata.get_table(
            'Gewerbegebietstypen', 'Definition_Projekt'
        )
        self.df_industry_types_base = industry_types.to_pandas()

        default_idx = self.df_industry_types_base['IDGewerbegebietstyp'] == \
            self.DEFAULT_INDUSTRY_ID
        self.df_industry_types_base.loc[
            default_idx, 'Name_Gewerbegebietstyp'] += ' (default)'

    def set_industry_presets(self, preset_id):
        '''
        set all sector values to database presets of given industry id
        '''
        if preset_id == -1:
            return
        idx = self.df_presets_base['IDGewerbegebietstyp'] == preset_id
        presets = self.df_presets_base[idx]
        for branche in self.branchen:
            param = getattr(self.params, branche.param_gewerbenutzung)
            p_idx = presets['ID_Branche_ProjektCheck'] == branche.id
            preset = int(presets[p_idx]['Vorschlagswert_in_Prozent'].values[0])
            param.value = preset

    def estimate_jobs(self):
        '''
        calculate estimation of number of jobs
        set estimated jobs to sectors of industry
        '''
        gemeindetyp = self.area.gemeinde_typ
        df_kennwerte = self.df_density_base[
            self.df_density_base['Gemeindetyp_ProjektCheck'] == gemeindetyp]

        jobs_sum = 0
        for branche in self.branchen:
            param = getattr(self.params, branche.param_gewerbenutzung)
            idx = df_kennwerte['ID_Branche_ProjektCheck'] == branche.id
            jobs_per_ha = int(df_kennwerte[idx]['AP_pro_ha_brutto'].values[0])
            jobs_ind = round(self.area.area * (param.input.value / 100.)
                             * jobs_per_ha)
            branche.estimated_jobs = jobs_ind
            branche.jobs_per_ha = jobs_per_ha
            jobs_sum += jobs_ind

        return jobs_sum

    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_gewerbe.txt')

        self.params.add(Title('Bezugszeitraum'))
        self.params.beginn_nutzung = Param(
            area.beginn_nutzung, SpinBox(minimum=2000, maximum=2100),
            label='Beginn des Bezuges', repr_format='%d'
        )
        self.params.aufsiedlungsdauer = Param(
            area.aufsiedlungsdauer, SpinBox(minimum=1, maximum=100),
            label='Dauer des Bezuges (Jahre, 1 = Bezug wird noch\n'
            'im Jahr des Bezugsbeginns abgeschlossen)', unit='Jahr(e)'
        )

        self.params.add(Seperator(margin=10))

        self.params.add(
            Title('Voraussichtlicher Anteil der Branchen an der Nettofläche'))

        preset_names = self.df_industry_types_base[
            'Name_Gewerbegebietstyp'].values
        preset_ids = self.df_industry_types_base['IDGewerbegebietstyp'].values
        options = ['Gebietstyp wählen'] + list(preset_names)
        self.preset_combo = ComboBox(options, [-1] + list(preset_ids))
        self.preset_combo.input.model().item(0).setEnabled(False)
        param = Param(0, self.preset_combo, label='Vorschlagswerte')
        param.hide_in_overview = True
        self.params.add(param, name='gebietstyp')
        # break grid layout
        self.params.add(
            QSpacerItem(0, 3, QSizePolicy.Fixed, QSizePolicy.Minimum))

        def values_changed():
            if self.auto_check.value:
                n_jobs = self.estimate_jobs()
                self.ap_slider.set_value(n_jobs)

        def slider_changed():
            self.preset_combo.set_value(options[0])
            values_changed()

        def preset_changed():
            self.set_industry_presets(self.preset_combo.input.currentData())
            values_changed()

        self.preset_combo.changed.connect(preset_changed)

        dependency = SumDependency(100)
        for branche in self.branchen:
            feature = self.gewerbeanteile.get(
                id_branche=branche.id,
                id_teilflaeche=self.area.id
            )
            value = feature.anteil_definition if feature else 0
            slider = Slider(maximum=100, width=200, lockable=True)
            param = Param(
                value, slider, label=f'{branche.Name_Branche_ProjektCheck}',
                unit='%'
            )
            slider.changed.connect(slider_changed)
            dependency.add(param)
            self.params.add(param, name=branche.param_gewerbenutzung)

        self.params.add(Seperator())

        self.params.add(Title('Voraussichtliche Anzahl an Arbeitsplätzen'))

        self.auto_check = Checkbox()
        self.params.auto_check = Param(
            bool(self.area.ap_ist_geschaetzt), self.auto_check,
            label='Automatische Schätzung'
        )

        self.ap_slider = Slider(maximum=10000)
        self.params.arbeitsplaetze_insgesamt = Param(
            self.area.ap_gesamt, self.ap_slider,
            label='Zahl der Arbeitsplätze\n'
            'nach Vollbezug (Summe über alle Branchen)'
        )

        def toggle_auto_check():
            # disable input for manual setting of jobs
            # when auto check is enabled
            read_only = self.auto_check.value
            for _input in [self.ap_slider.slider, self.ap_slider.spinbox]:
                _input.setAttribute(Qt.WA_TransparentForMouseEvents, read_only)
                _input.setFocusPolicy(Qt.NoFocus if read_only
                                      else Qt.StrongFocus)
                _input.update()
            values_changed()

        self.auto_check.changed.connect(toggle_auto_check)
        toggle_auto_check()

        # set to default preset if assignment is new
        if len(self.gewerbeanteile) == 0:
            self.set_industry_presets(self.DEFAULT_INDUSTRY_ID)
            ap_gesamt = self.estimate_jobs()
            self.params.arbeitsplaetze_insgesamt.value = ap_gesamt
            self.save()

        self.params.changed.connect(self.save)
        self.params.show(
            title='Gewerbe: Bezugszeitraum und Maß der baulichen Nutzung')

    def save(self):
        '''
        write the current parameter values to the database
        '''
        for branche in self.branchen:
            feature = self.gewerbeanteile.get(id_branche=branche.id,
                                              id_teilflaeche=self.area.id)
            if not feature:
                feature = self.gewerbeanteile.add(
                    id_branche=branche.id,
                    id_teilflaeche=self.area.id
                )
            feature.name_teilflaeche = self.area.name
            feature.anteil_definition = getattr(
                self.params, branche.param_gewerbenutzung).value
            feature.name_branche = branche.Name_Branche_ProjektCheck
            feature.anzahl_jobs_schaetzung = getattr(
                branche, 'estimated_jobs', 0)
            feature.dichtekennwert = getattr(
                branche, 'jobs_per_ha', 0)
            feature.save()

        self.area.beginn_nutzung = self.params.beginn_nutzung.value
        self.area.aufsiedlungsdauer = self.params.aufsiedlungsdauer.value
        self.area.ap_gesamt = self.params.arbeitsplaetze_insgesamt.value
        self.area.ap_ist_geschaetzt = self.params.auto_check.value

        self.area.save()

        # just estimate for output in case auto estimation is deactivated
        # (estimated values needed in any case)
        self.estimate_jobs()
        self.set_growth(self.area)
        self.set_percentages(self.area)
        self.set_ways(self.area)

        Traffic.reset()
        MunicipalTaxRevenue.reset_gewerbe_einzelhandel()

    def clear(self, area):
        '''
        clear all data related to industry as the type of use of the given area
        '''
        MunicipalTaxRevenue.reset_gewerbe_einzelhandel()
        self.gewerbeanteile.filter(id_teilflaeche=area.id).delete()
        self.ap_nach_jahr.filter(id_teilflaeche=area.id).delete()
        area.ap_gesamt = None
        area.save()

    def set_growth(self, area):
        '''
        calculate and store the development of the number of employees
        '''
        n_jobs = area.ap_gesamt
        begin = area.beginn_nutzung
        duration = area.aufsiedlungsdauer

        end = begin + self.BETRACHTUNGSZEITRAUM_JAHRE - 1

        self.ap_nach_jahr.filter(id_teilflaeche=area.id).delete()

        for progress in range(0, end - begin + 1):
            proc_factor = (float(progress + 1) / duration
                           if progress + 1 <= duration
                           else 1)
            year = begin + progress

            self.ap_nach_jahr.add(
                id_teilflaeche=self.area.id,
                name_teilflaeche=self.area.name,
                jahr=year,
                arbeitsplaetze=n_jobs * proc_factor
            )

    def set_percentages(self, area):
        '''
        this already could have done when saving,
        but is here based on the old ArcGIS code
        '''
        df = self.gewerbeanteile.filter(id_teilflaeche=area.id).to_pandas()
        df['anteil_branche'] = df['anteil_definition'] * df['dichtekennwert']
        df['anteil_branche'] /= df['anteil_branche'].sum()
        df['anteil_branche'] *= 100
        df = df.round({'anteil_branche': 0})
        self.gewerbeanteile.update_pandas(df)

    def set_ways(self, area):
        '''
        calculate and store the daily ways done by the employees of the area
        '''
        df_anteile = self.gewerbeanteile.filter(
            id_teilflaeche=area.id).to_pandas()
        df_basedata = (self.basedata.get_table(
            'Gewerbe_Branchen', 'Definition_Projekt').features().filter(
                ID_Branche_ProjektCheck__gt=0).to_pandas())
        df_basedata.rename(columns={'ID_Branche_ProjektCheck': 'id_branche'},
                           inplace=True)
        estimated = df_anteile['anzahl_jobs_schaetzung']
        estimated_sum = estimated.sum()
        preset = area.ap_gesamt
        cor_factor = preset / estimated_sum if estimated_sum > 0 else 0
        joined = df_anteile.merge(df_basedata, on='id_branche', how='left')
        n_ways = estimated * cor_factor * joined['Wege_je_Beschäftigten']
        n_ways_miv = n_ways * joined['Anteil_Pkw_Fahrer'] / 100

        area.wege_gesamt = int(n_ways.sum())
        area.wege_miv = int(n_ways_miv.sum())

        area.save()

    def close(self):
        '''
        close parameters
        '''
        if hasattr(self, 'params'):
            self.params.close()
Esempio n. 13
0
class Wohnen:
    '''
    sub-domain of project area definitions. Set up the structure of a
    residential project area
    '''
    def __init__(self, project, layout):
        self.basedata = project.basedata
        self.gebaeudetypen_base = self.basedata.get_table(
            'Wohnen_Gebaeudetypen', 'Definition_Projekt'
        ).features()
        self.df_presets = self.basedata.get_table(
            'WE_nach_Gebietstyp', 'Definition_Projekt'
        ).to_pandas()
        self.wohneinheiten = Wohneinheiten.features(create=True)
        self.layout = layout

    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_wohnen.txt')
        self.params.add(Title('Bezugszeitraum'))
        self.params.beginn_nutzung = Param(
            area.beginn_nutzung, SpinBox(minimum=2000, maximum=2100),
            label='Beginn des Bezuges', repr_format='%d'
        )
        self.params.aufsiedlungsdauer = Param(
            area.aufsiedlungsdauer, SpinBox(minimum=1, maximum=100),
            label='Dauer des Bezuges', unit='Jahr(e)')
        self.params.add(Seperator())

        self.params.add(Title('Anzahl Wohneinheiten nach Gebäudetypen'))

        # load the building presets to select from
        preset_names, idx = np.unique(self.df_presets['Gebietstyp'].values,
                                     return_index=True)
        idx.sort()
        preset_names = self.df_presets['Gebietstyp'].values[idx]
        options = ['Gebietstyp wählen'] + list(preset_names)
        self.preset_combo = ComboBox(options)
        self.preset_combo.input.model().item(0).setEnabled(False)

        param = Param(0, self.preset_combo, label='Vorschlagswerte')
        param.hide_in_overview = True
        self.params.add(param, name='gebietstyp')
        self.params.add(
            QSpacerItem(0, 3, QSizePolicy.Fixed, QSizePolicy.Minimum))

        # preset is selected
        def preset_changed(gebietstyp):
            presets = self.df_presets[self.df_presets['Gebietstyp']==gebietstyp]
            for idx, preset in presets.iterrows():
                id_bt = preset['IDGebaeudetyp']
                bt = self.gebaeudetypen_base.get(id=id_bt)
                param = self.params.get(bt.param_we)
                param.input.value = self.area.area * preset['WE_pro_Hektar']
        self.preset_combo.changed.connect(preset_changed)

        for bt in self.gebaeudetypen_base:
            param_name = bt.param_we
            feature = self.wohneinheiten.get(id_gebaeudetyp=bt.id,
                                             id_teilflaeche=self.area.id)
            value = feature.we if feature else 0
            slider = Slider(maximum=2000)
            self.params.add(Param(
                value, slider,
                label=f'... in {bt.display_name}'),
                name=param_name
            )
            slider.changed.connect(
                lambda: self.preset_combo.set_value(options[0]))

        self.params.add(Seperator())

        self.params.add(Title('Mittlere Anzahl Bewohner pro Wohneinheit\n'
                              '(3 Jahre nach Bezug)'))

        for bt in self.gebaeudetypen_base:
            param_name = bt.param_ew_je_we
            feature = self.wohneinheiten.get(id_gebaeudetyp=bt.id,
                                             id_teilflaeche=self.area.id)
            # set to default if no feature yet
            value = feature.ew_je_we if feature else bt.default_ew_je_we
            self.params.add(Param(
                value, DoubleSpinBox(step=0.1, maximum=50),
                label=f'... in {bt.display_name}'),
                name=param_name
            )

        self.params.add(Seperator())

        self.params.add(Title('Anteil der Bewohner unter 18 Jahre'))

        # load the age presets to select from
        for bt in self.gebaeudetypen_base:
            param_name = bt.param_anteil_u18
            feature = self.wohneinheiten.get(id_gebaeudetyp=bt.id,
                                             id_teilflaeche=self.area.id)
            # set to default if no feature yet
            value = feature.anteil_u18 if feature else bt.default_anteil_u18
            self.params.add(Param(
                value, Slider(maximum=60), unit='%',
                label=f'... in {bt.display_name}'),
                name=param_name
            )

        self.params.changed.connect(self.save)
        self.params.show(
            title='Wohnen: Bezugszeitraum, Maß der baulichen Nutzung, '
            'Haushaltsstrukturen')

    def save(self):
        '''
        write the current parameter values to the database
        '''
        we_sum = 0
        for bt in self.gebaeudetypen_base:
            feature = self.wohneinheiten.get(id_gebaeudetyp=bt.id,
                                             id_teilflaeche=self.area.id)
            if not feature:
                feature = self.wohneinheiten.add(
                    id_gebaeudetyp=bt.id, id_teilflaeche=self.area.id)
            we = getattr(self.params, bt.param_we).value
            feature.we = we
            we_sum += we
            ew_je_we = getattr(self.params, bt.param_ew_je_we).value
            feature.ew_je_we = ew_je_we
            anteil_u18 = getattr(self.params, bt.param_anteil_u18).value
            feature.anteil_u18 = anteil_u18
            cor_factor = ew_je_we / bt.Ew_pro_WE_Referenz
            feature.korrekturfaktor = cor_factor
            feature.name_gebaeudetyp = bt.NameGebaeudetyp
            feature.save()

        self.area.beginn_nutzung = self.params.beginn_nutzung.value
        self.area.aufsiedlungsdauer = self.params.aufsiedlungsdauer.value
        self.area.we_gesamt = we_sum

        Traffic.reset()
        MunicipalTaxRevenue.reset_wohnen()

        self.area.save()

        job = WohnenDevelopment(self.basedata, self.area)

        dialog = ProgressDialog(
            job, auto_close=True,
            parent=self.layout.parentWidget())
        dialog.show()

    def clear(self, area):
        '''
        clear all data related to residential use of the given area
        '''
        self.wohneinheiten.filter(id_teilflaeche=area.id).delete()
        area.we_gesamt = None
        area.ew = 0
        area.save()
        MunicipalTaxRevenue.reset_wohnen()

    def close(self):
        '''
        close parameters
        '''
        if hasattr(self, 'params'):
            self.params.close()