Exemple #1
0
    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):
                raise UserError('Program {} failed: {}'.format(
                    os.path.basename(path), error))
            else:
                raise UserError(
                    '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)))
Exemple #2
0
 def run_convert_to_wps_binary(self) -> None:
     msg_bar = self.iface.messageBar()  # type: QgsMessageBar
     layer = self.iface.activeLayer()  # type: QgsMapLayer
     if layer is None:
         raise UserError('No layer selected, use the "Layers" panel')
     source = layer.source()
     if not os.path.exists(source):
         # Currently in-memory layers are not supported, but QGIS in most cases saves
         # layers to temporary files on disk during processing operations, so this is not a big issue.
         raise UnsupportedError(
             'Only layers that exist on the filesystem are supported')
     reply = QMessageBox.question(self.iface.mainWindow(), 'Layer type',
                                  "Is this layer's data categorical?",
                                  QMessageBox.Yes, QMessageBox.No)
     is_categorical = reply == QMessageBox.Yes
     out_dir = QFileDialog.getExistingDirectory(
         caption='Select WPS Binary File Output Folder')
     if not out_dir:
         return
     if not ensure_folder_empty(out_dir, self.iface):
         return
     output = convert_to_wps_binary(source,
                                    out_dir,
                                    is_categorical,
                                    strict_datum=False)
     msg_bar.pushInfo(
         PLUGIN_NAME,
         'WPS Binary Format files created in {}'.format(out_dir))
     if output.datum_mismatch:
         msg_bar.pushWarning(
             PLUGIN_NAME,
             'Input layer had an unexpected datum, no datum shift was performed. Expected: {}, Actual: {}'
             .format(output.datum_mismatch.expected,
                     output.datum_mismatch.actual))
Exemple #3
0
    def create_domain_crs(self) -> CRS:
        proj = self.get_proj_kwargs()
        if proj is None:
            raise UserError('Incomplete projection definition')

        map_proj = proj['map_proj']

        if map_proj == 'lambert':
            if self.center_lat.is_valid():
                origin_lat = self.center_lat.value()
            else:
                origin_lat = 0
            crs = CRS.create_lambert(proj['truelat1'], proj['truelat2'], LonLat(proj['stand_lon'], origin_lat))
        elif map_proj == 'polar':
            crs = CRS.create_polar(proj['truelat1'], proj['stand_lon'])
        elif map_proj == 'mercator':
            if self.center_lon.is_valid():
                origin_lon = self.center_lon.value()
            else:
                origin_lon = 0
            crs = CRS.create_mercator(proj['truelat1'], origin_lon)
        elif map_proj == 'lat-lon':
            crs = CRS.create_lonlat()
        else:
            assert False, 'unknown proj: ' + map_proj
        return crs
Exemple #4
0
 def on_export_geogrid_namelist_button_clicked(self):
     if not self.update_project():
         raise UserError('Domain configuration invalid, check fields')
     file_path, _ = QFileDialog.getSaveFileName(caption='Save WPS namelist as', \
                                                directory='namelist.wps')
     if not file_path:
         return
     wps_namelist = convert_project_to_wps_namelist(self.project)
     write_namelist(wps_namelist, file_path)
Exemple #5
0
    def on_set_layer_extent_button_clicked(self):
        if not self.resolution.is_valid():
            return
        layer = self.iface.activeLayer()  # type: QgsMapLayer
        if layer is None:
            print('foo')
            raise UserError('No layer selected, use the "Layers" panel')

        layer_crs = layer.crs()  # type: QgsCoordinateReferenceSystem

        extent = layer.extent()  # type: QgsRectangle
        self.set_domain_to_extent(layer_crs, extent)
Exemple #6
0
    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()
Exemple #7
0
    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'

        use_mpi = supports_mpi and self.options.mpi_enabled
        if use_mpi and '-nompi' in path:
            raise UserError(
                'MPI is enabled but your WRF/WPS distribution does not support it.\n'
                'In the plugin options, either choose/download a distribution with MPI support or disable MPI'
            )

        # 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=use_mpi,
                               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
 def on_ok_clicked(self) -> None:
     if not self.paths:
         raise UserError('No GRIB files were added')
     if not self.interval_input.is_valid():
         raise UserError('Interval must be an integer above 0')
     if self.start_date == self.end_date:
         raise UserError('Start date cannot be the same as end date')
     if self.start_date > self.end_date:
         raise UserError('Start date cannot be after the end date')
     if not self.vtable_path:
         raise UserError('No VTable file selected')
     if not os.path.exists(os.path.join(self.vtable_dir, self.vtable_path)):
         raise UserError('VTable file does not exist')
     self.accept()
Exemple #9
0
    def create_domain_crs(self) -> CRS:
        proj = self.get_proj_kwargs()
        if proj is None:
            raise UserError('Incomplete projection definition')

        origin_valid = all(
            map(lambda w: w.is_valid(), [self.center_lat, self.center_lon]))
        if origin_valid:
            origin = LonLat(self.center_lon.value(), self.center_lat.value())
        else:
            origin = LonLat(0, 0)

        if proj['map_proj'] == 'lambert':
            crs = CRS.create_lambert(proj['truelat1'], proj['truelat2'],
                                     origin)
        elif proj['map_proj'] == 'lat-lon':
            crs = CRS.create_lonlat()
        else:
            assert False, 'unknown proj: ' + proj['map_proj']
        return crs
Exemple #10
0
 def kill_program(self):
     if self.pid == -1:
         raise UserError('Program not started yet')
     os.kill(self.pid, signal.SIGTERM)