示例#1
0
    def has_same_projection_as(self, other_layer):
        """
        Check if the layer uses the same projection as another layer

        :param other_layer: layer to compare with
        :type other_layer: QgsVectorLayer
        :returns: (bool, str):

            (True, msg)
                if the layers use the same coordinates,
                specifying the projection in the message

            (False, msg)
                if they use different projections,
                specifying the projections in the message
        """
        this_layer_projection = self.layer.crs().geographicCrsAuthId()
        other_layer_projection = other_layer.crs().geographicCrsAuthId()
        if (this_layer_projection != other_layer_projection):
            msg = tr("The two layers use different coordinate"
                     " reference systems (%s vs %s)" %
                     (this_layer_projection, other_layer_projection))
            return False, msg
        else:
            msg = tr("Both layers use the following coordinate"
                     " reference system: %s" % this_layer_projection)
            return True, msg
 def on_clone_btn_clicked(self):
     title = (self.selected_proj_def['title'] +
              ' (copy)' if 'title' in self.selected_proj_def else '(copy)')
     title, ok = QInputDialog().getText(self,
                                        tr('Assign a title'),
                                        tr('Project definition title'),
                                        text=title)
     if ok:
         self.add_proj_def(title, self.selected_proj_def)
示例#3
0
    def work(self):
        """
        :returns:
            (fname, msg), where fname is the name of the target csv file that
            will store the downloaded data, and msg is a message describing if
            the download is performed successfully
        :raises: SvNetworkError
        """
        self.set_message.emit(
            tr('Waiting for the OpenQuake Platform to export the data...'))
        self.toggle_show_progress.emit(False)
        page = self.downloader.host + PLATFORM_EXPORT_VARIABLES_DATA
        data = dict(sv_variables_ids=self.sv_variables_ids,
                    export_geometries=self.load_geometries,
                    country_iso_codes=self.country_iso_codes)
        result = self.downloader.sess.post(page, data=data, stream=True)

        self.set_message.emit(tr('Downloading data from platform'))

        self.toggle_show_progress.emit(True)
        if result.status_code == 200:
            # save csv on a temporary file
            fd, fname = tempfile.mkstemp(suffix='.csv')
            os.close(fd)
            # All the fields of the csv file will be considered as text fields
            # unless a .csvt file with the same name as the .csv file is used
            # to specify the field types.
            # For the type descriptor, use the same name as the csv file
            fname_types = fname.split('.')[0] + '.csvt'
            # We expect iso, country_name, v1, v2, ... vn
            # Count variables ids
            sv_variables_count = len(self.sv_variables_ids.split(','))
            # build the string that describes data types for the csv
            types_string = '"String","String"' + ',"Real"' * sv_variables_count
            if self.load_geometries:
                types_string += ',"String"'
            with open(fname_types, 'w', newline='') as csvt:
                csvt.write(types_string)
            with open(fname, 'w', newline='') as csv_file:
                n_countries_to_download = len(self.country_iso_codes)
                n_downloaded_countries = 0
                for line in result.iter_lines():
                    if self.is_killed:
                        raise UserAbortedNotification(
                            'Download aborted by user')

                    csv_file.write(line.decode('utf8') + os.linesep)
                    n_downloaded_countries += 1
                    progress = (1.0 * n_downloaded_countries /
                                n_countries_to_download * 100)
                    self.progress.emit(progress)

                msg = 'The socioeconomic data has been saved into %s' % fname
                return fname, msg
        else:
            raise SvNetworkError(result.content)
 def load_loss_layer(self, loss_layer_path):
     # Load loss layer
     if self.loss_layer_is_vector:
         loss_layer = QgsVectorLayer(loss_layer_path, tr('Loss map'), 'ogr')
         if not loss_layer.geometryType() == QGis.Point:
             msg = 'Loss map must contain points'
             log_msg(msg, level='C', message_bar=self.iface.messageBar())
             return False
     else:
         loss_layer = QgsRasterLayer(loss_layer_path, tr('Loss map'))
     # Add loss layer to registry
     if loss_layer.isValid():
         QgsMapLayerRegistry.instance().addMapLayer(loss_layer)
     else:
         msg = 'Invalid loss map'
         log_msg(msg, level='C', message_bar=self.iface.messageBar())
         return None
     # Zoom depending on the zonal layer's extent
     return loss_layer
示例#5
0
def calculate_raster_stats(loss_layer, zonal_layer, iface):
    """
    In case the layer containing loss data is raster, use
    QgsZonalStatistics to calculate NUM_POINTS, SUM and AVG
    values for each zone
    """
    zonal_statistics = QgsZonalStatistics(
        zonal_layer,
        loss_layer.dataProvider().dataSourceUri())
    progress_dialog = QProgressDialog(tr('Calculating zonal statistics'),
                                      tr('Abort...'), 0, 0)
    zonal_statistics.calculateStatistics(progress_dialog)
    # TODO: This is not giving any warning in case no loss points are
    #       contained by any of the zones
    if progress_dialog.wasCanceled():
        msg = ("You aborted aggregation, so there are"
               " no data for analysis. Exiting...")
        log_msg(msg, level='C', message_bar=iface.messageBar())
    # FIXME: We probably need to return something more
    return (loss_layer, zonal_layer)
    def set_labels(self):
        self.situation_lbl.setVisible(self.exists_on_platform)
        self.question_lbl.setVisible(self.exists_on_platform)
        for button in self.upload_action.buttons():
            button.setVisible(self.exists_on_platform)

        if self.update_radio.isChecked():
            explaination_lbl = tr(
                'The current project definition will be added to the '
                'OpenQuake Platform project\nidentified as "%s"'
                % self.suppl_info['platform_layer_id'])
            title_lbl = tr('Project title')
            description_lbl = tr('Project description')
            self.title_le.setEnabled(False)
            self.description_te.setEnabled(False)
            self.zone_label_field_cbx.setEnabled(False)
            self.license_cbx.setEnabled(False)
            self.set_zone_label_field()
            self.set_license()
        else:
            explaination_lbl = tr(
                'A new layer will be created on the OpenQuake Platform.')
            title_lbl = tr('New layer title')
            description_lbl = tr('New layer abstract')
            self.title_le.setEnabled(True)
            self.description_te.setEnabled(True)
            self.zone_label_field_cbx.setEnabled(True)
            self.license_cbx.setEnabled(True)

        self.title_lbl.setText(title_lbl)
        self.description_lbl.setText(description_lbl)
        self.explaination_lbl.setText(explaination_lbl)
 def load_zonal_layer(self, zonal_layer_path):
     # Load zonal layer
     zonal_layer = QgsVectorLayer(zonal_layer_path, tr('Zonal data'), 'ogr')
     if not zonal_layer.geometryType() == QGis.Polygon:
         msg = 'Zonal layer must contain zone polygons'
         log_msg(msg, level='C', message_bar=self.iface.messageBar())
         return False
     # Add zonal layer to registry
     if zonal_layer.isValid():
         QgsMapLayerRegistry.instance().addMapLayer(zonal_layer)
     else:
         msg = 'Invalid zonal layer'
         log_msg(msg, level='C', message_bar=self.iface.messageBar())
         return None
     return zonal_layer
示例#8
0
    def __init__(self, loss_layer, zonal_layer):
        QDialog.__init__(self)
        # Set up the user interface from Designer.
        self.setupUi(self)
        self.ok_button = self.buttonBox.button(QDialogButtonBox.Ok)
        self.set_ok_button()

        # if the loss layer does not contain an attribute specifying the ids of
        # zones, the user must not be forced to select such attribute, so we
        # add an "empty" option to the combobox
        self.zone_id_attr_name_loss_cbox.addItem(tr("Use zonal geometries"))

        # if the zonal_layer doesn't have a field containing a unique zone id,
        # the user can choose to add such unique id
        self.zone_id_attr_name_zone_cbox.addItem(
            tr("Add field with unique zone id"))

        # Load in the comboboxes only the names of the attributes compatible
        # with the following analyses
        for field in loss_layer.fields():
            # for the zone id accept both numeric or textual fields
            self.zone_id_attr_name_loss_cbox.addItem(field.name())
            # Accept only numeric fields to contain loss data
            if field.typeName() in NUMERIC_FIELD_TYPES:
                self.loss_attrs_multisel.add_unselected_items([field.name()])

        self.zone_id_attr_name_loss_cbox.setCurrentIndex(0)

        for field in zonal_layer.fields():
            # for the zone id accept both numeric or textual fields
            self.zone_id_attr_name_zone_cbox.addItem(field.name())
            # by default, set the selection to the first textual field

        self.zone_id_attr_name_zone_cbox.setCurrentIndex(0)

        self.loss_attrs_multisel.selection_changed.connect(self.set_ok_button)
示例#9
0
 def downloadContent(self, reply):
     self.stop()
     home = os.path.expanduser('~')
     downloads = os.path.join(home, 'Downloads')
     download = os.path.join(home, 'Download')
     if os.path.isdir(downloads):
         dest_dir = downloads
     if os.path.isdir(download):
         dest_dir = download
     if not dest_dir:
         dest_dir = QFileDialog.getExistingDirectory(
             self, tr("Select the destination folder"), home,
             QFileDialog.ShowDirsOnly)
     if not dest_dir:
         return
     try:
         file_name = reply.rawHeader('Content-Disposition').split('=')[1]
         file_name = str(file_name).strip('"')
     except Exception as exc:
         header_pairs = reply.rawHeaderPairs()
         self.gem_api.common.error(
             'Unable to get the file name from headers: %s\n'
             'Exception: %s' % (header_pairs, str(exc)))
         return
     file_content = str(reply.readAll())
     # From
     # http://doc.qt.io/archives/qt-4.8/qwebpage.html#unsupportedContent
     # "The receiving slot is responsible for deleting the reply"
     reply.close()
     reply.deleteLater()
     file_fullpath = os.path.join(dest_dir, file_name)
     if os.path.exists(file_fullpath):
         name, ext = os.path.splitext(file_name)
         i = 1
         while True:
             file_fullpath = os.path.join(dest_dir,
                                          '%s_%s%s' % (name, i, ext))
             if not os.path.exists(file_fullpath):
                 break
             i += 1
     with open(file_fullpath, "w", newline='') as f:
         f.write(file_content)
     self.gem_api.common.info('File downloaded as: %s' % file_fullpath)
 def load_zonal_layer(self, zonal_layer_path, make_a_copy=False):
     # Load zonal layer
     zonal_layer = QgsVectorLayer(zonal_layer_path, tr('Zonal data'), 'ogr')
     if not zonal_layer.geometryType() == QGis.Polygon:
         msg = 'Zonal layer must contain zone polygons'
         log_msg(msg, level='C', message_bar=self.iface.messageBar())
         return False
     if make_a_copy:
         # Make a copy, where stats will be added
         zonal_layer_plus_stats = ProcessLayer(
             zonal_layer).duplicate_in_memory()
     else:
         zonal_layer_plus_stats = zonal_layer
     # Add zonal layer to registry
     if zonal_layer_plus_stats.isValid():
         QgsMapLayerRegistry.instance().addMapLayer(zonal_layer_plus_stats)
     else:
         msg = 'Invalid zonal layer'
         log_msg(msg, level='C', message_bar=self.iface.messageBar())
         return None
     return zonal_layer_plus_stats
示例#11
0
 def progress_cb(self, param, current, total):
     if self.is_killed:
         raise UserAbortedNotification('Upload aborted by user')
     if not param:
         return
     # check out
     # http://tcd.netinf.eu/doc/classnilib_1_1encode_1_1MultipartParam.html
     # for a complete list of the properties param provides to you
     progress = float(current) / float(total) * 100
     self.progress.emit(progress)
     # here we remove the progress bar and the cancel button since the
     # server side processing kicks in at 100% but we allow for some
     # rounding.
     if progress > 99:
         self.toggle_show_progress.emit(False)
         self.toggle_show_cancel.emit(False)
         self.set_message.emit(self.upload_size_msg + ' ' +
                               tr('(processing on Platform)'))
     if DEBUG:
         print("PROGRESS: {0} ({1}) - {2:d}/{3:d} - {4:.2f}%").format(
             param.name, param.filename, current, total, progress)
示例#12
0
def purge_zones_without_loss_points(zonal_layer, loss_attrs_dict, iface):
    """
    Delete from the zonal layer the zones that contain no loss points
    """
    tot_zones = zonal_layer.featureCount()
    msg = tr("Purging zones containing no loss points...")
    msg_bar_item, progress = create_progress_message_bar(
        iface.messageBar(), msg)

    with edit(zonal_layer):
        for current_zone, zone_feature in enumerate(zonal_layer.getFeatures()):
            progress_percent = current_zone / float(tot_zones) * 100
            progress.setValue(progress_percent)
            # save the ids of the zones to purge (which contain no loss
            # points)
            if zone_feature[loss_attrs_dict['count']] == 0:
                zonal_layer.deleteFeature(zone_feature.id())

    clear_progress_message_bar(iface.messageBar(), msg_bar_item)

    msg = "Zones containing no loss points were deleted"
    log_msg(msg, level='W', message_bar=iface.messageBar())
    return zonal_layer
示例#13
0
    def work(self):
        # To upload the layer to the platform, we need it to be a shapefile.
        # So we need to check if the active layer is stored as a shapefile and,
        # if it isn't, save it as a shapefile
        data_file = '%s%s' % (self.file_stem, '.shp')
        projection = self.current_layer.crs().geographicCRSAuthId()
        if (self.current_layer.storageType() == 'ESRI Shapefile'
                and projection == 'EPSG:4326'):
            # copy the shapefile (with all its files) into the temporary
            # directory, using self.file_stem as name
            self.set_message.emit(tr('Preparing the files to be uploaded...'))
            layer_source = self.current_layer.publicSource()
            layer_source_stem = layer_source[:-4]  # remove '.shp'
            for ext in ['shp', 'dbf', 'shx', 'prj']:
                src = "%s.%s" % (layer_source_stem, ext)
                dst = "%s.%s" % (self.file_stem, ext)
                shutil.copyfile(src, dst)
        else:
            # if it's not a shapefile or it is in a bad projection,
            # we need to build a shapefile from it
            self.set_message.emit(
                tr('Writing the shapefile to be uploaded...'))
            result = save_layer_as_shapefile(
                self.current_layer,
                data_file,
                crs=QgsCoordinateReferenceSystem(
                    4326, QgsCoordinateReferenceSystem.EpsgCrsId))
            if result != QgsVectorFileWriter.NoError:
                raise RuntimeError('Could not save shapefile')
        file_size_mb = os.path.getsize(data_file)
        file_size_mb += os.path.getsize(self.file_stem + '.shx')
        file_size_mb += os.path.getsize(self.file_stem + '.dbf')
        # convert bytes to MB
        file_size_mb = file_size_mb / 1024 / 1024
        self.upload_size_msg = tr('Uploading ~%s MB...' % file_size_mb)
        self.set_message.emit(self.upload_size_msg)
        permissions = {
            "authenticated":
            "_none",
            "anonymous":
            "_none",
            "users": [[self.username, "layer_readwrite"],
                      [self.username, "layer_admin"]]
        }

        data = {
            'layer_title': os.path.basename(self.file_stem),
            'base_file': open('%s.shp' % self.file_stem, 'rb'),
            'dbf_file': open('%s.dbf' % self.file_stem, 'rb'),
            'shx_file': open('%s.shx' % self.file_stem, 'rb'),
            'prj_file': open('%s.prj' % self.file_stem, 'rb'),
            'xml_file': open('%s.xml' % self.file_stem, 'rb'),
            'charset': 'UTF-8',
            'permissions': json.dumps(permissions)
        }

        # generate headers and data-generator an a requests-compatible format
        # and provide our progress-callback
        data_generator, headers = multipart_encode_for_requests(
            data, cb=self.progress_cb)

        # use the requests-lib to issue a post-request with out data attached
        r = self.session.post(self.hostname + '/layers/upload',
                              data=data_generator,
                              headers=headers)

        try:
            response = json.loads(r.text)
            return self.hostname + response['url'], True
        except KeyError:
            if 'errors' in response:
                raise KeyError(response['errors'])
            else:
                raise KeyError("The server did not provide error messages")
        except ValueError:
            raise RuntimeError(r.text)
示例#14
0
    def accept(self):
        self.suppl_info['title'] = self.title_le.text()
        if 'title' not in self.project_definition:
            self.project_definition['title'] = self.suppl_info['title']
        self.suppl_info['abstract'] = self.description_te.toPlainText()
        if 'description' not in self.project_definition:
            self.project_definition['description'] = self.suppl_info[
                'abstract']
        zone_label_field = self.zone_label_field_cbx.currentText()
        self.suppl_info['zone_label_field'] = zone_label_field

        license_name = self.license_cbx.currentText()
        license_idx = self.license_cbx.currentIndex()
        license_url = self.license_cbx.itemData(license_idx)
        license_txt = '%s (%s)' % (license_name, license_url)
        self.suppl_info['license'] = license_txt
        self.suppl_info['irmt_plugin_version'] = IRMT_PLUGIN_VERSION
        self.suppl_info['supplemental_information_version'] = \
            SUPPLEMENTAL_INFORMATION_VERSION
        self.suppl_info['vertices_count'] = self.vertices_count

        self.suppl_info['project_definitions'][self.selected_idx] = \
            self.project_definition
        active_layer_id = self.iface.activeLayer().id()
        write_layer_suppl_info_to_qgs(active_layer_id, self.suppl_info)

        if self.do_update:
            with WaitCursorManager(
                    'Updating project on the OpenQuake Platform',
                    self.iface.messageBar()):
                hostname, username, password = get_credentials('platform')
                session = Session()
                try:
                    platform_login(hostname, username, password, session)
                except SvNetworkError as e:
                    error_msg = (
                        'Unable to login to the platform: ' + e.message)
                    log_msg(error_msg, level='C',
                            message_bar=self.iface.messageBar(),
                            exception=e)
                    return
                if 'platform_layer_id' not in self.suppl_info:
                    error_msg = ('Unable to retrieve the id of'
                                 'the layer on the Platform')
                    log_msg(error_msg, level='C',
                            message_bar=self.iface.messageBar())
                    return
                response = update_platform_project(
                    hostname, session, self.project_definition,
                    self.suppl_info['platform_layer_id'])
                if response.ok:
                    log_msg(tr(response.text), level='S',
                            message_bar=self.iface.messageBar())
                else:
                    error_msg = response.text
                    # example of response text:
                    # "The title 'No incomplete data' was already assigned to
                    # another project definition. Please provide a new unique
                    # one."
                    # NOTE: if the api response message changes, this might
                    # not work properly
                    if 'Please provide a new unique' in response.text:
                        error_msg += (' Please consider using the "Manage'
                                      ' project definitions" functionality'
                                      ' to save the current project definition'
                                      ' as a new one having a unique title.')
                    log_msg(error_msg, level='C',
                            message_bar=self.iface.messageBar())
        else:
            if DEBUG:
                log_msg('xml_file: %s' % self.xml_file)
            # do not upload the selected_project_definition_idx
            self.suppl_info.pop('selected_project_definition_idx', None)
            write_iso_metadata_file(self.xml_file,
                                    self.suppl_info)
            metadata_dialog = UploadDialog(
                self.iface, self.file_stem)
            metadata_dialog.upload_successful.connect(
                lambda layer_url: insert_platform_layer_id(
                    layer_url,
                    active_layer_id,
                    self.suppl_info))
            if metadata_dialog.exec_():
                QDesktopServices.openUrl(QUrl(metadata_dialog.layer_url))
            elif DEBUG:
                log_msg("metadata_dialog cancelled")

        super(UploadSettingsDialog, self).accept()
示例#15
0
 def on_add_proj_def_btn_clicked(self):
     title, ok = QInputDialog().getText(self, tr('Assign a title'),
                                        tr('Project definition title'))
     if ok:
         self.add_proj_def(title)
    def style_maps(layer,
                   style_by,
                   iface,
                   output_type='damages-rlzs',
                   perils=None,
                   add_null_class=False,
                   render_higher_on_top=False,
                   repaint=True,
                   use_sgc_style=False):
        symbol = QgsSymbol.defaultSymbol(layer.geometryType())
        # see properties at:
        # https://qgis.org/api/qgsmarkersymbollayerv2_8cpp_source.html#l01073
        symbol.setOpacity(1)
        if isinstance(symbol, QgsMarkerSymbol):
            # do it only for the layer with points
            symbol.symbolLayer(0).setStrokeStyle(Qt.PenStyle(Qt.NoPen))

        style = get_style(layer, iface.messageBar())

        # this is the default, as specified in the user settings
        ramp = QgsGradientColorRamp(style['color_from'], style['color_to'])
        style_mode = style['style_mode']

        # in most cases, we override the user-specified setting, and use
        # instead a setting that was required by scientists
        if output_type in OQ_TO_LAYER_TYPES:
            default_qgs_style = QgsStyle().defaultStyle()
            default_color_ramp_names = default_qgs_style.colorRampNames()
            if output_type in (
                    'damages-rlzs',
                    'avg_losses-rlzs',
                    'avg_losses-stats',
            ):
                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                # jenks = natural breaks
                if Qgis.QGIS_VERSION_INT < 31000:
                    style_mode = QgsGraduatedSymbolRenderer.Jenks
                else:
                    style_mode = 'Jenks'
                ramp_type_idx = default_color_ramp_names.index('Reds')
                symbol.setColor(QColor(RAMP_EXTREME_COLORS['Reds']['top']))
                inverted = False
            elif (output_type in ('gmf_data', 'ruptures')
                  or (output_type == 'hmaps' and not use_sgc_style)):
                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                # jenks = natural breaks
                if output_type == 'ruptures':
                    if Qgis.QGIS_VERSION_INT < 31000:
                        style_mode = QgsGraduatedSymbolRenderer.Pretty
                    else:
                        style_mode = 'PrettyBreaks'
                else:
                    if Qgis.QGIS_VERSION_INT < 31000:
                        style_mode = QgsGraduatedSymbolRenderer.EqualInterval
                    else:
                        style_mode = 'EqualInterval'
                ramp_type_idx = default_color_ramp_names.index('Spectral')
                inverted = True
                symbol.setColor(QColor(RAMP_EXTREME_COLORS['Reds']['top']))
            elif output_type == 'hmaps' and use_sgc_style:
                # FIXME: for SGC they were using size 10000 map units

                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                # jenks = natural breaks
                if Qgis.QGIS_VERSION_INT < 31000:
                    style_mode = QgsGraduatedSymbolRenderer.Pretty
                else:
                    style_mode = 'PrettyBreaks'
                try:
                    ramp_type_idx = default_color_ramp_names.index(
                        'SGC_Green2Red_Hmap_Color_Ramp')
                except ValueError:
                    raise ValueError(
                        'Color ramp SGC_Green2Red_Hmap_Color_Ramp was '
                        'not found. Please import it from '
                        'Settings -> Style Manager, loading '
                        'svir/resources/sgc_green2red_hmap_color_ramp.xml')
                inverted = False
                registry = QgsApplication.symbolLayerRegistry()
                symbol_props = {
                    'name': 'square',
                    'color': '0,0,0',
                    'color_border': '0,0,0',
                    'offset': '0,0',
                    'size': '1.5',  # FIXME
                    'angle': '0',
                }
                square = registry.symbolLayerMetadata(
                    "SimpleMarker").createSymbolLayer(symbol_props)
                symbol = QgsSymbol.defaultSymbol(layer.geometryType()).clone()
                symbol.deleteSymbolLayer(0)
                symbol.appendSymbolLayer(square)
                symbol.symbolLayer(0).setStrokeStyle(Qt.PenStyle(Qt.NoPen))
            elif output_type in ['asset_risk', 'input']:
                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                # jenks = natural breaks
                if Qgis.QGIS_VERSION_INT < 31000:
                    style_mode = QgsGraduatedSymbolRenderer.EqualInterval
                else:
                    style_mode = 'EqualInterval'
                # exposure_strings = ['number', 'occupants', 'value']
                # setting exposure colors by default
                colors = {
                    'single': RAMP_EXTREME_COLORS['Blues']['top'],
                    'ramp_name': 'Blues'
                }
                inverted = False
                if output_type == 'asset_risk':
                    damage_strings = perils
                    for damage_string in damage_strings:
                        if damage_string in style_by:
                            colors = {
                                'single':
                                RAMP_EXTREME_COLORS['Spectral']['top'],
                                'ramp_name': 'Spectral'
                            }
                            inverted = True
                            break
                else:  # 'input'
                    colors = {
                        'single': RAMP_EXTREME_COLORS['Greens']['top'],
                        'ramp_name': 'Greens'
                    }
                    symbol.symbolLayer(0).setShape(
                        QgsSimpleMarkerSymbolLayerBase.Square)
                single_color = colors['single']
                ramp_name = colors['ramp_name']
                ramp_type_idx = default_color_ramp_names.index(ramp_name)
                symbol.setColor(QColor(single_color))
            else:
                raise NotImplementedError(
                    'Undefined color ramp for output type %s' % output_type)
            ramp = default_qgs_style.colorRamp(
                default_color_ramp_names[ramp_type_idx])
            if inverted:
                ramp.invert()
        # get unique values
        fni = layer.fields().indexOf(style_by)
        unique_values = layer.dataProvider().uniqueValues(fni)
        num_unique_values = len(unique_values - {NULL})
        if num_unique_values > 2:
            if Qgis.QGIS_VERSION_INT < 31000:
                renderer = QgsGraduatedSymbolRenderer.createRenderer(
                    layer, style_by, min(num_unique_values, style['classes']),
                    style_mode, symbol.clone(), ramp)
            else:
                renderer = QgsGraduatedSymbolRenderer(style_by, [])
                # NOTE: the following returns an instance of one of the
                #       subclasses of QgsClassificationMethod
                classification_method = \
                    QgsApplication.classificationMethodRegistry().method(
                        style_mode)
                renderer.setClassificationMethod(classification_method)
                renderer.updateColorRamp(ramp)
                renderer.updateSymbols(symbol.clone())
                renderer.updateClasses(
                    layer, min(num_unique_values, style['classes']))
            if not use_sgc_style:
                if Qgis.QGIS_VERSION_INT < 31000:
                    label_format = renderer.labelFormat()
                    # NOTE: the following line might be useful
                    # label_format.setTrimTrailingZeroes(True)
                    label_format.setPrecision(2)
                    renderer.setLabelFormat(label_format, updateRanges=True)
                else:
                    renderer.classificationMethod().setLabelPrecision(2)
                    renderer.calculateLabelPrecision()
        elif num_unique_values == 2:
            categories = []
            for unique_value in unique_values:
                symbol = symbol.clone()
                try:
                    symbol.setColor(
                        QColor(RAMP_EXTREME_COLORS[ramp_name]
                               ['bottom' if unique_value ==
                                min(unique_values) else 'top']))
                except Exception:
                    symbol.setColor(
                        QColor(style['color_from'] if unique_value ==
                               min(unique_values) else style['color_to']))
                category = QgsRendererCategory(unique_value, symbol,
                                               str(unique_value))
                # entry for the list of category items
                categories.append(category)
            renderer = QgsCategorizedSymbolRenderer(style_by, categories)
        else:
            renderer = QgsSingleSymbolRenderer(symbol.clone())
        if add_null_class and NULL in unique_values:
            # add a class for NULL values
            rule_renderer = QgsRuleBasedRenderer(symbol.clone())
            root_rule = rule_renderer.rootRule()
            not_null_rule = root_rule.children()[0].clone()
            # strip parentheses from stringified color HSL
            not_null_rule.setFilterExpression(
                '%s IS NOT NULL' % QgsExpression.quotedColumnRef(style_by))
            not_null_rule.setLabel('%s:' % style_by)
            root_rule.appendChild(not_null_rule)
            null_rule = root_rule.children()[0].clone()
            null_rule.setSymbol(
                QgsFillSymbol.createSimple({
                    'color': '160,160,160',
                    'style': 'diagonal_x'
                }))
            null_rule.setFilterExpression(
                '%s IS NULL' % QgsExpression.quotedColumnRef(style_by))
            null_rule.setLabel(tr('No points'))
            root_rule.appendChild(null_rule)
            if isinstance(renderer, QgsGraduatedSymbolRenderer):
                # create value ranges
                rule_renderer.refineRuleRanges(not_null_rule, renderer)
                # remove default rule
            elif isinstance(renderer, QgsCategorizedSymbolRenderer):
                rule_renderer.refineRuleCategoris(not_null_rule, renderer)
            for rule in rule_renderer.rootRule().children()[1].children():
                label = rule.label()
                # by default, labels are like:
                # ('"collapse-structural-ASH_DRY_sum" >= 0.0000 AND
                # "collapse-structural-ASH_DRY_sum" <= 2.3949')
                first, second = label.split(" AND ")
                bottom = first.rsplit(" ", 1)[1]
                top = second.rsplit(" ", 1)[1]
                simplified = "%s - %s" % (bottom, top)
                rule.setLabel(simplified)
            root_rule.removeChildAt(0)
            renderer = rule_renderer
        if render_higher_on_top:
            renderer.setUsingSymbolLevels(True)
            symbol_items = [item for item in renderer.legendSymbolItems()]
            for i in range(len(symbol_items)):
                sym = symbol_items[i].symbol().clone()
                key = symbol_items[i].ruleKey()
                for lay in range(sym.symbolLayerCount()):
                    sym.symbolLayer(lay).setRenderingPass(i)
                renderer.setLegendSymbolItem(key, sym)
        layer.setRenderer(renderer)
        if not use_sgc_style:
            layer.setOpacity(0.7)
        if repaint:
            layer.triggerRepaint()
            iface.setActiveLayer(layer)
            iface.zoomToActiveLayer()
            # NOTE QGIS3: probably not needed
            # iface.layerTreeView().refreshLayerSymbology(layer.id())
            iface.mapCanvas().refresh()
示例#17
0
 def kill(self):
     self.is_killed = True
     self.set_message.emit(tr('Aborting...'))
     self.toggle_show_progress.emit(False)
示例#18
0
    def work(self):
        # To upload the layer to the platform, we need it to be a shapefile.
        # So we need to check if the active layer is stored as a shapefile and,
        # if it isn't, save it as a shapefile
        data_file = '%s%s' % (self.file_stem, '.shp')
        projection = self.current_layer.crs().geographicCrsAuthId()
        if (self.current_layer.storageType() == 'ESRI Shapefile'
                and projection == 'EPSG:4326'):
            # copy the shapefile (with all its files) into the temporary
            # directory, using self.file_stem as name
            self.set_message.emit(tr(
                'Preparing the files to be uploaded...'))
            layer_source = self.current_layer.publicSource()
            layer_source_stem = layer_source[:-4]  # remove '.shp'
            for ext in ['shp', 'dbf', 'shx', 'prj']:
                src = "%s.%s" % (layer_source_stem, ext)
                dst = "%s.%s" % (self.file_stem, ext)
                shutil.copyfile(src, dst)
        else:
            # if it's not a shapefile or it is in a bad projection,
            # we need to build a shapefile from it
            self.set_message.emit(tr(
                'Writing the shapefile to be uploaded...'))
            writer_error, error_msg = save_layer_as(
                self.current_layer, data_file,
                'ESRI Shapefile',
                crs=QgsCoordinateReferenceSystem(
                    4326, QgsCoordinateReferenceSystem.EpsgCrsId))
            if writer_error:
                raise RuntimeError(
                    'Could not save shapefile. %s: %s' % (writer_error,
                                                          error_msg))
        file_size_mb = os.path.getsize(data_file)
        file_size_mb += os.path.getsize(self.file_stem + '.shx')
        file_size_mb += os.path.getsize(self.file_stem + '.dbf')
        # convert bytes to MB
        file_size_mb = file_size_mb / 1024 / 1024
        self.upload_size_msg = tr('Uploading ~%s MB...' % file_size_mb)
        self.set_message.emit(self.upload_size_msg)
        user_permissions = [
                            'change_layer_style', 'add_layer',
                            'change_layer', 'delete_layer',
                            'view_resourcebase',
                            'download_resourcebase',
                            'publish_resourcebase',
                            ]
        admin_permissions = [
                             'change_layer_data',
                             'change_resourcebase_metadata',
                             'change_resourcebase',
                             'delete_resourcebase',
                             'change_resourcebase_permissions',
                             ]
        admin_permissions.extend(user_permissions)
        permissions = {
                       "admin": {
                                 self.username: admin_permissions
                                 },
                       "users": {
                                 self.username: user_permissions
                                 }
                       }

        files = {'base_file': open('%s.shp' % self.file_stem, 'rb'),
                 'dbf_file': open('%s.dbf' % self.file_stem, 'rb'),
                 'shx_file': open('%s.shx' % self.file_stem, 'rb'),
                 'prj_file': open('%s.prj' % self.file_stem, 'rb'),
                 'xml_file': open('%s.xml' % self.file_stem, 'rb')}
        data = {'layer_title': os.path.basename(self.file_stem),
                'charset': 'UTF-8',
                'permissions': json.dumps(permissions),
                'metadata_uploaded_preserve': True}

        self.progress.emit(-1)

        r = self.session.post(
            self.hostname + '/layers/upload', data=data, files=files)

        self.toggle_show_progress.emit(False)
        self.toggle_show_cancel.emit(False)
        self.set_message.emit(
            self.upload_size_msg + ' ' + tr('(processing on Platform)'))

        try:
            response = json.loads(r.text)
            return self.hostname + response['url'], True
        except KeyError:
            if 'errors' in response:
                raise KeyError(response['errors'])
            else:
                raise KeyError("The server did not provide error messages")
        except ValueError:
            raise RuntimeError(r.text)
示例#19
0
def _add_zone_id_to_points_internal(iface, loss_layer, zonal_layer,
                                    zone_id_in_zones_attr_name):
    """
       On the hypothesis that we don't know what is the zone in which
       each point was collected we use an alternative implementation of what
       SAGA does, i.e.,
       we add a field to the loss layer, containing the id of the zone
       to which it belongs. In order to achieve that:
       * we create a spatial index of the loss points
       * for each zone (in the layer containing zonally-aggregated SVI
           * we identify points that are inside the zone's bounding box
           * we check if each of these points is actually inside the
               zone's geometry; if it is:
               * copy the zone id into the new field of the loss point
       Notes:
       * loss_layer contains the not aggregated loss points
       * zonal_layer contains the zone geometries
       """
    # make a copy of the loss layer and use that from now on
    add_to_registry = True if DEBUG else False
    loss_layer_plus_zones = \
        ProcessLayer(loss_layer).duplicate_in_memory(
                new_name='Loss plus zone labels',
                add_to_registry=add_to_registry)
    # add to it the new attribute that will contain the zone id
    # and to do that we need to know the type of the zone id field
    zonal_layer_fields = zonal_layer.fields()
    zone_id_field_variant, zone_id_field_type_name = [
        (field.type(), field.typeName()) for field in zonal_layer_fields
        if field.name() == zone_id_in_zones_attr_name
    ][0]
    zone_id_field = QgsField(zone_id_in_zones_attr_name, zone_id_field_variant)
    zone_id_field.setTypeName(zone_id_field_type_name)
    assigned_attr_names_dict = \
        ProcessLayer(loss_layer_plus_zones).add_attributes(
                [zone_id_field])
    zone_id_in_losses_attr_name = assigned_attr_names_dict.values()[0]
    # get the index of the new attribute, to be used to update its values
    zone_id_attr_idx = loss_layer_plus_zones.fieldNameIndex(
        zone_id_in_losses_attr_name)
    # to show the overall progress, cycling through points
    tot_points = loss_layer_plus_zones.featureCount()
    msg = tr("Step 2 of 3: creating spatial index for loss points...")
    msg_bar_item, progress = create_progress_message_bar(
        iface.messageBar(), msg)
    # create spatial index
    with TraceTimeManager(tr("Creating spatial index for loss points..."),
                          DEBUG):
        spatial_index = QgsSpatialIndex()
        for current_point, loss_feature in enumerate(
                loss_layer_plus_zones.getFeatures()):
            progress_perc = current_point / float(tot_points) * 100
            progress.setValue(progress_perc)
            spatial_index.insertFeature(loss_feature)
    clear_progress_message_bar(iface.messageBar(), msg_bar_item)
    with edit(loss_layer_plus_zones):
        # to show the overall progress, cycling through zones
        tot_zones = zonal_layer.featureCount()
        msg = tr("Step 3 of 3: labeling points by zone id...")
        msg_bar_item, progress = create_progress_message_bar(
            iface.messageBar(), msg)
        for current_zone, zone_feature in enumerate(zonal_layer.getFeatures()):
            progress_perc = current_zone / float(tot_zones) * 100
            progress.setValue(progress_perc)
            msg = "{0}% - Zone: {1} on {2}".format(progress_perc,
                                                   zone_feature.id(),
                                                   tot_zones)
            with TraceTimeManager(msg, DEBUG):
                zone_geometry = zone_feature.geometry()
                # Find ids of points within the bounding box of the zone
                point_ids = spatial_index.intersects(
                    zone_geometry.boundingBox())
                # check if the points inside the bounding box of the zone
                # are actually inside the zone's geometry
                for point_id in point_ids:
                    msg = "Checking if point {0} is actually inside " \
                          "the zone".format(point_id)
                    with TraceTimeManager(msg, DEBUG):
                        # Get the point feature by the point's id
                        request = QgsFeatureRequest().setFilterFid(point_id)
                        point_feature = loss_layer_plus_zones.getFeatures(
                            request).next()
                        point_geometry = QgsGeometry(point_feature.geometry())
                        # check if the point is actually inside the zone
                        # and it is not only contained by its bounding box
                        if zone_geometry.contains(point_geometry):
                            zone_id = zone_feature[zone_id_in_zones_attr_name]
                            loss_layer_plus_zones.changeAttributeValue(
                                point_id, zone_id_attr_idx, zone_id)
        # for consistency with the SAGA algorithm, remove points that don't
        # belong to any zone
        for point_feature in loss_layer_plus_zones.getFeatures():
            if not point_feature[zone_id_in_losses_attr_name]:
                loss_layer_plus_zones.deleteFeature(point_feature.id())
    clear_progress_message_bar(iface.messageBar(), msg_bar_item)
    return loss_layer_plus_zones, zone_id_in_losses_attr_name
示例#20
0
def calculate_vector_stats_aggregating_by_zone_id(loss_layer,
                                                  zonal_layer,
                                                  zone_id_in_losses_attr_name,
                                                  zone_id_in_zones_attr_name,
                                                  loss_attr_names,
                                                  loss_attrs_dict,
                                                  iface,
                                                  old_field_to_new_field=None,
                                                  extra=True):
    """
    Once we know the zone id of each point in the loss map, we
    can count how many points are in each zone, sum and average their values
    """
    tot_points = loss_layer.featureCount()
    msg = tr("Step 2 of 3: aggregating losses by zone id...")
    msg_bar_item, progress = create_progress_message_bar(
        iface.messageBar(), msg)
    # if the user picked an attribute from the loss layer, to be
    # used as zone id, use that; otherwise, use the attribute
    # copied from the zonal layer
    if not zone_id_in_losses_attr_name:
        zone_id_in_losses_attr_name = zone_id_in_zones_attr_name
    with TraceTimeManager(msg, DEBUG):
        zone_stats = {}
        for current_point, point_feat in enumerate(loss_layer.getFeatures()):
            progress_perc = current_point / float(tot_points) * 100
            progress.setValue(progress_perc)
            zone_id = point_feat[zone_id_in_losses_attr_name]
            if zone_id not in zone_stats:
                zone_stats[zone_id] = {}
            for loss_attr_name in loss_attr_names:
                if loss_attr_name not in zone_stats[zone_id]:
                    zone_stats[zone_id][loss_attr_name] = {
                        'count': 0,
                        'sum': 0.0
                    }
                if old_field_to_new_field:
                    loss_value = point_feat[
                        old_field_to_new_field[loss_attr_name]]
                else:
                    loss_value = point_feat[loss_attr_name]
                zone_stats[zone_id][loss_attr_name]['count'] += 1
                zone_stats[zone_id][loss_attr_name]['sum'] += loss_value
    clear_progress_message_bar(iface.messageBar(), msg_bar_item)
    if extra:
        msg = tr("Step 3 of 3: writing point counts, loss sums and averages"
                 " into the zonal layer...")
    else:
        msg = tr("Step 3 of 3: writing sums into the zonal layer...")
    with TraceTimeManager(msg, DEBUG):
        tot_zones = zonal_layer.featureCount()
        msg_bar_item, progress = create_progress_message_bar(
            iface.messageBar(), msg)
        with edit(zonal_layer):
            if extra:
                count_idx = zonal_layer.fieldNameIndex(
                    loss_attrs_dict['count'])
                avg_idx = {}
            sum_idx = {}
            for loss_attr_name in loss_attr_names:
                sum_idx[loss_attr_name] = zonal_layer.fieldNameIndex(
                    loss_attrs_dict[loss_attr_name]['sum'])
                if extra:
                    avg_idx[loss_attr_name] = zonal_layer.fieldNameIndex(
                        loss_attrs_dict[loss_attr_name]['avg'])
            for current_zone, zone_feat in enumerate(
                    zonal_layer.getFeatures()):
                progress_perc = current_zone / float(tot_zones) * 100
                progress.setValue(progress_perc)
                # get the id of the current zone
                zone_id = zone_feat[zone_id_in_zones_attr_name]
                # initialize points_count, loss_sum and loss_avg
                # to zero, and update them afterwards only if the zone
                # contains at least one loss point
                points_count = 0
                if extra:
                    loss_avg = {}
                loss_sum = {}
                for loss_attr_name in loss_attr_names:
                    loss_sum[loss_attr_name] = 0.0
                    if extra:
                        loss_avg[loss_attr_name] = 0.0
                # retrieve count and sum from the dictionary, using
                # the zone id as key to get the values from the
                # corresponding dict (otherwise, keep zero values)
                if zone_id in zone_stats:
                    for loss_attr_name in loss_attr_names:
                        loss_sum[loss_attr_name] = \
                            zone_stats[zone_id][loss_attr_name]['sum']
                        points_count = \
                            zone_stats[zone_id][loss_attr_name]['count']
                        if extra:
                            # division by zero should be impossible, because
                            # we are computing this only for zones containing
                            # at least one point (otherwise we keep all zeros)
                            loss_avg[loss_attr_name] = (
                                loss_sum[loss_attr_name] / points_count)
                            # NOTE: The following line looks redundant
                            zone_stats[zone_id][loss_attr_name]['avg'] = (
                                loss_avg[loss_attr_name])
                # without casting to int and to float, it wouldn't work
                fid = zone_feat.id()
                if extra:
                    zonal_layer.changeAttributeValue(fid, count_idx,
                                                     int(points_count))
                for loss_attr_name in loss_attr_names:
                    if points_count:
                        zonal_layer.changeAttributeValue(
                            fid, sum_idx[loss_attr_name],
                            float(loss_sum[loss_attr_name]))
                        if extra:
                            zonal_layer.changeAttributeValue(
                                fid, avg_idx[loss_attr_name],
                                float(loss_avg[loss_attr_name]))
                    else:
                        # if no points were found in that region, let both
                        # sum and average be NULL instead of 0
                        zonal_layer.changeAttributeValue(
                            fid, sum_idx[loss_attr_name],
                            QPyNullVariant(float))
                        if extra:
                            zonal_layer.changeAttributeValue(
                                fid, avg_idx[loss_attr_name],
                                QPyNullVariant(float))
    clear_progress_message_bar(iface.messageBar(), msg_bar_item)
    notify_loss_aggregation_by_zone_complete(loss_attrs_dict,
                                             loss_attr_names,
                                             iface,
                                             extra=extra)
    return (loss_layer, zonal_layer, loss_attrs_dict)