コード例 #1
0
    def __init__(self, iface) -> None:
        super().__init__()

        self.options = get_options()
        self.msg_bar = MessageBar(iface)

        self.tree_widget = QTreeWidget()
        self.populate_tree()

        self.select_mandatory_hires_button = QPushButton(
            'Select Mandatory Fields in Highest Resolution')
        self.select_mandatory_hires_button.clicked.connect(
            self.on_select_mandatory_hires_button_clicked)

        self.select_mandatory_lores_button = QPushButton(
            'Select Mandatory Fields in Lowest Resolution')
        self.select_mandatory_lores_button.clicked.connect(
            self.on_select_mandatory_lores_button_clicked)

        self.download_button = QPushButton('Download Selected Datasets')
        self.download_button.clicked.connect(self.on_download_button_clicked)

        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, PROGRESS_BAR_MAX)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.hide()

        vbox = QVBoxLayout()
        vbox.addWidget(self.tree_widget)
        vbox.addWidget(self.select_mandatory_hires_button)
        vbox.addWidget(self.select_mandatory_lores_button)
        vbox.addWidget(self.download_button)
        vbox.addWidget(self.progress_bar)
        self.setLayout(vbox)
コード例 #2
0
ファイル: widget_run.py プロジェクト: mostamndi/gis4wrf
    def __init__(self, iface: QgisInterface) -> None:
        super().__init__()

        self.iface = iface

        self.options = get_options()
        self.msg_bar = MessageBar(iface)

        self.wps_box, [open_namelist_wps, prepare_only_wps, run_geogrid, run_ungrib, run_metgrid, open_output_wps] = \
            self.create_gbox_with_btns('WPS', [
                'Open Configuration',
                'Prepare only',
                ['Run Geogrid', 'Run Ungrib', 'Run Metgrid'],
                'Visualize Output'
            ])
        self.wrf_box, [open_namelist_wrf, prepare_only_wrf, run_real, run_wrf, open_output_wrf] = \
            self.create_gbox_with_btns('WRF', [
                'Open Configuration',
                'Prepare only',
                ['Run Real', 'Run WRF'],
                'Visualize Output'
            ])
        self.control_box, [kill_program] = self.create_gbox_with_btns(
            'Program control', ['Kill Program'])
        self.control_box.setVisible(False)
        self.stdout_box, self.stdout_textarea, self.stdout_highlighter = \
            self.create_stdout_box()

        vbox = QVBoxLayout()
        vbox.addWidget(self.wps_box)
        vbox.addWidget(self.wrf_box)
        vbox.addWidget(self.control_box)
        vbox.addWidget(self.stdout_box)
        self.stdout_box.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        self.setLayout(vbox)

        open_namelist_wps.clicked.connect(self.on_open_namelist_wps_clicked)
        prepare_only_wps.clicked.connect(self.on_prepare_only_wps_clicked)
        run_geogrid.clicked.connect(self.on_run_geogrid_clicked)
        run_ungrib.clicked.connect(self.on_run_ungrib_clicked)
        run_metgrid.clicked.connect(self.on_run_metgrid_clicked)
        open_output_wps.clicked.connect(self.on_open_output_wps_clicked)

        open_namelist_wrf.clicked.connect(self.on_open_namelist_wrf_clicked)
        prepare_only_wrf.clicked.connect(self.on_prepare_only_wrf_clicked)
        run_real.clicked.connect(self.on_run_real_clicked)
        run_wrf.clicked.connect(self.on_run_wrf_clicked)
        open_output_wrf.clicked.connect(self.on_open_output_wrf_clicked)

        kill_program.clicked.connect(self.on_kill_program_clicked)
コード例 #3
0
class GeoToolsDownloadManager(QWidget):
    def __init__(self, iface) -> None:
        super().__init__()

        self.options = get_options()
        self.msg_bar = MessageBar(iface)

        self.tree_widget = QTreeWidget()
        self.populate_tree()

        self.select_mandatory_hires_button = QPushButton(
            'Select Mandatory Fields in Highest Resolution')
        self.select_mandatory_hires_button.clicked.connect(
            self.on_select_mandatory_hires_button_clicked)

        self.select_mandatory_lores_button = QPushButton(
            'Select Mandatory Fields in Lowest Resolution')
        self.select_mandatory_lores_button.clicked.connect(
            self.on_select_mandatory_lores_button_clicked)

        self.download_button = QPushButton('Download Selected Datasets')
        self.download_button.clicked.connect(self.on_download_button_clicked)

        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, PROGRESS_BAR_MAX)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.hide()

        vbox = QVBoxLayout()
        vbox.addWidget(self.tree_widget)
        vbox.addWidget(self.select_mandatory_hires_button)
        vbox.addWidget(self.select_mandatory_lores_button)
        vbox.addWidget(self.download_button)
        vbox.addWidget(self.progress_bar)
        self.setLayout(vbox)

    def populate_tree(self) -> None:
        self.tree_widget.setHeaderLabels(['ID', 'Resolution', 'Description'])
        self.tree_widget.setRootIsDecorated(False)
        self.tree_widget.setSortingEnabled(True)
        self.tree_widget.sortByColumn(0, Qt.AscendingOrder)
        header = self.tree_widget.header()
        header.setSectionsMovable(False)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QHeaderView.Stretch)

        for dataset_id, (description, resolution) in geo_datasets.items():
            item = QTreeWidgetItem(self.tree_widget)
            item.setFlags(item.flags() | Qt.ItemIsUserCheckable)

            item.setText(0, dataset_id)
            item.setData(0, Qt.UserRole, dataset_id)
            item.setCheckState(0, Qt.Unchecked)
            if is_geo_dataset_downloaded(dataset_id, self.options.geog_dir):
                item.setFlags(Qt.NoItemFlags)
                item.setToolTip(
                    0, 'Dataset downloaded in: {}'.format(
                        get_geo_dataset_path(dataset_id,
                                             self.options.geog_dir)))
            else:
                item.setToolTip(0, dataset_id)

            if isinstance(resolution, str):
                item.setText(1, resolution)
            else:
                item.setText(1, formatted_dd_to_dms(resolution))
                item.setToolTip(1, '{}°'.format(resolution))

            item.setText(2, description)
            item.setToolTip(2, description)

    def on_select_mandatory_lores_button_clicked(self):
        self.select_datasets(geo_datasets_mandatory_lores)

    def on_select_mandatory_hires_button_clicked(self):
        self.select_datasets(geo_datasets_mandatory_hires)

    def select_datasets(self, names: List[str]) -> None:
        items = self.get_items()
        for name, item in items.items():
            item.setCheckState(0,
                               Qt.Checked if name in names else Qt.Unchecked)

    def get_items(self) -> Dict[str, QTreeWidgetItem]:
        items = {}  # type: Dict[str,QTreeWidgetItem]
        for index in range(self.tree_widget.topLevelItemCount()):
            item = self.tree_widget.topLevelItem(index)
            items[item.data(0, Qt.UserRole)] = item
        return items

    def on_download_button_clicked(self) -> None:
        datasets_to_download = []
        for name, item in self.get_items().items():
            if item.checkState(0) == Qt.Checked:
                datasets_to_download.append(name)

        thread = TaskThread(
            lambda: self.download_datasets(datasets_to_download),
            yields_progress=True)
        thread.started.connect(self.on_started_download)
        thread.progress.connect(self.on_progress_download)
        thread.finished.connect(self.on_finished_download)
        thread.succeeded.connect(self.on_successful_download)
        thread.failed.connect(reraise)
        thread.start()

    def on_started_download(self):
        self.download_button.hide()
        self.progress_bar.show()
        self.tree_widget.setEnabled(False)

    def on_progress_download(self, progress: float, status: str) -> None:
        bar_value = int(progress * PROGRESS_BAR_MAX)
        self.progress_bar.setValue(bar_value)
        self.progress_bar.repaint()  # otherwise just updates in 1% steps
        logger.debug(f'Geo data download: {progress*100:.1f}% - {status}')

    def on_finished_download(self) -> None:
        self.download_button.show()
        self.progress_bar.hide()
        self.tree_widget.setEnabled(True)
        self.tree_widget.clear()
        self.populate_tree()
        Broadcast.geo_datasets_updated.emit()

    def on_successful_download(self) -> None:
        self.msg_bar.success('Geographical datasets downloaded successfully.')

    def download_datasets(
            self, dataset_names: List[str]) -> Iterable[Tuple[float, str]]:
        yield 0.0, 'selected: ' + ', '.join(dataset_names)
        for i, name in enumerate(dataset_names):
            for dataset_progress in download_and_extract_geo_dataset(
                    name, self.options.geog_dir):
                total_progress = (i + dataset_progress) / len(dataset_names)
                yield total_progress, f'downloading {name} ({dataset_progress*100:.1f}%)'
コード例 #4
0
ファイル: widget_run.py プロジェクト: mostamndi/gis4wrf
class RunWidget(QWidget):
    tab_active = pyqtSignal()
    view_wrf_nc_file = pyqtSignal(str)

    def __init__(self, iface: QgisInterface) -> None:
        super().__init__()

        self.iface = iface

        self.options = get_options()
        self.msg_bar = MessageBar(iface)

        self.wps_box, [open_namelist_wps, prepare_only_wps, run_geogrid, run_ungrib, run_metgrid, open_output_wps] = \
            self.create_gbox_with_btns('WPS', [
                'Open Configuration',
                'Prepare only',
                ['Run Geogrid', 'Run Ungrib', 'Run Metgrid'],
                'Visualize Output'
            ])
        self.wrf_box, [open_namelist_wrf, prepare_only_wrf, run_real, run_wrf, open_output_wrf] = \
            self.create_gbox_with_btns('WRF', [
                'Open Configuration',
                'Prepare only',
                ['Run Real', 'Run WRF'],
                'Visualize Output'
            ])
        self.control_box, [kill_program] = self.create_gbox_with_btns(
            'Program control', ['Kill Program'])
        self.control_box.setVisible(False)
        self.stdout_box, self.stdout_textarea, self.stdout_highlighter = \
            self.create_stdout_box()

        vbox = QVBoxLayout()
        vbox.addWidget(self.wps_box)
        vbox.addWidget(self.wrf_box)
        vbox.addWidget(self.control_box)
        vbox.addWidget(self.stdout_box)
        self.stdout_box.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        self.setLayout(vbox)

        open_namelist_wps.clicked.connect(self.on_open_namelist_wps_clicked)
        prepare_only_wps.clicked.connect(self.on_prepare_only_wps_clicked)
        run_geogrid.clicked.connect(self.on_run_geogrid_clicked)
        run_ungrib.clicked.connect(self.on_run_ungrib_clicked)
        run_metgrid.clicked.connect(self.on_run_metgrid_clicked)
        open_output_wps.clicked.connect(self.on_open_output_wps_clicked)

        open_namelist_wrf.clicked.connect(self.on_open_namelist_wrf_clicked)
        prepare_only_wrf.clicked.connect(self.on_prepare_only_wrf_clicked)
        run_real.clicked.connect(self.on_run_real_clicked)
        run_wrf.clicked.connect(self.on_run_wrf_clicked)
        open_output_wrf.clicked.connect(self.on_open_output_wrf_clicked)

        kill_program.clicked.connect(self.on_kill_program_clicked)

    @property
    def project(self) -> Project:
        return self._project

    @project.setter
    def project(self, val: Project) -> None:
        ''' Sets the currently active project. See tab_simulation. '''
        self._project = val

    def prepare_wps_run(self) -> None:
        if not self.options.wps_dir:
            raise RuntimeError('WPS is not available!')
        self.project.prepare_wps_run(self.options.wps_dir)

    def prepare_wrf_run(self) -> None:
        if not self.options.wrf_dir:
            raise RuntimeError('WRF is not available!')
        self.project.prepare_wrf_run(self.options.wrf_dir)

    def on_open_namelist_wps_clicked(self) -> None:
        self.project.update_wps_namelist()
        self.open_editor_dialog(self.project.wps_namelist_path,
                                get_namelist_schema('wps'))

    def on_open_namelist_wrf_clicked(self) -> None:
        # TODO wrap exceptions (from wrf_namelist.py) into message bar messages
        #      (same for prepare_*_run)
        self.project.update_wrf_namelist()
        self.open_editor_dialog(self.project.wrf_namelist_path,
                                get_namelist_schema('wrf'))

    def on_prepare_only_wps_clicked(self) -> None:
        self.prepare_wps_run()

        self.msg_bar.success('Successfully prepared WPS files in ' +
                             self.project.run_wps_folder)

    def on_prepare_only_wrf_clicked(self) -> None:
        self.prepare_wrf_run()

        self.msg_bar.success('Successfully prepared WRF files in ' +
                             self.project.run_wrf_folder)

    def on_run_geogrid_clicked(self) -> None:
        self.prepare_wps_run()

        # TODO move this into core
        self.run_program(self.options.geogrid_exe,
                         self.project.run_wps_folder,
                         supports_mpi=True)

    def on_run_ungrib_clicked(self) -> None:
        self.prepare_wps_run()
        self.run_program(self.options.ungrib_exe,
                         self.project.run_wps_folder,
                         supports_mpi=False)

    def on_run_metgrid_clicked(self) -> None:
        self.prepare_wps_run()
        self.run_program(self.options.metgrid_exe,
                         self.project.run_wps_folder,
                         supports_mpi=True)

    def on_run_real_clicked(self) -> None:
        self.prepare_wrf_run()
        self.run_program(self.options.real_exe,
                         self.project.run_wrf_folder,
                         supports_mpi=True)

    def on_run_wrf_clicked(self) -> None:
        self.prepare_wrf_run()
        self.run_program(self.options.wrf_exe,
                         self.project.run_wrf_folder,
                         supports_mpi=True)

    def on_open_output_wps_clicked(self) -> None:
        path, _ = QFileDialog.getOpenFileName(
            caption='Open WRF NetCDF File',
            directory=self.project.run_wps_folder,
            filter='geo_em*;met_em*')
        if not path:
            return
        self.view_wrf_nc_file.emit(path)

    def on_open_output_wrf_clicked(self) -> None:
        path, _ = QFileDialog.getOpenFileName(
            caption='Open WRF NetCDF File',
            directory=self.project.run_wrf_folder,
            filter='wrfinput*;wrfbdy*;wrfout*')
        if not path:
            return
        self.view_wrf_nc_file.emit(path)

    def on_kill_program_clicked(self) -> None:
        self.dont_report_program_status = True
        self.thread.kill_program()

    def run_program(self, path: str, cwd: str, supports_mpi: bool) -> None:
        self.dont_report_program_status = False
        self.wps_box.setVisible(False)
        self.wrf_box.setVisible(False)
        self.control_box.setVisible(True)
        self.run_program_in_background(path, cwd,
                                       self.on_program_execution_done,
                                       supports_mpi)

    def on_program_execution_done(self, path: str, error: Union[bool, str,
                                                                None]) -> None:
        self.wps_box.setVisible(True)
        self.wrf_box.setVisible(True)
        self.control_box.setVisible(False)
        # The above causes a resize of the program output textarea
        # which requires that we scroll to the bottom again.
        vert_scrollbar = self.stdout_textarea.verticalScrollBar()
        vert_scrollbar.setValue(vert_scrollbar.maximum())

        if self.dont_report_program_status:
            return

        if error is None:
            # An exception that will be reported by the caller after this function returns.
            return
        if error:
            if isinstance(error, str):
                QMessageBox.critical(
                    self.iface.mainWindow(), PLUGIN_NAME,
                    'Program {} failed: {}'.format(os.path.basename(path),
                                                   error))
            else:
                QMessageBox.critical(
                    self.iface.mainWindow(), PLUGIN_NAME,
                    'Program {} failed with errors, check the logs.'.format(
                        os.path.basename(path)))
        else:
            QMessageBox.information(
                self.iface.mainWindow(), PLUGIN_NAME,
                'Program {} finished without errors!'.format(
                    os.path.basename(path)))

    def open_editor_dialog(self, path: str, nml_schema: dict) -> Optional[str]:
        dialog = NmlEditorDialog(path, nml_schema)
        dialog.exec_()

    def create_gbox_with_btns(self, gbox_name: str, btn_names: List[Union[str,List[str]]]) \
            -> Tuple[QGroupBox, List[QPushButton]]:
        vbox = QVBoxLayout()
        btns = []
        for name_or_list in btn_names:
            if isinstance(name_or_list, str):
                name = name_or_list
                btn = QPushButton(name)
                btns.append(btn)
                vbox.addWidget(btn)
            else:
                hbox = QHBoxLayout()
                for name in name_or_list:
                    btn = QPushButton(name)
                    btns.append(btn)
                    hbox.addWidget(btn)
                vbox.addLayout(hbox)
        gbox = QGroupBox(gbox_name)
        gbox.setLayout(vbox)
        return gbox, btns

    def create_stdout_box(
            self) -> Tuple[QGroupBox, QPlainTextEdit, QSyntaxHighlighter]:
        gbox = QGroupBox('Program output')

        vbox = QVBoxLayout()
        gbox.setLayout(vbox)

        text_area = QPlainTextEdit()
        vbox.addWidget(text_area)
        text_area.setReadOnly(True)
        text_area.setWordWrapMode(QTextOption.NoWrap)

        palette = text_area.palette()  # type: QPalette
        palette.setColor(QPalette.Active, QPalette.Base, QColor('#1E1E1E'))
        palette.setColor(QPalette.Inactive, QPalette.Base, QColor('#1E1E1E'))
        text_area.setPalette(palette)

        font = QFont('Monospace', 9)
        font.setStyleHint(QFont.TypeWriter)
        fmt = QTextCharFormat()
        fmt.setFont(font)
        fmt.setForeground(QColor(212, 212, 212))
        text_area.setCurrentCharFormat(fmt)

        doc = text_area.document()
        highlighter = LogSeverityHighlighter(doc)

        return gbox, text_area, highlighter

    def run_program_in_background(self, path: str, cwd: str, on_done: Callable[
        [str, Union[bool, str, None]], None], supports_mpi: bool) -> None:
        self.stdout_textarea.clear()

        # WRF/WPS does not use exit codes to indicate success/failure,
        # therefore in addition we look for a pattern in the program output.
        # TODO add 'FATAL' as patterm
        wrf_error_pattern = 'ERROR'

        # Using QThread and signals (instead of a plain Python thread) is necessary
        # so that the on_done callback is run on the UI thread, instead of the worker thread.
        thread = ProgramThread(path,
                               cwd,
                               wrf_error_pattern,
                               use_mpi=supports_mpi
                               and self.options.mpi_enabled,
                               mpi_processes=self.options.mpi_processes)

        def on_output(out: str) -> None:
            self.stdout_textarea.appendPlainText(out)
            vert_scrollbar = self.stdout_textarea.verticalScrollBar()
            vert_scrollbar.setValue(vert_scrollbar.maximum())

        def on_finished() -> None:
            # When lines come in fast, then the highlighter is not called on each line.
            # Re-highlighting at the end is a work-around to at least have correct
            # highlighting after program termination.
            self.stdout_highlighter.rehighlight()

            if thread.exc_info:
                on_done(path, None)
                raise thread.exc_info[0].with_traceback(*thread.exc_info[1:])
            on_done(path, thread.error)

        thread.output.connect(on_output)
        thread.finished.connect(on_finished)
        thread.start()
        # so that we can kill the program later if requested
        self.thread = thread
コード例 #5
0
    def __init__(self, iface) -> None:
        super().__init__()

        self.iface = iface
        self.options = get_options()
        self.msg_bar = MessageBar(iface)

        vbox = QVBoxLayout()
        self.setLayout(vbox)

        hbox = QHBoxLayout()
        vbox.addLayout(hbox)

        hbox.addWidget(QLabel('Dataset: '))
        self.cbox_dataset = QComboBox()
        self.cbox_dataset.addItem('-')
        for index, (dataset_name,
                    dataset_label) in enumerate(met_datasets.items()):
            self.cbox_dataset.addItem(dataset_name, dataset_name)
            self.cbox_dataset.setItemData(index + 1, dataset_label,
                                          Qt.ToolTipRole)
        self.cbox_dataset.currentIndexChanged.connect(self.on_dataset_changed)
        hbox.addWidget(self.cbox_dataset)

        hbox_product_name = QHBoxLayout()
        vbox.addLayout(hbox_product_name)
        hbox_product_name.addWidget(QLabel('Product: '))
        self.cbox_product = QComboBox()
        self.cbox_product.currentIndexChanged.connect(self.on_product_changed)
        hbox_product_name.addWidget(self.cbox_product)

        hbox_start_datetime = QHBoxLayout()
        vbox.addLayout(hbox_start_datetime)
        self.dedit_start_date = QDateTimeEdit()
        self.dedit_start_date.setCalendarPopup(True)
        hbox_start_datetime.addWidget(QLabel('Start: '))
        hbox_start_datetime.addWidget(self.dedit_start_date)

        hbox_end_datetime = QHBoxLayout()
        vbox.addLayout(hbox_end_datetime)
        self.dedit_end_date = QDateTimeEdit()
        self.dedit_end_date.setCalendarPopup(True)
        hbox_end_datetime.addWidget(QLabel('End: '))
        hbox_end_datetime.addWidget(self.dedit_end_date)

        gbox_extent = QGroupBox('Extent')
        vbox.addWidget(gbox_extent)
        vbox_extent = QVBoxLayout()
        gbox_extent.setLayout(vbox_extent)

        hbox_extent = QHBoxLayout()
        vbox_extent.addLayout(hbox_extent)
        self.radio_global = QRadioButton('Global')
        self.radio_global.toggled.connect(self.on_extent_radio_button_clicked)
        hbox_extent.addWidget(self.radio_global)
        self.radio_subset = QRadioButton('Subset')
        self.radio_subset.toggled.connect(self.on_extent_radio_button_clicked)
        hbox_extent.addWidget(self.radio_subset)

        self.widget_extent = QWidget()
        vbox_extent.addWidget(self.widget_extent)
        grid_extent = QGridLayout()
        self.widget_extent.setLayout(grid_extent)
        self.widget_extent.hide()
        self.top = add_grid_lineedit(grid_extent,
                                     0,
                                     'North Latitude',
                                     LAT_VALIDATOR,
                                     '°',
                                     required=True)
        self.right = add_grid_lineedit(grid_extent,
                                       1,
                                       'East Longitude',
                                       LON_VALIDATOR,
                                       '°',
                                       required=True)
        self.left = add_grid_lineedit(grid_extent,
                                      2,
                                      'West Longitude',
                                      LON_VALIDATOR,
                                      '°',
                                      required=True)
        self.bottom = add_grid_lineedit(grid_extent,
                                        3,
                                        'South Latitude',
                                        LAT_VALIDATOR,
                                        '°',
                                        required=True)
        self.extent_from_active_layer = QPushButton('Set from Active Layer')
        grid_extent.addWidget(self.extent_from_active_layer, 4, 1)
        self.extent_from_active_layer.clicked.connect(
            self.on_extent_from_active_layer_button_clicked)
        self.radio_global.setChecked(True)

        self.tree = QListWidget()
        vbox_tree = QVBoxLayout()
        vbox.addLayout(vbox_tree)
        vbox_tree.addWidget(self.tree)

        self.btn_download = QPushButton('Download')
        self.btn_download.clicked.connect(self.on_download_button_clicked)
        vbox.addWidget(self.btn_download)

        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, PROGRESS_BAR_MAX)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.hide()
        vbox.addWidget(self.progress_bar)
コード例 #6
0
class MetToolsDownloadManager(QWidget):
    def __init__(self, iface) -> None:
        super().__init__()

        self.iface = iface
        self.options = get_options()
        self.msg_bar = MessageBar(iface)

        vbox = QVBoxLayout()
        self.setLayout(vbox)

        hbox = QHBoxLayout()
        vbox.addLayout(hbox)

        hbox.addWidget(QLabel('Dataset: '))
        self.cbox_dataset = QComboBox()
        self.cbox_dataset.addItem('-')
        for index, (dataset_name,
                    dataset_label) in enumerate(met_datasets.items()):
            self.cbox_dataset.addItem(dataset_name, dataset_name)
            self.cbox_dataset.setItemData(index + 1, dataset_label,
                                          Qt.ToolTipRole)
        self.cbox_dataset.currentIndexChanged.connect(self.on_dataset_changed)
        hbox.addWidget(self.cbox_dataset)

        hbox_product_name = QHBoxLayout()
        vbox.addLayout(hbox_product_name)
        hbox_product_name.addWidget(QLabel('Product: '))
        self.cbox_product = QComboBox()
        self.cbox_product.currentIndexChanged.connect(self.on_product_changed)
        hbox_product_name.addWidget(self.cbox_product)

        hbox_start_datetime = QHBoxLayout()
        vbox.addLayout(hbox_start_datetime)
        self.dedit_start_date = QDateTimeEdit()
        self.dedit_start_date.setCalendarPopup(True)
        hbox_start_datetime.addWidget(QLabel('Start: '))
        hbox_start_datetime.addWidget(self.dedit_start_date)

        hbox_end_datetime = QHBoxLayout()
        vbox.addLayout(hbox_end_datetime)
        self.dedit_end_date = QDateTimeEdit()
        self.dedit_end_date.setCalendarPopup(True)
        hbox_end_datetime.addWidget(QLabel('End: '))
        hbox_end_datetime.addWidget(self.dedit_end_date)

        gbox_extent = QGroupBox('Extent')
        vbox.addWidget(gbox_extent)
        vbox_extent = QVBoxLayout()
        gbox_extent.setLayout(vbox_extent)

        hbox_extent = QHBoxLayout()
        vbox_extent.addLayout(hbox_extent)
        self.radio_global = QRadioButton('Global')
        self.radio_global.toggled.connect(self.on_extent_radio_button_clicked)
        hbox_extent.addWidget(self.radio_global)
        self.radio_subset = QRadioButton('Subset')
        self.radio_subset.toggled.connect(self.on_extent_radio_button_clicked)
        hbox_extent.addWidget(self.radio_subset)

        self.widget_extent = QWidget()
        vbox_extent.addWidget(self.widget_extent)
        grid_extent = QGridLayout()
        self.widget_extent.setLayout(grid_extent)
        self.widget_extent.hide()
        self.top = add_grid_lineedit(grid_extent,
                                     0,
                                     'North Latitude',
                                     LAT_VALIDATOR,
                                     '°',
                                     required=True)
        self.right = add_grid_lineedit(grid_extent,
                                       1,
                                       'East Longitude',
                                       LON_VALIDATOR,
                                       '°',
                                       required=True)
        self.left = add_grid_lineedit(grid_extent,
                                      2,
                                      'West Longitude',
                                      LON_VALIDATOR,
                                      '°',
                                      required=True)
        self.bottom = add_grid_lineedit(grid_extent,
                                        3,
                                        'South Latitude',
                                        LAT_VALIDATOR,
                                        '°',
                                        required=True)
        self.extent_from_active_layer = QPushButton('Set from Active Layer')
        grid_extent.addWidget(self.extent_from_active_layer, 4, 1)
        self.extent_from_active_layer.clicked.connect(
            self.on_extent_from_active_layer_button_clicked)
        self.radio_global.setChecked(True)

        self.tree = QListWidget()
        vbox_tree = QVBoxLayout()
        vbox.addLayout(vbox_tree)
        vbox_tree.addWidget(self.tree)

        self.btn_download = QPushButton('Download')
        self.btn_download.clicked.connect(self.on_download_button_clicked)
        vbox.addWidget(self.btn_download)

        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, PROGRESS_BAR_MAX)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.hide()
        vbox.addWidget(self.progress_bar)

    def on_dataset_changed(self, index: int):
        self.cbox_product.clear()
        dataset_name = self.cbox_dataset.currentData()
        if dataset_name is None:
            return
        auth = (self.options.rda_username, self.options.rda_password)
        self.products = get_met_products(dataset_name, auth)
        for product in self.products.keys():
            self.cbox_product.addItem(product, product)

    def on_product_changed(self, index: int):
        if index == -1:
            return

        self.tree.clear()
        product_name = self.cbox_product.currentData()
        current_avail_vars = self.products[product_name]
        dates = []
        for name in current_avail_vars.keys():
            item = QListWidgetItem(current_avail_vars[name]['label'])
            item.setData(Qt.UserRole, name)
            item.setCheckState(Qt.Checked)
            self.tree.addItem(item)
            dates.append(current_avail_vars[name]['start_date'])
            dates.append(current_avail_vars[name]['end_date'])
        date_min = min(dates)
        date_max = max(dates)

        for dt_input in [self.dedit_start_date, self.dedit_end_date]:
            dt_input.setDateTimeRange(
                QDateTime(QDate(date_min.year, date_min.month, date_min.day),
                          QTime(date_min.hour, date_min.minute)),
                QDateTime(QDate(date_max.year, date_max.month, date_max.day),
                          QTime(date_max.hour, date_max.minute)))

        min_dt = self.dedit_start_date.minimumDateTime()
        max_dt = self.dedit_start_date.maximumDateTime()
        self.dedit_start_date.setDateTime(min_dt)
        self.dedit_end_date.setDateTime(max_dt)

    def on_download_button_clicked(self):
        param_names = []
        for index in range(self.tree.count()):
            item = self.tree.item(index)
            if item.checkState() == Qt.Checked:
                param_name = item.data(Qt.UserRole)
                param_names.append(param_name)

        dataset_name = self.cbox_dataset.currentData()
        product_name = self.cbox_product.currentData()
        start_date = self.dedit_start_date.dateTime().toPyDateTime()
        end_date = self.dedit_end_date.dateTime().toPyDateTime()
        if dataset_name is None or product_name is None:
            raise UserError('Dataset/Product not selected')

        args = [
            self.options.met_dir, dataset_name, product_name, start_date,
            end_date
        ]
        if is_met_dataset_downloaded(*args):
            reply = QMessageBox.question(self.iface.mainWindow(
            ), 'Existing dataset', (
                'You already downloaded data with the selected dataset/product/date/time combination. '
                'If you continue, this data will be removed.\n'
                'Location: {}'.format(get_met_dataset_path(*args))),
                                         QMessageBox.Ok, QMessageBox.Cancel)
            if reply == QMessageBox.Cancel:
                return

        lat_north = self.top.value()
        lat_south = self.bottom.value()
        lon_west = self.left.value()
        lon_east = self.right.value()
        auth = (self.options.rda_username, self.options.rda_password)

        thread = TaskThread(lambda: download_met_dataset(
            self.options.met_dir, auth, dataset_name, product_name,
            param_names, start_date, end_date, lat_south, lat_north, lon_west,
            lon_east),
                            yields_progress=True)
        thread.started.connect(self.on_started_download)
        thread.progress.connect(self.on_progress_download)
        thread.finished.connect(self.on_finished_download)
        thread.succeeded.connect(self.on_successful_download)
        thread.failed.connect(reraise)
        thread.start()

    def on_started_download(self) -> None:
        self.btn_download.hide()
        self.progress_bar.show()

    def on_progress_download(self, progress: float, status: str) -> None:
        bar_value = int(progress * PROGRESS_BAR_MAX)
        self.progress_bar.setValue(bar_value)
        self.progress_bar.repaint()  # otherwise just updates in 1% steps
        if status == 'submitted':
            self.msg_bar.info(
                'Met dataset download request submitted successfully, waiting until available for download...'
            )
        elif status == 'ready':
            self.msg_bar.info(
                'Met dataset download request is now ready, downloading...')
        logger.debug(f'Met data download: {progress*100:.1f}% - {status}')

    def on_finished_download(self) -> None:
        self.btn_download.show()
        self.progress_bar.hide()

    def on_successful_download(self) -> None:
        self.msg_bar.success('Meteorological dataset downloaded successfully.')
        Broadcast.met_datasets_updated.emit()

    def on_extent_radio_button_clicked(self):
        if self.radio_global.isChecked():
            self.top.set_value(90)
            self.bottom.set_value(-90)
            self.left.set_value(-180)
            self.right.set_value(180)
            self.top.setDisabled(True)
            self.bottom.setDisabled(True)
            self.left.setDisabled(True)
            self.right.setDisabled(True)
            self.widget_extent.hide()

        elif self.radio_subset.isChecked():
            self.widget_extent.show()
            self.top.setDisabled(False)
            self.bottom.setDisabled(False)
            self.left.setDisabled(False)
            self.right.setDisabled(False)

    def on_extent_from_active_layer_button_clicked(self):
        layer = self.iface.activeLayer()  # type: Optional[QgsMapLayer]
        if layer is None:
            return
        layer_crs = CRS(layer.crs().toProj4())
        target_crs = CRS('+proj=latlong +datum=WGS84')
        extent = layer.extent()  # type: QgsRectangle
        bbox = rect_to_bbox(extent)
        bbox_geo = layer_crs.transform_bbox(bbox, target_crs.srs)
        padding = 5  # degrees
        lat_south = max(bbox_geo.miny - 5, -90)
        lat_north = min(bbox_geo.maxy + 5, 90)
        lon_west = max(bbox_geo.minx - 5, -180)
        lon_east = min(bbox_geo.maxx + 5, 180)
        self.bottom.set_value(lat_south)
        self.top.set_value(lat_north)
        self.left.set_value(lon_west)
        self.right.set_value(lon_east)
コード例 #7
0
class GeoToolsDownloadManager(QWidget):
    def __init__(self, iface) -> None:
        super().__init__()

        self.options = get_options()
        self.msg_bar = MessageBar(iface)

        self.tree_widget = QTreeWidget()
        self.populate_tree()

        self.select_mandatory_hires_button = QPushButton(
            'Select Mandatory Fields in Highest Resolution')
        self.select_mandatory_hires_button.clicked.connect(
            self.on_select_mandatory_hires_button_clicked)

        self.select_mandatory_lores_button = QPushButton(
            'Select Mandatory Fields in Lowest Resolution')
        self.select_mandatory_lores_button.clicked.connect(
            self.on_select_mandatory_lores_button_clicked)

        self.download_button = QPushButton('Download Selected Datasets')
        self.download_button.clicked.connect(self.on_download_button_clicked)

        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 0)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.hide()

        vbox = QVBoxLayout()
        vbox.addWidget(self.tree_widget)
        vbox.addWidget(self.select_mandatory_hires_button)
        vbox.addWidget(self.select_mandatory_lores_button)
        vbox.addWidget(self.download_button)
        vbox.addWidget(self.progress_bar)
        self.setLayout(vbox)

    def populate_tree(self) -> None:
        self.tree_widget.setHeaderItem(
            QTreeWidgetItem(['ID', 'Description', 'Resolution']))
        self.tree_widget.setRootIsDecorated(False)
        self.tree_widget.setSortingEnabled(True)
        for id, (description, resolution) in geo_datasets.items():
            item = QTreeWidgetItem(self.tree_widget)

            item.setText(0, id)
            item.setData(0, Qt.UserRole, id)
            item.setCheckState(0, Qt.Unchecked)
            if is_geo_dataset_downloaded(id, self.options.geog_dir):
                item.setFlags(Qt.NoItemFlags)
                item.setToolTip(
                    0, 'Dataset downloaded in: {}'.format(
                        get_geo_dataset_path(id, self.options.geog_dir)))
            else:
                item.setToolTip(0, id)

            item.setText(1, description)
            item.setToolTip(1, description)

            if isinstance(resolution, str):
                item.setText(2, resolution)
            else:
                item.setText(2, formatted_dd_to_dms(resolution))
                item.setToolTip(2, '{}°'.format(resolution))

            item.setFlags(item.flags() | Qt.ItemIsUserCheckable)

    def on_select_mandatory_lores_button_clicked(self):
        self.select_datasets(geo_datasets_mandatory_lores)

    def on_select_mandatory_hires_button_clicked(self):
        self.select_datasets(geo_datasets_mandatory_hires)

    def select_datasets(self, names: List[str]) -> None:
        items = self.get_items()
        for name, item in items.items():
            item.setCheckState(0,
                               Qt.Checked if name in names else Qt.Unchecked)

    def get_items(self) -> Dict[str, QTreeWidgetItem]:
        items = {}  # type: Dict[str,QTreeWidgetItem]
        for index in range(self.tree_widget.topLevelItemCount()):
            item = self.tree_widget.topLevelItem(index)
            items[item.data(0, Qt.UserRole)] = item
        return items

    def on_download_button_clicked(self) -> None:
        datasets_to_download = []
        for name, item in self.get_items().items():
            if item.checkState(0) == Qt.Checked:
                datasets_to_download.append(name)

        # TODO report progress
        thread = TaskThread(
            lambda: self.download_datasets(datasets_to_download))
        thread.started.connect(self.on_started_download)
        thread.finished.connect(self.on_finished_download)
        thread.succeeded.connect(self.on_successful_download)
        thread.failed.connect(reraise)
        thread.start()

    def on_started_download(self):
        self.download_button.hide()
        self.progress_bar.show()
        self.tree_widget.setEnabled(False)

    def on_finished_download(self) -> None:
        self.download_button.show()
        self.progress_bar.hide()
        self.tree_widget.setEnabled(True)
        self.tree_widget.clear()
        self.populate_tree()
        Broadcast.geo_datasets_updated.emit()

    def on_successful_download(self) -> None:
        self.msg_bar.success('Geographical datasets downloaded successfully.')

    def download_datasets(self, dataset_names: List[str]) -> None:
        for name in dataset_names:
            download_and_extract_geo_dataset(name, self.options.geog_dir)