Пример #1
0
    def onFeaturesCommitted(self, layerid, features):
        """
        Update the related entities with the FK key from the primary spatial unit PK.
        """
        if layerid in self._pendingFKEntities:
            pendingLayerEntity = self._pendingFKEntities[layerid]

            # Get map layer using layerid
            refLayer = QgsProject.instance().mapLayer(layerid)

            if refLayer is not None:
                fidx = refLayer.fields().indexFromName(
                    pendingLayerEntity.featureAttributeName())

                # Show progress dialog for updating the features.
                progressLabel = QApplication.translate(
                    "StdmMapToolCreateFeature", "Updating related entities...")
                progressDlg = QProgressDialog(progressLabel, "", 0,
                                              len(features),
                                              self.iface.mainWindow())
                progressDlg.setWindowModality(Qt.WindowModal)

                for i, feat in enumerate(features):
                    progressDlg.setValue(i)
                    uniqueAttrValue = feat.attributes()[fidx]
                    pendingLayerEntity.setPrimaryKey(uniqueAttrValue,
                                                     int(feat.id()))

                progressDlg.setValue(len(features))
                progressDlg.deleteLater()
                del progressDlg
Пример #2
0
class OsmDownloaderDialog(QDialog, FORM_CLASS):
    """Downloader for OSM data."""
    def __init__(self, parent=None, iface=None):
        """Constructor for import dialog.

        :param parent: Optional widget to use as parent.
        :type parent: QWidget

        :param iface: An instance of QgisInterface.
        :type iface: QgisInterface
        """
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setupUi(self)
        icon = resources_path('img', 'icons', 'show-osm-download.svg')
        self.setWindowIcon(QtGui.QIcon(icon))
        title = self.tr('InaSAFE OpenStreetMap Downloader')
        self.setWindowTitle(title)

        self.iface = iface
        self.progress_dialog = None

        # Set up things for context help
        self.help_button = self.button_box.button(
            QtWidgets.QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)
        self.main_stacked_widget.setCurrentIndex(1)

        # Output directory
        self.directory_button.clicked.connect(self.directory_button_clicked)
        self.output_directory.setPlaceholderText(
            self.tr('[Create a temporary layer]'))

        # Disable boundaries group box until boundary checkbox is ticked
        self.boundary_group.setEnabled(False)

        # set up the validator for the file name prefix
        expression = QRegExp('^[A-Za-z0-9-_]*$')
        validator = QtGui.QRegExpValidator(expression, self.filename_prefix)
        self.filename_prefix.setValidator(validator)

        # Advanced panel
        self.line_edit_custom.setPlaceholderText(STAGING_SERVER)
        developer_mode = setting('developer_mode', expected_type=bool)
        self.group_box_advanced.setVisible(developer_mode)

        self.restore_state()

        # Setup the rectangle map tool
        self.canvas = iface.mapCanvas()
        self.rectangle_map_tool = \
            RectangleMapTool(self.canvas)
        self.rectangle_map_tool.rectangle_created.connect(
            self.update_extent_from_rectangle)
        self.capture_button.clicked.connect(self.drag_rectangle_on_map_canvas)

        # Setup pan tool
        self.pan_tool = QgsMapToolPan(self.canvas)
        self.canvas.setMapTool(self.pan_tool)

        # Setup helper for admin_level
        json_file_path = resources_path('osm', 'admin_level_per_country.json')
        if os.path.isfile(json_file_path):
            with open(json_file_path, encoding='utf-8') as f:
                self.countries = json.load(f)
                self.bbox_countries = None
                self.populate_countries()
                # connect
                self.country_comboBox.currentIndexChanged.connect(
                    self.update_helper_political_level)
                self.admin_level_comboBox.currentIndexChanged.connect(
                    self.update_helper_political_level)

        self.update_extent_from_map_canvas()

    def update_helper_political_level(self):
        """To update the helper about the country and the admin_level."""
        current_country = self.country_comboBox.currentText()
        index = self.admin_level_comboBox.currentIndex()
        current_level = self.admin_level_comboBox.itemData(index)
        content = None
        try:
            content = \
                self.countries[current_country]['levels'][str(current_level)]
            if content == 'N/A' or content == 'fixme' or content == '':
                raise KeyError
        except KeyError:
            content = self.tr('undefined')
        finally:
            text = self.tr('which represents %s in') % content
            self.boundary_helper.setText(text)

    def populate_countries(self):
        """Populate the combobox about countries and levels."""
        for i in range(1, 12):
            self.admin_level_comboBox.addItem(self.tr('Level %s') % i, i)

        # Set current index to admin_level 8, the most common one
        self.admin_level_comboBox.setCurrentIndex(7)

        list_countries = sorted(
            [self.tr(country) for country in list(self.countries.keys())])
        for country in list_countries:
            self.country_comboBox.addItem(country)

        self.bbox_countries = {}
        for country in list_countries:
            multipolygons = self.countries[country]['bbox']
            self.bbox_countries[country] = []
            for coords in multipolygons:
                bbox = QgsRectangle(coords[0], coords[3], coords[2], coords[1])
                self.bbox_countries[country].append(bbox)

        self.update_helper_political_level()

    def help_toggled(self, flag):
        """Show or hide the help tab in the stacked widget.

        .. versionadded: 3.2

        :param flag: Flag indicating whether help should be shown or hidden.
        :type flag: bool
        """
        if flag:
            self.help_button.setText(self.tr('Hide Help'))
            self.show_help()
        else:
            self.help_button.setText(self.tr('Show Help'))
            self.hide_help()

    def hide_help(self):
        """Hide the usage info from the user.

        .. versionadded:: 3.2
        """
        self.main_stacked_widget.setCurrentIndex(1)

    def show_help(self):
        """Show usage info to the user."""
        # Read the header and footer html snippets
        self.main_stacked_widget.setCurrentIndex(0)
        header = html_header()
        footer = html_footer()

        string = header

        message = osm_downloader_help()
        string += message.to_html()
        string += footer

        self.help_web_view.setHtml(string)

    def restore_state(self):
        """Read last state of GUI from configuration file."""
        last_path = setting('directory', '', expected_type=str)
        self.output_directory.setText(last_path)

    def save_state(self):
        """Store current state of GUI to configuration file."""
        set_setting('directory', self.output_directory.text())

    def update_extent(self, extent):
        """Update extent value in GUI based from an extent.

        :param extent: A list in the form [xmin, ymin, xmax, ymax] where all
            coordinates provided are in Geographic / EPSG:4326.
        :type extent: list
        """
        self.x_minimum.setValue(extent[0])
        self.y_minimum.setValue(extent[1])
        self.x_maximum.setValue(extent[2])
        self.y_maximum.setValue(extent[3])

        # Updating the country if possible.
        rectangle = QgsRectangle(extent[0], extent[1], extent[2], extent[3])
        center = rectangle.center()

        for country in self.bbox_countries:
            for polygon in self.bbox_countries[country]:
                if polygon.contains(center):
                    index = self.country_comboBox.findText(country)
                    self.country_comboBox.setCurrentIndex(index)
                    break
            else:
                # Continue if the inner loop wasn't broken.
                continue
            # Inner loop was broken, break the outer.
            break
        else:
            self.country_comboBox.setCurrentIndex(0)

    def update_extent_from_map_canvas(self):
        """Update extent value in GUI based from value in map.

        .. note:: Delegates to update_extent()
        """

        self.bounding_box_group.setTitle(
            self.tr('Bounding box from the map canvas'))
        # Get the extent as [xmin, ymin, xmax, ymax]
        extent = viewport_geo_array(self.iface.mapCanvas())
        self.update_extent(extent)

    def update_extent_from_rectangle(self):
        """Update extent value in GUI based from the QgsMapTool rectangle.

        .. note:: Delegates to update_extent()
        """

        self.show()
        self.canvas.unsetMapTool(self.rectangle_map_tool)
        self.canvas.setMapTool(self.pan_tool)

        rectangle = self.rectangle_map_tool.rectangle()
        if rectangle:
            self.bounding_box_group.setTitle(
                self.tr('Bounding box from rectangle'))
            extent = rectangle_geo_array(rectangle, self.iface.mapCanvas())
            self.update_extent(extent)

    def directory_button_clicked(self):
        """Show a dialog to choose directory."""
        # noinspection PyCallByClass,PyTypeChecker
        self.output_directory.setText(
            QFileDialog.getExistingDirectory(
                self, self.tr('Select download directory')))

    def drag_rectangle_on_map_canvas(self):
        """Hide the dialog and allow the user to draw a rectangle."""

        self.hide()
        self.rectangle_map_tool.reset()
        self.canvas.unsetMapTool(self.pan_tool)
        self.canvas.setMapTool(self.rectangle_map_tool)

    def get_checked_features(self):
        """Create a tab with all checked features.

        :return A list with all features which are checked in the UI.
        :rtype list
        """
        feature_types = []
        if self.roads_flag.isChecked():
            feature_types.append('roads')
        if self.buildings_flag.isChecked():
            feature_types.append('buildings')
        if self.building_points_flag.isChecked():
            feature_types.append('building-points')
        if self.flood_prone_flag.isChecked():
            feature_types.append('flood-prone')
        if self.evacuation_centers_flag.isChecked():
            feature_types.append('evacuation-centers')
        if self.boundary_flag.isChecked():
            level = self.admin_level_comboBox.currentIndex() + 1
            feature_types.append('boundary-%s' % level)
        return feature_types

    def accept(self):
        """Do osm download and display it in QGIS."""
        error_dialog_title = self.tr('InaSAFE OpenStreetMap Downloader Error')

        # Lock the bounding_box_group
        self.bounding_box_group.setDisabled(True)

        # Get the extent
        y_minimum = self.y_minimum.value()
        y_maximum = self.y_maximum.value()
        x_minimum = self.x_minimum.value()
        x_maximum = self.x_maximum.value()
        extent = [x_minimum, y_minimum, x_maximum, y_maximum]

        # Validate extent
        valid_flag = validate_geo_array(extent)
        if not valid_flag:
            message = self.tr(
                'The bounding box is not valid. Please make sure it is '
                'valid or check your projection!')
            # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
            display_warning_message_box(self, error_dialog_title, message)
            # Unlock the bounding_box_group
            self.bounding_box_group.setEnabled(True)
            return

        # Validate features
        feature_types = self.get_checked_features()
        if len(feature_types) < 1:
            message = self.tr('No feature selected. '
                              'Please make sure you have checked one feature.')
            # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
            display_warning_message_box(self, error_dialog_title, message)
            # Unlock the bounding_box_group
            self.bounding_box_group.setEnabled(True)
            return

        if self.radio_custom.isChecked():
            server_url = self.line_edit_custom.text()
            if not server_url:
                # It's the place holder.
                server_url = STAGING_SERVER
        else:
            server_url = PRODUCTION_SERVER

        try:
            self.save_state()
            self.require_directory()

            # creating progress dialog for download
            self.progress_dialog = QProgressDialog(self)
            self.progress_dialog.setAutoClose(False)
            self.progress_dialog.setWindowTitle(self.windowTitle())

            for feature_type in feature_types:

                output_directory = self.output_directory.text()
                if output_directory == '':
                    output_directory = temp_dir(sub_dir='work')
                output_prefix = self.filename_prefix.text()
                overwrite = self.overwrite_flag.isChecked()
                output_base_file_path = self.get_output_base_path(
                    output_directory, output_prefix, feature_type, overwrite)

                # noinspection PyTypeChecker
                download(feature_type, output_base_file_path, extent,
                         self.progress_dialog, server_url)

                try:
                    self.load_shapefile(feature_type, output_base_file_path)
                except FileMissingError as exception:
                    display_warning_message_box(self, error_dialog_title,
                                                str(exception))
            self.done(QDialog.Accepted)
            self.rectangle_map_tool.reset()

        except CanceledImportDialogError:
            # don't show anything because this exception raised
            # when user canceling the import process directly
            pass
        except Exception as exception:  # pylint: disable=broad-except
            # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
            display_warning_message_box(self, error_dialog_title,
                                        str(exception))

            self.progress_dialog.cancel()
            self.progress_dialog.deleteLater()

        finally:
            # Unlock the bounding_box_group
            self.bounding_box_group.setEnabled(True)

    def get_output_base_path(self, output_directory, output_prefix,
                             feature_type, overwrite):
        """Get a full base name path to save the shapefile.

        :param output_directory: The directory where to put results.
        :type output_directory: str

        :param output_prefix: The prefix to add for the shapefile.
        :type output_prefix: str

        :param feature_type: What kind of features should be downloaded.
            Currently 'buildings', 'building-points' or 'roads' are supported.
        :type feature_type: str

        :param overwrite: Boolean to know if we can overwrite existing files.
        :type overwrite: bool

        :return: The base path.
        :rtype: str
        """
        path = os.path.join(output_directory,
                            '%s%s' % (output_prefix, feature_type))

        if overwrite:

            # If a shapefile exists, we must remove it (only the .shp)
            shp = '%s.shp' % path
            if os.path.isfile(shp):
                os.remove(shp)

        else:
            separator = '-'
            suffix = self.get_unique_file_path_suffix('%s.shp' % path,
                                                      separator)

            if suffix:
                path = os.path.join(
                    output_directory, '%s%s%s%s' %
                    (output_prefix, feature_type, separator, suffix))

        return path

    @staticmethod
    def get_unique_file_path_suffix(file_path, separator='-', i=0):
        """Return the minimum number to suffix the file to not overwrite one.
        Example : /tmp/a.txt exists.
            - With file_path='/tmp/b.txt' will return 0.
            - With file_path='/tmp/a.txt' will return 1 (/tmp/a-1.txt)

        :param file_path: The file to check.
        :type file_path: str

        :param separator: The separator to add before the prefix.
        :type separator: str

        :param i: The minimum prefix to check.
        :type i: int

        :return: The minimum prefix you should add to not overwrite a file.
        :rtype: int
        """

        basename = os.path.splitext(file_path)
        if i != 0:
            file_path_test = os.path.join(
                '%s%s%s%s' % (basename[0], separator, i, basename[1]))
        else:
            file_path_test = file_path

        if os.path.isfile(file_path_test):
            return OsmDownloaderDialog.get_unique_file_path_suffix(
                file_path, separator, i + 1)
        else:
            return i

    def require_directory(self):
        """Ensure directory path entered in dialog exist.

        When the path does not exist, this function will ask the user if he
        want to create it or not.

        :raises: CanceledImportDialogError - when user choose 'No' in
            the question dialog for creating directory.
        """
        path = self.output_directory.text()

        if path == '':
            # If let empty, we create an temporary directory
            return

        if os.path.exists(path):
            return

        title = self.tr('Directory %s not exist') % path
        question = self.tr(
            'Directory %s not exist. Do you want to create it?') % path
        # noinspection PyCallByClass,PyTypeChecker
        answer = QMessageBox.question(self, title, question,
                                      QMessageBox.Yes | QMessageBox.No)

        if answer == QMessageBox.Yes:
            if len(path) != 0:
                os.makedirs(path)
            else:
                # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
                display_warning_message_box(
                    self, self.tr('InaSAFE error'),
                    self.tr('Output directory can not be empty.'))
                raise CanceledImportDialogError()
        else:
            raise CanceledImportDialogError()

    def load_shapefile(self, feature_type, base_path):
        """Load downloaded shape file to QGIS Main Window.

        :param feature_type: What kind of features should be downloaded.
            Currently 'buildings', 'building-points' or 'roads' are supported.
        :type feature_type: str

        :param base_path: The base path of the shape file (without extension).
        :type base_path: str

        :raises: FileMissingError - when buildings.shp not exist
        """

        path = '%s.shp' % base_path

        if not os.path.exists(path):
            message = self.tr(
                '%s does not exist. The server does not have any data for '
                'this extent.' % path)
            raise FileMissingError(message)

        layer = self.iface.addVectorLayer(path, feature_type, 'ogr')

        # Check if it's a building layer about the 2.5D
        if feature_type == 'buildings':
            layer_scope = QgsExpressionContextUtils.layerScope(layer)
            if not layer_scope.variable('qgis_25d_height'):
                QgsExpressionContextUtils.setLayerVariable(
                    layer, 'qgis_25d_height', 0.0002)
            if not layer_scope.variable('qgis_25d_angle'):
                QgsExpressionContextUtils.setLayerVariable(
                    layer, 'qgis_25d_angle', 70)

    def reject(self):
        """Redefinition of the method to remove the rectangle selection tool.

        It will call the super method.
        """
        self.canvas.unsetMapTool(self.rectangle_map_tool)
        self.rectangle_map_tool.reset()

        super(OsmDownloaderDialog, self).reject()
Пример #3
0
    def open_style(input_file):  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
        """
        Opens a .style file
        """

        if not Extractor.is_mdb_tools_binary_available():
            bar = iface.messageBar()
            widget = bar.createMessage('SLYR', "MDB Tools utility not found")
            settings_button = QPushButton("Configure…", pressed=partial(open_settings, widget))
            widget.layout().addWidget(settings_button)
            bar.pushWidget(widget, Qgis.Critical)
            return True

        style = QgsStyle()
        style.createMemoryDatabase()

        symbol_names = set()

        def make_name_unique(name):
            """
            Ensures that the symbol name is unique (in a case insensitive way)
            """
            counter = 0
            candidate = name
            while candidate.lower() in symbol_names:
                # make name unique
                if counter == 0:
                    candidate += '_1'
                else:
                    candidate = candidate[:candidate.rfind('_') + 1] + str(counter)
                counter += 1
            symbol_names.add(candidate.lower())
            return candidate

        feedback = QgsFeedback()

        progress_dialog = QProgressDialog("Loading style database…", "Abort", 0, 100, None)
        progress_dialog.setWindowTitle("Loading Style")

        def progress_changed(progress):
            """
            Handles feedback to progress dialog bridge
            """
            progress_dialog.setValue(progress)
            iters = 0
            while QCoreApplication.hasPendingEvents() and iters < 100:
                QCoreApplication.processEvents()
                iters += 1

        feedback.progressChanged.connect(progress_changed)

        def cancel():
            """
            Slot to cancel the import
            """
            feedback.cancel()

        progress_dialog.canceled.connect(cancel)
        unreadable = []
        warnings = set()
        errors = set()

        types_to_extract = [Extractor.FILL_SYMBOLS, Extractor.LINE_SYMBOLS, Extractor.MARKER_SYMBOLS,
                            Extractor.COLOR_RAMPS,
                            Extractor.TEXT_SYMBOLS, Extractor.LABELS, Extractor.MAPLEX_LABELS, Extractor.AREA_PATCHES,
                            Extractor.LINE_PATCHES]

        type_percent = 100 / len(types_to_extract)

        for type_index, symbol_type in enumerate(types_to_extract):

            try:
                raw_symbols = Extractor.extract_styles(input_file, symbol_type)
            except MissingBinaryException:
                show_warning('MDB Tools utility not found', 'Convert style',
                             'The MDB tools "mdb-export" utility is required to convert .style databases. Please setup a path to the MDB tools utility in the SLYR options panel.',
                             level=Qgis.Critical)
                progress_dialog.deleteLater()
                return True

            if feedback.isCanceled():
                break

            for index, raw_symbol in enumerate(raw_symbols):
                feedback.setProgress(index / len(raw_symbols) * type_percent + type_percent * type_index)
                if feedback.isCanceled():
                    break
                name = raw_symbol[Extractor.NAME]
                tags = raw_symbol[Extractor.TAGS].split(';')

                if symbol_type in (
                        Extractor.AREA_PATCHES, Extractor.LINE_PATCHES, Extractor.TEXT_SYMBOLS, Extractor.MAPLEX_LABELS,
                        Extractor.LABELS):
                    if symbol_type == Extractor.AREA_PATCHES:
                        type_string = 'area patches'
                    elif symbol_type == Extractor.LINE_PATCHES:
                        type_string = 'line patches'
                    elif symbol_type == Extractor.TEXT_SYMBOLS:
                        type_string = 'text symbols'
                    elif symbol_type == Extractor.MAPLEX_LABELS:
                        type_string = 'maplex labels'
                    elif symbol_type == Extractor.LABELS:
                        type_string = 'labels'
                    else:
                        type_string = ''

                    unreadable.append('<b>{}</b>: {} conversion requires a licensed version of the SLYR plugin'.format(
                        html.escape(name), type_string))
                    continue

                unique_name = make_name_unique(name)

                handle = BytesIO(raw_symbol[Extractor.BLOB])
                stream = Stream(handle)
                stream.allow_shortcuts = False

                try:
                    symbol = stream.read_object()
                except UnreadableSymbolException as e:
                    e = 'Unreadable object: {}'.format(e)
                    unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e))))
                    continue
                except NotImplementedException as e:
                    unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e))))
                    continue
                except UnsupportedVersionException as e:
                    e = 'Unsupported version: {}'.format(e)
                    unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e))))
                    continue
                except UnknownClsidException as e:
                    unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e))))
                    continue
                except UnreadablePictureException as e:
                    unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e))))
                    continue

                context = Context()
                context.symbol_name = unique_name

                def unsupported_object_callback(msg, level=Context.WARNING):
                    if level == Context.WARNING:
                        warnings.add('<b>{}</b>: {}'.format(html.escape(unique_name), html.escape(msg)))
                    elif level == Context.CRITICAL:
                        errors.add('<b>{}</b>: {}'.format(html.escape(unique_name), html.escape(msg)))

                context.unsupported_object_callback = unsupported_object_callback
                # context.style_folder, _ = os.path.split(output_file)

                try:
                    qgis_symbol = SymbolConverter.Symbol_to_QgsSymbol(symbol, context)
                except NotImplementedException as e:
                    unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e))))
                    continue
                except UnreadablePictureException as e:
                    unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e))))
                    continue

                if isinstance(qgis_symbol, QgsSymbol):
                    # self.check_for_missing_fonts(qgis_symbol, feedback)
                    style.addSymbol(unique_name, qgis_symbol, True)
                elif isinstance(qgis_symbol, QgsColorRamp):
                    style.addColorRamp(unique_name, qgis_symbol, True)

                if tags:
                    if isinstance(qgis_symbol, QgsSymbol):
                        assert style.tagSymbol(QgsStyle.SymbolEntity, unique_name, tags)
                    elif isinstance(qgis_symbol, QgsColorRamp):
                        assert style.tagSymbol(QgsStyle.ColorrampEntity, unique_name, tags)
        progress_dialog.deleteLater()
        if feedback.isCanceled():
            return True

        if errors or unreadable or warnings:
            message = ''
            if unreadable:
                message = '<p>The following symbols could not be converted:</p>'
                message += '<ul>'
                for w in unreadable:
                    message += '<li>{}</li>'.format(w.replace('\n', '<br>'))
                message += '</ul>'

            if errors:
                message += '<p>The following errors were generated while converting symbols:</p>'
                message += '<ul>'
                for w in errors:
                    message += '<li>{}</li>'.format(w.replace('\n', '<br>'))
                message += '</ul>'

            if warnings:
                message += '<p>The following warnings were generated while converting symbols:</p>'
                message += '<ul>'
                for w in warnings:
                    message += '<li>{}</li>'.format(w.replace('\n', '<br>'))
                message += '</ul>'

            show_warning('style could not be completely converted', 'Convert style', message,
                         level=Qgis.Critical if (unreadable or errors) else Qgis.Warning)

        if Qgis.QGIS_VERSION_INT >= 30800:
            dlg = QgsStyleManagerDialog(style, readOnly=True)
            dlg.setFavoritesGroupVisible(False)
            dlg.setSmartGroupsVisible(False)
            fi = QFileInfo(input_file)
            dlg.setBaseStyleName(fi.baseName())
        else:
            dlg = QgsStyleManagerDialog(style)
        dlg.exec_()
        return True
Пример #4
0
    def db2Feat(self, parent, table, results, columns, geom=""):
        # Execute the export process
        # Create driver
        drv = ogr.GetDriverByName(self.getDriverName())
        if drv is None:
            raise Exception("{0} driver not available.".format(
                self.getDriverName()))

        # Create data source
        self._ds = drv.CreateDataSource(self._targetFile)
        if self._ds is None:
            raise Exception("Creation of output file failed.")
        dest_crs = None
        # Create layer
        if geom != "":
            pgGeomType, srid = geometryType(table, geom)
            geomType = wkbTypes[pgGeomType]
            try:
                dest_crs = ogr.osr.SpatialReference()
            except AttributeError:
                dest_crs = osr.SpatialReference()
            dest_crs.ImportFromEPSG(srid)

        else:
            geomType = ogr.wkbNone
        layer_name = self.getLayerName()

        lyr = self._ds.CreateLayer(layer_name, dest_crs, geomType)

        if lyr is None:
            raise Exception("Layer creation failed")

        # Create fields
        for c in columns:

            field_defn = self.createField(table, c)

            if lyr.CreateField(field_defn) != 0:
                raise Exception("Creating %s field failed" % (c))

        # Add Geometry column to list for referencing in the result set
        if geom != "":
            columns.append(geom)

        featGeom = None

        # Configure progress dialog
        initVal = 0
        numFeat = results.rowcount
        progress = QProgressDialog("", "&Cancel", initVal, numFeat, parent)
        progress.setWindowModality(Qt.WindowModal)
        lblMsgTemp = QApplication.translate('OGRWriter',
                                            'Writing {0} of {1} to file...')

        entity = current_profile().entity_by_name(table)
        # Iterate the result set
        for r in results:
            # Progress dialog
            progress.setValue(initVal)
            progressMsg = lblMsgTemp.format(str(initVal + 1), str(numFeat))
            progress.setLabelText(progressMsg)

            if progress.wasCanceled():
                break

            # Create OGR Feature
            feat = ogr.Feature(lyr.GetLayerDefn())

            for i in range(len(columns)):
                colName = columns[i]

                # Check if its the geometry column in the iteration
                if colName == geom:
                    if r[i] is not None:
                        featGeom = ogr.CreateGeometryFromWkt(r[i])
                    else:
                        featGeom = ogr.CreateGeometryFromWkt("")

                    feat.SetGeometry(featGeom)

                else:
                    field_value = r[i]
                    feat.SetField(i, str(field_value))

            if lyr.CreateFeature(feat) != 0:
                progress.close()
                progress.deleteLater()
                del progress
                raise Exception("Failed to create feature in %s" %
                                (self._targetFile))

            if featGeom is not None:
                featGeom.Destroy()

            feat.Destroy()
            initVal += 1

        progress.setValue(numFeat)
        progress.deleteLater()
        del progress
Пример #5
0
    def executeSearch(self):
        """
        Base class override.
        Search for matching items for the specified entity and column.
        """
        model_root_node = None

        prog_dialog = QProgressDialog(self)
        prog_dialog.setFixedWidth(380)
        prog_dialog.setWindowTitle(
            QApplication.translate("STRViewEntityWidget",
                                   "Searching for STR..."))
        prog_dialog.show()
        prog_dialog.setRange(0, 10)
        search_term = self._searchTerm()

        prog_dialog.setValue(2)
        # Try to get the corresponding search term value from the completer model
        if self._completer_model is not None:
            reg_exp = QRegExp("^%s$" % search_term, Qt.CaseInsensitive,
                              QRegExp.RegExp2)
            self._proxy_completer_model.setFilterRegExp(reg_exp)

            if self._proxy_completer_model.rowCount() > 0:
                # Get corresponding actual value from the first matching item
                value_model_idx = self._proxy_completer_model.index(0, 1)
                source_model_idx = self._proxy_completer_model.mapToSource(
                    value_model_idx)
                prog_dialog.setValue(4)
                search_term = self._completer_model.data(
                    source_model_idx, Qt.DisplayRole)

        modelInstance = self.config.STRModel()

        modelQueryObj = modelInstance.queryObject()

        queryObjProperty = getattr(self.config.STRModel,
                                   self.currentFieldName())

        entity_name = modelQueryObj._primary_entity._label_name

        entity = self.curr_profile.entity_by_name(entity_name)

        prog_dialog.setValue(6)
        # Get property type so that the filter can
        # be applied according to the appropriate type
        propType = queryObjProperty.property.columns[0].type
        results = []
        try:
            if not isinstance(propType, String):

                col_name = self.currentFieldName()
                col = entity.columns[self.currentFieldName()]

                if col.TYPE_INFO == 'LOOKUP':
                    lookup_entity = lookup_parent_entity(
                        self.curr_profile, col_name)
                    lkp_model = entity_model(lookup_entity)
                    lkp_obj = lkp_model()
                    value_obj = getattr(lkp_model, 'value')

                    result = lkp_obj.queryObject().filter(
                        func.lower(value_obj) == func.lower(
                            search_term)).first()
                    if result is None:
                        result = lkp_obj.queryObject().filter(
                            func.lower(value_obj).like(search_term +
                                                       '%')).first()

                    if result is not None:
                        results = modelQueryObj.filter(
                            queryObjProperty == result.id).all()

                    else:
                        results = []

            else:
                results = modelQueryObj.filter(
                    func.lower(queryObjProperty) == func.lower(
                        search_term)).all()

            if self.validity.isEnabled():
                valid_str_ids = self.str_validity_period_filter(results)
            else:
                valid_str_ids = None

            prog_dialog.setValue(7)
        except exc.StatementError:
            prog_dialog.deleteLater()
            del prog_dialog
            return model_root_node, [], search_term

        # if self.formatter is not None:
        # self.formatter.setData(results)
        # model_root_node = self.formatter.root(valid_str_ids)
        prog_dialog.setValue(10)
        prog_dialog.hide()

        prog_dialog.deleteLater()
        del prog_dialog

        return results, search_term
Пример #6
0
    def _load_source_documents(self, source_docs):
        """
        Load source documents into document listing widget.
        """
        # Configure progress dialog
        progress_msg = QApplication.translate(
            "ViewSTR", "Loading supporting documents...")

        progress_dialog = QProgressDialog(self)
        if len(source_docs) > 0:
            progress_dialog.setWindowTitle(progress_msg)
            progress_dialog.setRange(0, len(source_docs))
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.setFixedWidth(380)
            progress_dialog.show()
            progress_dialog.setValue(0)
        self._notif_search_config.clear()

        self.tbSupportingDocs.clear()
        self._source_doc_manager.reset()

        if len(source_docs) < 1:
            empty_msg = QApplication.translate(
                'ViewSTR', 'No supporting document is uploaded '
                'for this social tenure relationship.')
            self._notif_search_config.clear()
            self._notif_search_config.insertWarningNotification(empty_msg)

        for i, (doc_type_id, doc_obj) in enumerate(source_docs.items()):

            # add tabs, and container and widget for each tab
            tab_title = self._source_doc_manager.doc_type_mapping[doc_type_id]

            tab_widget = QWidget()
            tab_widget.setObjectName(tab_title)

            cont_layout = QVBoxLayout(tab_widget)
            cont_layout.setObjectName('widget_layout_' + tab_title)

            scrollArea = QScrollArea(tab_widget)
            scrollArea.setFrameShape(QFrame.NoFrame)

            scrollArea_contents = QWidget()
            scrollArea_contents.setObjectName('tab_scroll_area_' + tab_title)

            tab_layout = QVBoxLayout(scrollArea_contents)
            tab_layout.setObjectName('layout_' + tab_title)

            scrollArea.setWidgetResizable(True)

            scrollArea.setWidget(scrollArea_contents)
            cont_layout.addWidget(scrollArea)

            self._source_doc_manager.registerContainer(tab_layout, doc_type_id)

            for doc in doc_obj:

                try:
                    # add doc widgets
                    self._source_doc_manager.insertDocFromModel(
                        doc, doc_type_id)
                except DummyException as ex:
                    LOGGER.debug(str(ex))

            self.tbSupportingDocs.addTab(tab_widget, tab_title)
            progress_dialog.setValue(i + 1)

        progress_dialog.deleteLater()
        del progress_dialog
Пример #7
0
    def featToDb(self, targettable, columnmatch, append, parentdialog,
                 geomColumn=None, geomCode=-1, translator_manager=None):
        """
        Performs the data import from the source layer to the STDM database.
        :param targettable: Destination table name
        :param columnmatch: Dictionary containing source columns as keys and target columns as the values.
        :param append: True to append, false to overwrite by deleting previous records
        :param parentdialog: A reference to the calling dialog.
        :param translator_manager: Instance of 'stdm.data.importexport.ValueTranslatorManager'
        containing value translators defined for the destination table columns.
        :type translator_manager: ValueTranslatorManager
        """
        # Check current profile
        if self._current_profile is None:
            msg = QApplication.translate(
                'OGRReader',
                'The current profile could not be determined.\nPlease set it '
                'in the Options dialog or Configuration Wizard.'
            )
            raise ConfigurationException(msg)

        if translator_manager is None:
            translator_manager = ValueTranslatorManager()

        # Delete existing rows in the target table if user has chosen to overwrite
        if not append:
            delete_table_data(targettable)

        # Container for mapping column names to their corresponding values

        lyr = self.getLayer()
        lyr.ResetReading()
        feat_defn = lyr.GetLayerDefn()
        numFeat = lyr.GetFeatureCount()

        # Configure progress dialog
        init_val = 0
        progress = QProgressDialog("", "&Cancel", init_val, numFeat,
                                   parentdialog)
        progress.setWindowModality(Qt.WindowModal)
        lblMsgTemp = "Importing {0} of {1} to STDM..."

        # Set entity for use in translators
        destination_entity = self._data_source_entity(targettable)

        for feat in lyr:
            column_value_mapping = {}
            column_count = 0
            progress.setValue(init_val)
            progressMsg = lblMsgTemp.format((init_val + 1), numFeat)
            progress.setLabelText(progressMsg)

            if progress.wasCanceled():
                break

            # Reset source document manager for new records
            if destination_entity.supports_documents:
                if self._source_doc_manager is not None:
                    self._source_doc_manager.reset()

            for f in range(feat_defn.GetFieldCount()):
                field_defn = feat_defn.GetFieldDefn(f)
                field_name = field_defn.GetNameRef()

                # Append value only if it has been defined by the user
                if field_name in columnmatch:
                    dest_column = columnmatch[field_name]

                    field_value = feat.GetField(f)

                    # Create mapped class only once
                    if self._mapped_cls is None:
                        mapped_cls, mapped_doc_cls = self._get_mapped_class(
                            targettable)

                        if mapped_cls is None:
                            msg = QApplication.translate(
                                "OGRReader",
                                "Something happened that caused the "
                                "database table not to be mapped to the "
                                "corresponding model class. Please contact"
                                " your system administrator."
                            )

                            raise RuntimeError(msg)

                        self._mapped_cls = mapped_cls
                        self._mapped_doc_cls = mapped_doc_cls

                        # Create source document manager if the entity supports them
                        if destination_entity.supports_documents:
                            self._source_doc_manager = SourceDocumentManager(
                                destination_entity.supporting_doc,
                                self._mapped_doc_cls
                            )

                        if geomColumn is not None:
                            # Use geometry column SRID in the target table
                            self._geomType, self._targetGeomColSRID = \
                                geometryType(targettable, geomColumn)

                    '''
                    Check if there is a value translator defined for the
                    specified destination column.
                    '''
                    value_translator = translator_manager.translator(
                        dest_column)

                    if value_translator is not None:
                        # Set destination table entity
                        value_translator.entity = destination_entity

                        source_col_names = value_translator.source_column_names()
                        field_value_mappings = self._map_column_values(
                            feat,
                            feat_defn,
                            source_col_names
                        )
                        # Set source document manager if required
                        if value_translator.requires_source_document_manager:
                            value_translator.source_document_manager = self._source_doc_manager

                        field_value = value_translator.referencing_column_value(
                            field_value_mappings
                        )

                    if not isinstance(field_value, IgnoreType):
                        # Check column type and rename if multiple select for
                        # SQLAlchemy compatibility
                        col_obj = destination_entity.column(dest_column)
                        if col_obj.TYPE_INFO == 'MULTIPLE_SELECT':
                            lk_name = col_obj.value_list.name
                            dest_column = '{0}_collection'.format(lk_name)

                        column_value_mapping[dest_column] = field_value

                    # Set supporting documents
                    if destination_entity.supports_documents:
                        column_value_mapping['documents'] = \
                            self._source_doc_manager.model_objects()

                    column_count += 1

            # Only insert geometry if it has been defined by the user
            if geomColumn is not None:
                geom = feat.GetGeometryRef()
                if geom is not None:
                    # Check if the geometry types match
                    layerGeomType = geom.GetGeometryName()

                    # Convert polygon to multipolygon if the destination table is multi-polygon.
                    geom_wkb, geom_type = self.auto_fix_geom_type(
                        geom, layerGeomType, self._geomType)
                    column_value_mapping[geomColumn] = "SRID={0!s};{1}".format(
                        self._targetGeomColSRID, geom_wkb)

                    if geom_type.lower() != self._geomType.lower():
                        raise TypeError(
                            "The geometries of the source and destination columns do not match.\n"
                            "Source Geometry Type: {0}, Destination Geometry Type: {1}".format(
                                geom_type,
                                self._geomType))

            # Insert the record
            self._insertRow(targettable, column_value_mapping)

            init_val += 1

        try:
            self._dbSession.commit()
        except (DataError, IntegrityError) as e:
            self._dbSession.rollback()
            progress.close()
            progress.deleteLater()
            del progress
            raise ImportFeatureException(str(e))

        progress.setValue(numFeat)

        progress.deleteLater()
        del progress
Пример #8
0
    def onGenerate(self):
        """
        Slot raised to initiate the certificate generation process.
        """
        self._notif_bar.clear()
        success_status = True
        config = self.current_config()
        self.last_data_source = config.data_source()
        if config is None:
            self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \
                                                                           "The entity configuration could not be extracted."))
            return

        # Get selected records and validate
        records = self.tabWidget.currentWidget().entities()

        if self.chk_template_datasource.isChecked():
            records = self._dummy_template_records()

        if len(records) == 0:
            self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \
                                                                           "Please load at least one entity record"))
            return

        if not self._docTemplatePath:
            self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \
                                                                           "Please select a document template to use"))
            return

        documentNamingAttrs = self.lstDocNaming.selectedMappings()

        if self.chkUseOutputFolder.checkState() == Qt.Checked and len(
                documentNamingAttrs) == 0:
            self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \
                                                                           "Please select at least one field for naming the output document"))

            return

        # Set output file properties
        if self.rbExpImage.isChecked():
            outputMode = DocumentGenerator.Image
            fileExtension = self.cboImageType.currentText()
            saveAsText = "Image File"

        else:
            outputMode = DocumentGenerator.PDF
            fileExtension = "pdf"
            saveAsText = "PDF File"

        # Show save file dialog if not using output folder
        if self.chkUseOutputFolder.checkState() == Qt.Unchecked:
            docDir = source_document_location()

            if self._outputFilePath:
                fileInfo = QFileInfo(self._outputFilePath)
                docDir = fileInfo.dir().path()

            self._outputFilePath, _ = QFileDialog.getSaveFileName(
                self,
                QApplication.translate("DocumentGeneratorDialog",
                                       "Save Document"), docDir,
                "{0} (*.{1})".format(
                    QApplication.translate("DocumentGeneratorDialog",
                                           saveAsText), fileExtension))

            if not self._outputFilePath:
                self._notif_bar.insertErrorNotification(
                    QApplication.translate(
                        "DocumentGeneratorDialog",
                        "Process aborted. No output file was specified."))

                return

            # Include extension in file name
            self._outputFilePath = self._outputFilePath  # + "." + fileExtension

        # else:
        # Multiple files to be generated.
        # pass

        self._doc_generator.set_link_field(config.link_field())

        self._doc_generator.clear_attr_value_formatters()

        if not self.chk_template_datasource.isChecked():
            # Apply cell formatters for naming output files
            self._doc_generator.set_attr_value_formatters(config.formatters())

        entity_field_name = "id"

        # Iterate through the selected records
        progressDlg = QProgressDialog(self)
        progressDlg.setMaximum(len(records))

        try:
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

            for i, record in enumerate(records):
                progressDlg.setValue(i)

                if progressDlg.wasCanceled():
                    success_status = False
                    break

                # User-defined location
                if self.chkUseOutputFolder.checkState() == Qt.Unchecked:
                    status, msg = self._doc_generator.run(
                        self._docTemplatePath,
                        entity_field_name,
                        record.id,
                        outputMode,
                        data_source=self.ds_entity.name,
                        filePath=self._outputFilePath)
                    self._doc_generator.clear_temporary_layers()
                # Output folder location using custom naming
                else:

                    status, msg = self._doc_generator.run(
                        self._docTemplatePath,
                        entity_field_name,
                        record.id,
                        outputMode,
                        dataFields=documentNamingAttrs,
                        fileExtension=fileExtension,
                        data_source=self.ds_entity.name)
                    self._doc_generator.clear_temporary_layers()

                if not status:
                    result = QMessageBox.warning(
                        self,
                        QApplication.translate("DocumentGeneratorDialog",
                                               "Document Generate Error"), msg,
                        QMessageBox.Ignore | QMessageBox.Abort)

                    if result == QMessageBox.Abort:
                        progressDlg.close()
                        progressDlg.deleteLater()
                        del progressDlg
                        success_status = False

                        # Restore cursor
                        QApplication.restoreOverrideCursor()

                        return

                    # If its the last record and user has selected to ignore
                    if i + 1 == len(records):
                        progressDlg.close()
                        progressDlg.deleteLater()
                        del progressDlg
                        success_status = False

                        # Restore cursor
                        QApplication.restoreOverrideCursor()

                        return

                else:
                    progressDlg.setValue(len(records))

            QApplication.restoreOverrideCursor()

            QMessageBox.information(
                self,
                QApplication.translate("DocumentGeneratorDialog",
                                       "Document Generation Complete"),
                QApplication.translate(
                    "DocumentGeneratorDialog",
                    "Document generation has successfully completed."))

        except DummyException as ex:
            LOGGER.debug(str(ex))
            err_msg = sys.exc_info()[1]
            QApplication.restoreOverrideCursor()

            QMessageBox.critical(
                self, "STDM",
                QApplication.translate(
                    "DocumentGeneratorDialog",
                    "Error Generating documents - %s" % (err_msg)))
            success_status = False

        progressDlg.deleteLater()
        del progressDlg

        # Reset UI
        self.reset(success_status)