Exemple #1
0
def layer_from_source(source_uri, name):
    """Return QgsMapLayer from a given source uri.

    :param source_uri: A source URI
    :type source_uri: basestring

    :param name: Designated layer name
    :type name: basestring

    :return: QgsMapLayer
    :rtype: qgis.core.QgsMapLayer
    """
    vector_extensions = ('shp', 'geojson')
    raster_extensions = ('asc', 'tiff', 'tif', 'geotiff', 'geotif')
    qlr_extensions = ('qlr', )

    qgis_layer = None

    if is_file_path(source_uri):

        # sanitize source_uri
        sanitized_uri = urllib.unquote(source_uri).decode('utf-8')
        sanitized_uri.replace('file://', '')

        if source_uri.endswith(vector_extensions):
            qgis_layer = QgsVectorLayer(sanitized_uri, name, 'ogr')

        elif source_uri.endswith(raster_extensions):
            qgis_layer = QgsRasterLayer(sanitized_uri, name)

        elif source_uri.endswith(qlr_extensions):

            qgis_layer = QgsMapLayer.fromLayerDefinitionFile(sanitized_uri)
            if qgis_layer:
                qgis_layer = qgis_layer[0]
                qgis_layer.setName(name)

    elif is_tile_path(source_uri):

        # sanitize source_uri
        sanitized_uri = urllib.unquote(source_uri).decode('utf-8')
        # Check if it is only a url
        if sanitized_uri.startswith(('http://', 'https://')):
            # Then it is probably a tile xyz url
            sanitized_uri = 'type=xyz&url={0}'.format(sanitized_uri)
        # It might be in the form of query string
        query_params = urlparse.parse_qs(sanitized_uri)
        driver = query_params.get('driver', 'wms')

        qgis_layer = QgsRasterLayer(sanitized_uri, name, driver)

    return qgis_layer
Exemple #2
0
    def testLayerChangeDirtiesProject(self):
        """
        Test that making changes to certain layer properties results in dirty projects
        """
        p = QgsProject()
        l = QgsVectorLayer(os.path.join(TEST_DATA_DIR, "points.shp"), "points", "ogr")
        self.assertTrue(l.isValid())
        self.assertTrue(p.addMapLayers([l]))
        p.setDirty(False)

        l.setCrs(QgsCoordinateReferenceSystem('EPSG:3111'))
        self.assertTrue(p.isDirty())
        p.setDirty(False)

        l.setName('test')
        self.assertTrue(p.isDirty())
        p.setDirty(False)

        self.assertTrue(l.setSubsetString('class=\'a\''))
        self.assertTrue(p.isDirty())
Exemple #3
0
    def testLayerChangeDirtiesProject(self):
        """
        Test that making changes to certain layer properties results in dirty projects
        """
        p = QgsProject()
        l = QgsVectorLayer(os.path.join(TEST_DATA_DIR, "points.shp"), "points",
                           "ogr")
        self.assertTrue(l.isValid())
        self.assertTrue(p.addMapLayers([l]))
        p.setDirty(False)

        l.setCrs(QgsCoordinateReferenceSystem('EPSG:3111'))
        self.assertTrue(p.isDirty())
        p.setDirty(False)

        l.setName('test')
        self.assertTrue(p.isDirty())
        p.setDirty(False)

        self.assertTrue(l.setSubsetString('class=\'a\''))
        self.assertTrue(p.isDirty())
    def load_vector(self, csv_file, qml_file=None):
        # uri = "file:///{}?encoding=UTF-8&delimiter=,&xField=LON&yField=LAT&crs=EPSG:4326".format(regnie_csv_file)
        # -> was working, simpler variant:
        # uri = "{}{}?delimiter=,&xField=LON&yField=LAT".format('file:///', regnie_csv_file)
        # but not on Windows - needs crs specified:
        uri = "{}{}?delimiter=,&xField=LON&yField=LAT&crs=EPSG:4326".format(
            "file:///", csv_file)
        self.out("uri: {}".format(uri))

        # Make a vector layer:
        csv_layer = QgsVectorLayer(uri, csv_file.name, "delimitedtext")

        if not csv_layer.isValid():
            self._show_message(Qgis.Critical, csv_file.name)
            return

        layer_name = Path(csv_file).stem
        self._layer_name = layer_name
        csv_layer.setName(layer_name)
        self._remove_layer_with_same_name(layer_name)
        self._insert_layer(csv_layer, qml_file, 5)
    def _create_named_layer(self, json_src, layer_name, zoom_level, merge_features, geo_type):
        """
         * Creates a QgsVectorLayer and adds it to the group specified by layer_target_group
         * Invalid geometries will be removed during the process of merging features over tile boundaries
        """

        # layer_with_zoom = "{}{}{}".format(layer_name, VtReader._zoom_level_delimiter, zoom_level)
        layer = QgsVectorLayer(json_src, layer_name, "ogr")

        layer.setCustomProperty("vector_tile_source", self.source.source())
        layer.setCustomProperty("zoom_level", zoom_level)
        layer.setShortName(layer_name)
        layer.setDataUrl(self.source.source())

        if self.source.name() and "openmaptiles" in self.source.name().lower():
            layer.setDataUrl(remove_key(self.source.source()))
            layer.setAttribution(u"Vector Tiles © Klokan Technologies GmbH (CC-BY), Data © OpenStreetMap contributors (ODbL)")
            layer.setAttributionUrl("https://openmaptiles.com/hosting/")

        if merge_features and geo_type in [GeoTypes.LINE_STRING, GeoTypes.POLYGON]:
            layer = FeatureMerger().merge_features(layer)
            layer.setName(layer_name)
        return layer
    def startDraw(self):
        # Create and add the new layer
        layer = QgsVectorLayer('Polygon?crs=epsg:4326', providerLib='memory')
        layer.renderer().symbol().setColor(QColor.fromRgb(160, 82, 45))
        layer.setName(f'search aoi')
        QgsProject.instance().addMapLayer(layer)

        # Use the add feature tool
        self.iface.setActiveLayer(layer)

        # Function called when the feature is added to the layer
        def featureAdded():
            # Disconnect from the signal
            layer.featureAdded.disconnect()
            # Save changes and end edit mode
            layer.commitChanges()
            # Select the feature then fill the input
            feature = layer.selectAll()
            self.fillPolygonInput()

        # Connect the layer to the signal featureAdded
        layer.featureAdded.connect(featureAdded)
        layer.startEditing()
        self.iface.actionAddFeature().trigger()
    def testSaveLoadProject(self):
        schema_uri = self.encode_uri(self.ds_uri, self.schema)
        project_uri = self.encode_uri(self.ds_uri, self.schema, 'abc')

        self.dropProjectsTable()  # make sure we have a clean start

        prj = QgsProject()
        uri = self.vl.source()

        vl1 = QgsVectorLayer(uri, 'test', self.provider)
        self.assertEqual(vl1.isValid(), True)
        prj.addMapLayer(vl1)

        prj_storage = QgsApplication.projectStorageRegistry(
        ).projectStorageFromType(self.project_storage_type)
        self.assertTrue(prj_storage)

        lst0 = prj_storage.listProjects(schema_uri)
        self.assertEqual(lst0, [])

        # try to save project in the database

        prj.setFileName(project_uri)
        res = prj.write()
        self.assertTrue(res)

        lst1 = prj_storage.listProjects(schema_uri)
        self.assertEqual(lst1, ["abc"])

        # now try to load the project back

        prj2 = QgsProject()
        prj2.setFileName(project_uri)
        res = prj2.read()
        self.assertTrue(res)

        self.assertEqual(len(prj2.mapLayers()), 1)

        self.assertEqual(prj2.baseName(), "abc")
        self.assertEqual(prj2.absoluteFilePath(),
                         "")  # path not supported for project storages
        self.assertTrue(
            abs(prj2.lastModified().secsTo(QDateTime.currentDateTime())) < 10)
        lastModified = prj2.lastModified()

        # try to see project's metadata

        res, metadata = prj_storage.readProjectStorageMetadata(project_uri)
        self.assertTrue(res)
        self.assertEqual(metadata.name, "abc")
        time_project = metadata.lastModified
        time_now = QDateTime.currentDateTime()
        time_diff = time_now.secsTo(time_project)
        self.assertTrue(abs(time_diff) < 10)

        # try to update the project
        vl1.setName("testNew")
        prj.write()

        prj3 = QgsProject()
        prj3.setFileName(project_uri)
        res = prj3.read()
        self.assertTrue(res)

        prj4 = QgsProject()
        prj4.setFileName(project_uri)
        res = prj4.read()
        self.assertTrue(res)

        self.assertEqual(len(prj4.mapLayers()), 1)
        self.assertEqual(list(prj4.mapLayers().values())[0].name(), "testNew")

        self.assertEqual(prj4.baseName(), "abc")
        self.assertEqual(prj4.absoluteFilePath(),
                         "")  # path not supported for project storages
        self.assertTrue(prj4.lastModified() > lastModified)

        # try to remove the project

        res = prj_storage.removeProject(project_uri)
        self.assertTrue(res)

        lst2 = prj_storage.listProjects(schema_uri)
        self.assertEqual(lst2, [])

        self.dropProjectsTable(
        )  # make sure we have a clean finish... "leave no trace"
Exemple #8
0
class Layer:
    '''
    wrapper of a vector layer in the QGIS layer tree with some
    convenient functions. Can be grouped and addressed by its name.
    '''
    def __init__(self,
                 layername: str,
                 data_path: str,
                 groupname: str = '',
                 prepend: bool = True):
        '''
        Parameters
        ----------
        layername : str
            name of the layer in the data source
        data_path : str
            path to the data source of the layer
        groupname : str, optional
            name of the parent group, will be created if not existing, can be
            nested by joining groups with '/' e.g. 'Projekt/Hintergrundkarten',
            defaults to add layer to the root of the layer tree
        prepend : bool
            prepend the group of the layer if True (prepends each group if
            nested), append if False, defaults to prepending the group
        '''
        self.layername = layername
        self.data_path = data_path
        self.layer = None
        self._l = None
        self.groupname = groupname
        self.prepend = prepend

    @property
    def parent(self) -> QgsLayerTreeGroup:
        '''
        the parent group of the layer
        '''
        parent = QgsProject.instance().layerTreeRoot()
        if self.groupname:
            parent = Layer.add_group(self.groupname, prepend=self.prepend)
        return parent

    @property
    def _tree_layer(self) -> QgsLayerTreeLayer:
        '''
        tree representation of the layer
        '''
        if not self.layer:
            return None
        return self.parent.findLayer(self.layer)

    @property
    def layer(self) -> QgsVectorLayer:
        '''
        the wrapped vector layer
        '''
        try:
            layer = self._layer
            if layer is not None:
                # call function on layer to check if it still exists
                layer.id()
        except RuntimeError:
            return None
        return layer

    @layer.setter
    def layer(self, layer: QgsVectorLayer):
        self._layer = layer

    @classmethod
    def add_group(cls,
                  groupname: str,
                  prepend: bool = True) -> QgsLayerTreeGroup:
        '''
        add a group to the layer tree

        Parameters
        ----------
        groupname : str
            name of the parent group, will be created if not existing, can be
            nested by joining groups with '/' e.g. 'Projekt/Hintergrundkarten'
        prepend : bool, optional
            prepend the group if True (prepends each group if nested),
            append if False, defaults to prepending the group

        Returns
        ----------
        QgsLayerTreeGroup
            the created group (the deepest one in hierarchy if nested)
        '''
        groupnames = groupname.split('/')
        parent = QgsProject.instance().layerTreeRoot()
        group = cls._nest_groups(parent, groupnames, prepend=prepend)
        return group

    @classmethod
    def _nest_groups(cls,
                     parent: QgsLayerTreeGroup,
                     groupnames: List[str],
                     prepend: bool = True) -> QgsLayerTreeGroup:
        '''recursively nests groups in order of groupnames'''
        if len(groupnames) == 0:
            return parent
        next_parent = parent.findGroup(groupnames[0])
        if not next_parent:
            next_parent = (parent.insertGroup(0, groupnames[0])
                           if prepend else parent.addGroup(groupnames[0]))
        return cls._nest_groups(next_parent, groupnames[1:], prepend=prepend)

    @classmethod
    def find(cls, label: str, groupname: str = '') -> List[QgsLayerTreeLayer]:
        '''
        deep find tree layer by name in a group recursively

        Parameters
        ----------
        label : str
            label of the tree layer
        groupname : str, optional
            name of the group to search in, can be nested by joining groups with
            '/' e.g. 'Projekt/Hintergrundkarten', defaults to searching in layer
            tree root

        Returns
        ----------
        list
            list of tree layers matching the name, empty list if none found
        '''
        parent = QgsProject.instance().layerTreeRoot()
        if groupname:
            groupnames = groupname.split('/')
            while groupnames:
                g = groupnames.pop(0)
                parent = parent.findGroup(g)
                if not parent:
                    return []

        def deep_find(node, label):
            found = []
            if node:
                for child in node.children():
                    if child.name() == label:
                        found.append(child)
                    found.extend(deep_find(child, label))
            return found

        found = deep_find(parent, label)
        return found

    def draw(self,
             style_path: str = None,
             label: str = '',
             redraw: str = True,
             checked: bool = True,
             filter: str = None,
             expanded: bool = True,
             prepend: bool = False) -> QgsVectorLayer:
        '''
        load the data into a vector layer, draw it and add it to the layer tree

        Parameters
        ----------
        label : str, optional
            label of the layer, defaults to layer name this is initialized with
        style_path : str, optional
            a QGIS style (.qml) can be applied to the layer, defaults to no
            style
        redraw : bool, optional
            replace old layer with same name in same group if True,
            only create if not existing if set to False, else it is refreshed,
            defaults to redrawing the layer
        checked: bool, optional
            set check state of layer in layer tree, defaults to being checked
        filter: str, optional
            QGIS filter expression to filter the layer, defaults to no filtering
        expanded: str, optional
            sets the legend to expanded or not, defaults to an expanded legend
        prepend: bool, optional
            prepend the layer to the other layers in its group if True,
            append it if False, defaults to appending the layer

        Returns
        ----------
        QgsVectorLayer
            the created, replaced or refreshed vector layer

        '''
        if not self.layer:
            layers = Layer.find(label, groupname=self.groupname)
            if layers:
                self.layer = layers[0].layer()
        if redraw:
            self.remove()
        else:
            iface.mapCanvas().refreshAllLayers()

        if not self.layer:
            self.layer = QgsVectorLayer(self.data_path, self.layername, "ogr")
            if label:
                self.layer.setName(label)
            QgsProject.instance().addMapLayer(self.layer, False)
            self.layer.loadNamedStyle(style_path)
        tree_layer = self._tree_layer
        if not tree_layer:
            tree_layer = self.parent.insertLayer(0, self.layer) if prepend else\
                self.parent.addLayer(self.layer)
        tree_layer.setItemVisibilityChecked(checked)
        tree_layer.setExpanded(expanded)
        if filter is not None:
            self.layer.setSubsetString(filter)
        return self.layer

    def set_visibility(self, state: bool):
        '''
        change check state of layer, layer is not visible if unchecked

        Parameters
        ----------
        state: bool
            set check state of layer in layer tree
        '''

        tree_layer = self._tree_layer
        if tree_layer:
            tree_layer.setItemVisibilityChecked(state)

    def zoom_to(self):
        '''
        zooms map canvas to the extent of this layer
        '''
        if not self.layer:
            return
        canvas = iface.mapCanvas()
        self.layer.updateExtents()
        transform = QgsCoordinateTransform(
            self.layer.crs(),
            canvas.mapSettings().destinationCrs(), QgsProject.instance())
        canvas.setExtent(transform.transform(self.layer.extent()))

    def remove(self):
        '''
        remove the layer from map and layer tree
        '''
        if not self.layer:
            return
        QgsProject.instance().removeMapLayer(self.layer.id())
        self.layer = None
Exemple #9
0
def open_file(
        dialog=None,
        osm_file=None,
        output_geom_types=None,
        white_list_column=None,
        layer_name="OsmFile",
        config_outputs=None,
        output_dir=None,
        final_query=None,
        prefix_file=None):
    """
    Open an osm file.

    Memory layer if no output directory is set, or Geojson in the output directory.

    :param final_query: The query where the file comes from. Might be empty if
    it's a local OSM file.
    :type final_query: basestring
    """
    outputs = {}
    if output_dir:
        for layer in ['points', 'lines', 'multilinestrings', 'multipolygons']:
            if not prefix_file:
                prefix_file = layer_name

            outputs[layer] = join(
                output_dir, prefix_file + "_" + layer + ".geojson")

            if isfile(outputs[layer]):
                raise FileOutPutException(suffix='(' + outputs[layer] + ')')

    # Legacy, waiting to remove the OsmParser for QGIS >= 3.6
    # Change in osm_file_dialog.py L131 too
    output_geom_legacy = [l.value.lower() for l in output_geom_types]
    if not white_list_column:
        white_list_column = {}
    white_list_legacy = (
        {l.value.lower(): csv for l, csv in white_list_column.items()}
    )

    LOGGER.info('The OSM file is: {}'.format(osm_file))

    # Parsing the file
    osm_parser = OsmParser(
        osm_file=osm_file,
        layers=output_geom_legacy,
        white_list_column=white_list_legacy)

    osm_parser.signalText.connect(dialog.set_progress_text)
    osm_parser.signalPercentage.connect(dialog.set_progress_percentage)

    start_time = time.time()
    layers = osm_parser.parse()
    elapsed_time = time.time() - start_time
    parser_time = time.strftime("%Hh %Mm %Ss", time.gmtime(elapsed_time))
    LOGGER.info('The OSM parser took: {}'.format(parser_time))

    # Finishing the process with geojson or memory layer
    num_layers = 0

    for i, (layer, item) in enumerate(layers.items()):
        dialog.set_progress_percentage(i / len(layers) * 100)
        QApplication.processEvents()
        if item['featureCount'] and LayerType(layer.capitalize()) in output_geom_types:

            final_layer_name = layer_name
            # If configOutputs is not None (from My Queries)
            if config_outputs:
                if config_outputs[layer]['namelayer']:
                    final_layer_name = config_outputs[layer]['namelayer']

            if output_dir:
                dialog.set_progress_text(tr('From memory to GeoJSON: ' + layer))
                # Transforming the vector file
                osm_geometries = {
                    'points': QgsWkbTypes.Point,
                    'lines': QgsWkbTypes.LineString,
                    'multilinestrings': QgsWkbTypes.MultiLineString,
                    'multipolygons': QgsWkbTypes.MultiPolygon}
                memory_layer = item['vector_layer']

                encoding = get_default_encoding()
                writer = QgsVectorFileWriter(
                    outputs[layer],
                    encoding,
                    memory_layer.fields(),
                    osm_geometries[layer],
                    memory_layer.crs(),
                    "GeoJSON")

                for f in memory_layer.getFeatures():
                    writer.addFeature(f)

                del writer

                # Loading the final vector file
                new_layer = QgsVectorLayer(outputs[layer], final_layer_name, "ogr")
            else:
                new_layer = item['vector_layer']
                new_layer.setName(final_layer_name)

            # Try to set styling if defined
            if config_outputs and config_outputs[layer]['style']:
                new_layer.loadNamedStyle(config_outputs[layer]['style'])
            else:
                # Loading default styles
                if layer == "multilinestrings" or layer == "lines":
                    if "colour" in item['tags']:
                        new_layer.loadNamedStyle(
                            join(dirname(dirname(abspath(__file__))),
                                 "styles",
                                 layer + "_colour.qml"))

            # Add action about OpenStreetMap
            add_actions(new_layer, item['tags'])

            if final_query:
                QgsExpressionContextUtils.setLayerVariable(
                    new_layer, 'quickosm_query', final_query)

            QgsProject.instance().addMapLayer(new_layer)
            num_layers += 1

    return num_layers
    def get_layer_description_from_browser(self, category):
        """Obtain the description of the browser layer selected by user.

        :param category: The category of the layer to get the description.
        :type category: string

        :returns: Tuple of boolean and string. Boolean is true if layer is
            validated as compatible for current role (impact function and
            category) and false otherwise. String contains a description
            of the selected layer or an error message.
        :rtype: tuple
        """

        if category == 'hazard':
            browser = self.tvBrowserHazard
        elif category == 'exposure':
            browser = self.tvBrowserExposure
        elif category == 'aggregation':
            browser = self.tvBrowserAggregation
        else:
            raise InaSAFEError

        index = browser.selectionModel().currentIndex()
        if not index:
            return False, ''

        # Map the proxy model index to the source model index
        index = browser.model().mapToSource(index)
        item = browser.model().sourceModel().dataItem(index)
        if not item:
            return False, ''

        item_class_name = item.metaObject().className()
        # if not itemClassName.endswith('LayerItem'):
        if not item.type() == QgsDataItem.Layer:
            if item_class_name == 'QgsPGRootItem' and not item.children():
                return False, create_postGIS_connection_first
            else:
                return False, ''

        if item_class_name not in [
                'QgsOgrLayerItem', 'QgsGdalLayerItem', 'QgsPGLayerItem',
                'QgsLayerItem', ]:
            return False, ''

        path = item.path()

        if item_class_name in ['QgsOgrLayerItem', 'QgsGdalLayerItem',
                               'QgsLayerItem'] and not os.path.exists(path):
            return False, ''

        # try to create the layer
        if item_class_name == 'QgsOgrLayerItem':
            layer = QgsVectorLayer(path, '', 'ogr')
        elif item_class_name == 'QgsPGLayerItem':
            uri = self.postgis_path_to_uri(path)
            if uri:
                layer = QgsVectorLayer(uri.uri(), uri.table(), 'postgres')
            else:
                layer = None
        else:
            layer = QgsRasterLayer(path, '', 'gdal')

        if not layer or not layer.isValid():
            return False, self.tr('Not a valid layer.')

        try:
            keywords = self.keyword_io.read_keywords(layer)
            if ('layer_purpose' not in keywords and
                    'impact_summary' not in keywords):
                keywords = None
        except (HashNotFoundError,
                OperationalError,
                NoKeywordsFoundError,
                KeywordNotFoundError,
                InvalidParameterError,
                UnsupportedProviderError,
                MissingMetadata):
            keywords = None

        # set the layer name for further use in the step_fc_summary
        if keywords:
            if qgis_version() >= 21800:
                layer.setName(keywords.get('title'))
            else:
                layer.setLayerName(keywords.get('title'))

        if not self.parent.is_layer_compatible(layer, category, keywords):
            label_text = '%s<br/>%s' % (
                self.tr(
                    'This layer\'s keywords or type are not suitable:'),
                self.unsuitable_layer_description_html(
                    layer, category, keywords))
            return False, label_text

        # set the current layer (e.g. for the keyword creation sub-thread
        #                          or for adding the layer to mapCanvas)
        self.parent.layer = layer
        if category == 'hazard':
            self.parent.hazard_layer = layer
        elif category == 'exposure':
            self.parent.exposure_layer = layer
        else:
            self.parent.aggregation_layer = layer

        # Check if the layer is keywordless
        if keywords and 'keyword_version' in keywords:
            kw_ver = str(keywords['keyword_version'])
            self.parent.is_selected_layer_keywordless = (
                not is_keyword_version_supported(kw_ver))
        else:
            self.parent.is_selected_layer_keywordless = True

        desc = layer_description_html(layer, keywords)
        return True, desc
Exemple #11
0
def add_layer_from_geopackage(gpkgfile, layer_name, categories_field=None):
    gpkgfile += f"|layername={layer_name}"
    l = QgsVectorLayer(gpkgfile)
    l.setName(layer_name)
    QgsProject.instance().addMapLayer(l)
    return l
Exemple #12
0
 def on_click_Inici(self):
     global lbl_Cost
     global data
     global listFields
     global urlToLoad
     global TEMPORARY_PATH
 
     self.dlg.setEnabled(False)
     '''Tratamiento de errores'''
     llistaErrors = self.controlErrorsInput()
     if len(llistaErrors) > 0:
         llista = "Llista d'errors:\n\n"
         for i in range (0,len(llistaErrors)):
             llista += ("- "+llistaErrors[i] + '\n')
         QMessageBox.information(None, "Error", llista)
         self.dlg.setEnabled(True)
         return
     
     
     textBox = u'INICI DEL PROCÉS\n\n'
     self.dlg.text_info.setText(textBox)
     self.MouText()
     campNom = ''
     campGeometria = ''
     campCod = ''
     campSrc = ''
     campLat = ''
     campLng = ''
     
     
     '''Obtención de Nom y Geometria'''
     if self.dlg.radio_ws.isChecked():
         campNom,campGeometria,campLat, campLng, campCod,campSrc=self.searchNomGeomCodSrcInFile()
         urlToLoad=self.dlg.combo_ws.currentText()
         #if(urlToLoad[-4:]=='.csv'):
         error = self.loadCSV(campNom, campGeometria, campCod,True)
         if (error=="Error"):
             self.dlg.setEnabled(True)
             return
 
     else:
         campNom = self.dlg.combo_nom.currentText()
         campCod=self.dlg.combo_cod.currentText()
         campSrc=self.dlg.combo_src.currentText()   
         if self.dlg.radio_geom.isChecked():
             campGeometria = self.dlg.combo_geom.currentText()
         elif self.dlg.radio_latlng.isChecked():
             campLat=self.dlg.combo_lat.currentText()
             campLng=self.dlg.combo_lng.currentText()
     
     '''Creación vector layer'''
     self.dlg.progressBar.setValue(60)
     textBox += u'Generant capa vectorial...\n'
     self.dlg.text_info.setText(textBox)
     self.MouText()
     if campGeometria != '':
         file = 'file:///'+TEMPORARY_PATH+'/WS.csv?encoding=%s&delimiter=%s&wktField=%s&crs=%s' % (campCod,",", campGeometria,campSrc)
     elif campLat != '' and campLng != '':
         file = 'file:///'+TEMPORARY_PATH+'/WS.csv?encoding=%s&delimiter=%s&xField=%s&yField=%s&crs=%s' % (campCod,",", campLng, campLat,campSrc)
     else:
         file = 'file:///'+TEMPORARY_PATH+'/WS.csv?encoding=%s&delimiter=%s' % (campCod,",")  
     vlayergeom = QgsVectorLayer(file, self.dlg.txt_nomTaula.text(),'delimitedtext')
     try:
         vlayergeom = self.comprobarValidez(vlayergeom) #Sirve tanto para comprobar la corrección del CSV como para pasar el layer a memoria
     except Exception as ex:
         missatge="La geometria seleccionada no és correcte"
         print (missatge)
         template = "An exception of type {0} occurred. Arguments:\n{1!r}"
         message = template.format(type(ex).__name__, ex.args)
         print (message)
         QMessageBox.information(None, "Error", missatge)
         self.dlg.text_info.setText('')
         self.dlg.progressBar.setValue(0)
         self.dlg.setEnabled(True)
         return "Error"
     vlayergeom.setName(self.dlg.txt_nomTaula.text())
            
     self.dlg.progressBar.setValue(80)
     textBox += u'Adaptant camps...\n'
     self.dlg.text_info.setText(textBox)
     self.MouText()
     
     '''Se renombra el campo de nombre y se añade un id'''
     vlayergeom.startEditing()
     fields = vlayergeom.fields()
     for x in range(len(fields)):
         if(campNom in fields[x].displayName()):
             vlayergeom.renameAttribute(x,'Nom')
             break;
     vlayergeom.addAttribute(QgsField('id', QVariant.Int))
     vlayergeom.commitChanges()
     
     '''Se autonumera el id'''
     features = vlayergeom.getFeatures()
     vlayergeom.startEditing()
     x=1
     for feature in features:
         vlayergeom.changeAttributeValue(feature.id(),self.getIndexOfField(vlayergeom,"id"),x)
         x=x+1
     vlayergeom.commitChanges()
        
     
     '''Se borran los campos no seleccionados''' 
     if not self.dlg.radio_ws.isChecked():
         llista_sel=[]    
         if (len(self.dlg.ListaCamps.selectedItems())>0):
             for item in self.dlg.ListaCamps.selectedItems():
                 llista_sel.append(item.text())
         
         vlayergeom.startEditing()
         for elem in listFields:
             if elem not in llista_sel:
                 vlayergeom.deleteAttribute(self.getIndexOfField(vlayergeom,elem))
         
         vlayergeom.commitChanges()
    
    
     '''Se representa en pantalla'''
     QgsProject.instance().addMapLayer(vlayergeom,False)
     root = QgsProject.instance().layerTreeRoot()
     myLayerNode=QgsLayerTreeLayer(vlayergeom)
     root.insertChildNode(0,myLayerNode)
     myLayerNode.setCustomProperty("showFeatureCount", True)
     
     
     if self.dlg.checkBox_save.isChecked():
         this_folder = os.path.dirname(os.path.abspath(__file__))   
         '''UPDATE'''
         file = open(this_folder+'/default_ws.txt') 
         cont=0
         strToWrite = ''
         for line in file:
             if (line.split('=',1)[1].replace('\n','')==self.dlg.txt_url.text()):
                 cont+=1
                 strToWrite+= line
             elif (cont==1):
                 cont+=1
                 strToWrite+= 'nom='+campNom+'\n'
             elif (cont==2):
                 cont+=1
                 if self.dlg.radio_geom.isChecked():
                     strToWrite+= 'geom='+campGeometria+'\n'
                 else:
                     strToWrite+='geom=\n'
             elif (cont==3):
                 cont+=1
                 if self.dlg.radio_latlng.isChecked():
                     strToWrite+= 'lat='+campLat+'\n'
                 else:
                     strToWrite+='lat=\n'
             elif (cont==4):
                 cont+=1
                 if self.dlg.radio_latlng.isChecked():
                     strToWrite+= 'lng='+campLng+'\n'
                 else:
                     strToWrite+='lng=\n'
             elif (cont==5):
                 cont+=1
                 strToWrite+= 'cod='+campCod+'\n'
             elif (cont==6):
                 cont+=1
                 strToWrite+= 'src='+campSrc+'\n'
             else:
                 strToWrite+=line
         file.close()
         file = open(this_folder+'/default_ws.txt', "w")
         file.write(strToWrite)
         file.close()    
         
         '''APEND'''
         if cont == 0:
             strToAppend = '\nurl='+self.dlg.txt_url.text()
             strToAppend += '\nnom='+campNom
             if self.dlg.radio_geom.isChecked():
                 strToAppend += '\ngeo='+campGeometria
             else:
                 strToAppend += '\ngeo='
             if self.dlg.radio_latlng.isChecked():
                 strToAppend += '\nlat='+campLat
                 strToAppend += '\nlng='+campLng
             else:
                 strToAppend += '\nlat='
                 strToAppend += '\nlng='
             strToAppend += '\ncod='+campCod
             strToAppend += '\nsrc='+campSrc
             file = open(this_folder+'/default_ws.txt', "a")
             file.write(strToAppend)
             file.close()
         
         
         self.file2Combo("default_ws.txt", self.dlg.combo_ws, 'Selecciona una opció')
         
     
     self.dlg.progressBar.setValue(100)
     textBox += u'\nProcés finalitzat.\n'
     self.dlg.text_info.setText(textBox)
     self.MouText()
     self.dlg.setEnabled(True)
Exemple #13
0
def open_file(dialog=None,
              osm_file=None,
              output_geom_types=None,
              white_list_column=None,
              layer_name="OsmFile",
              config_outputs=None,
              output_dir=None,
              prefix_file=None):
    """
    Open an osm file.

    Memory layer if no output directory is set, or Geojson in the output directory.
    """
    outputs = {}
    if output_dir:
        for layer in ['points', 'lines', 'multilinestrings', 'multipolygons']:
            if not prefix_file:
                prefix_file = layer_name

            outputs[layer] = join(output_dir,
                                  prefix_file + "_" + layer + ".geojson")

            if isfile(outputs[layer]):
                raise FileOutPutException(suffix='(' + outputs[layer] + ')')

    # Parsing the file
    osm_parser = OsmParser(osm_file=osm_file,
                           layers=output_geom_types,
                           white_list_column=white_list_column)

    osm_parser.signalText.connect(dialog.set_progress_text)
    osm_parser.signalPercentage.connect(dialog.set_progress_percentage)
    layers = osm_parser.parse()

    # Finishing the process with geojson or memory layer
    num_layers = 0

    for i, (layer, item) in enumerate(layers.items()):
        dialog.set_progress_percentage(i / len(layers) * 100)
        QApplication.processEvents()
        if item['featureCount'] and layer in output_geom_types:

            final_layer_name = layer_name
            # If configOutputs is not None (from My Queries)
            if config_outputs:
                if config_outputs[layer]['namelayer']:
                    final_layer_name = config_outputs[layer]['namelayer']

            if output_dir:
                dialog.set_progress_text(
                    tr("QuickOSM", u"From memory to GeoJSON: " + layer))
                # Transforming the vector file
                osm_geometries = {
                    'points': QgsWkbTypes.Point,
                    'lines': QgsWkbTypes.LineString,
                    'multilinestrings': QgsWkbTypes.MultiLineString,
                    'multipolygons': QgsWkbTypes.MultiPolygon
                }
                memory_layer = item['vector_layer']

                encoding = get_default_encoding()
                writer = QgsVectorFileWriter(outputs[layer], encoding,
                                             memory_layer.fields(),
                                             osm_geometries[layer],
                                             memory_layer.crs(), "GeoJSON")

                for f in memory_layer.getFeatures():
                    writer.addFeature(f)

                del writer

                # Loading the final vector file
                new_layer = QgsVectorLayer(outputs[layer], final_layer_name,
                                           "ogr")
            else:
                new_layer = item['vector_layer']
                new_layer.setName(final_layer_name)

            # Try to set styling if defined
            if config_outputs and config_outputs[layer]['style']:
                new_layer.loadNamedStyle(config_outputs[layer]['style'])
            else:
                # Loading default styles
                if layer == "multilinestrings" or layer == "lines":
                    if "colour" in item['tags']:
                        new_layer.loadNamedStyle(
                            join(dirname(dirname(abspath(__file__))), "styles",
                                 layer + "_colour.qml"))

            # Add action about OpenStreetMap
            actions = new_layer.actions()
            actions.addAction(
                QgsAction.OpenUrl, "OpenStreetMap Browser",
                'http://www.openstreetmap.org/browse/'
                '[% "osm_type" %]/[% "osm_id" %]', False)
            actions.addAction(
                QgsAction.GenericPython, 'JOSM',
                'from QuickOSM.CoreQuickOSM.Actions import Actions;'
                'Actions.run("josm","[% "full_id" %]")', False)
            actions.addAction(
                QgsAction.OpenUrl, "User default editor",
                'http://www.openstreetmap.org/edit?'
                '[% "osm_type" %]=[% "osm_id" %]', False)

            for link in ['url', 'website', 'wikipedia', 'ref:UAI']:
                if link in item['tags']:
                    link = link.replace(":", "_")
                    actions.addAction(
                        QgsAction.GenericPython, link,
                        'from QuickOSM.core.actions import Actions;'
                        'Actions.run("' + link + '","[% "' + link + '" %]")',
                        False)

            if 'network' in item['tags'] and 'ref' in item['tags']:
                actions.addAction(
                    QgsAction.GenericPython, "Sketchline",
                    'from QuickOSM.core.actions import Actions;'
                    'Actions.run_sketch_line("[% "network" %]","[% "ref" %]")',
                    False)

            QgsProject.instance().addMapLayer(new_layer)
            num_layers += 1

    return num_layers
class MapBiomasAlertRequest(QObject):
    killProcess = pyqtSignal()
    currentProcess = pyqtSignal(str)
    finishedProcess = pyqtSignal()
    showExtentProcess = pyqtSignal()
    message = pyqtSignal(Qgis.MessageLevel, str)

    def __init__(self, canvas):
        super().__init__()
        self.nameCatalog = 'mapbiomas_alert_valid'
        self.apiMB = API_MapbiomasAlert()
        self.killProcess.connect(self.apiMB.kill)
        self.canvas = canvas
        self.project = QgsProject.instance()
        self.layerTreeRoot = self.project.layerTreeRoot()
        self.mapCanvasGeom = MapCanvasGeometry()
        self.pluginName = 'MapBiomas'
        self.crsCatalog = QgsCoordinateReferenceSystem('EPSG:4674')
        self.styleFile = 'mapbiomas_alert.qml'
        self.alert = None
        self.alert_id = None
        self.response = None
        self.isRunning = None

    def __del__(self):
        self.killProcess.disconnect(self.apiMB.kill)

    def _createCatalog(self):
        key_value = lambda k: "field={key}:{value}".format(
            key=k, value=self.apiMB.fields[k]['definition'])
        l_fields = [key_value(k) for k in self.apiMB.fields.keys()]
        l_fields.insert(
            0, "Multipolygon?crs={}".format(self.crsCatalog.authid().lower()))
        l_fields.append('index=yes')
        uri = '&'.join(l_fields)
        self.alert = QgsVectorLayer(uri, self.nameCatalog, 'memory')
        self.alert.loadNamedStyle(
            os.path.join(os.path.dirname(__file__), self.styleFile))
        self.alert_id = self.alert.id()

    def _responseFinished(self, response):
        self.response = response

    def actionsForm(self, nameAction, feature_id=None):
        """
        Run action defined in layer, provide by style file

        :param nameAction: Name of action
        :params feature_id: Feature ID
        """

        # Actions functions
        def flash(feature_id):
            geom = self.alert.getFeature(feature_id).geometry()
            self.mapCanvasGeom.flash([geom], self.alert)
            return {'isOk': True}

        def zoom(feature_id):
            geom = self.alert.getFeature(feature_id).geometry()
            self.mapCanvasGeom.zoom([geom], self.alert)
            return {'isOk': True}

        def report(feature_id):
            feat = self.alert.getFeature(feature_id)
            alerta_id = feat['alerta_id']
            cars_ids = feat['cars']
            if len(cars_ids) == 0:
                url = "{}/{}".format(API_MapbiomasAlert.urlReport, alerta_id)
                QDesktopServices.openUrl(QUrl(url))
            else:
                for car_id in cars_ids.split('\n'):
                    url = "{}/{}/car/{}".format(API_MapbiomasAlert.urlReport,
                                                alerta_id, car_id)
                    QDesktopServices.openUrl(QUrl(url))
            return {'isOk': True}

        actionsFunc = {'flash': flash, 'zoom': zoom, 'report': report}
        if not nameAction in actionsFunc.keys():
            return {
                'isOk': False,
                'message': "Missing action '{}'".format(nameAction)
            }
        return actionsFunc[nameAction](feature_id)

    def requestPopulateCatalog(self):
        def getWktExtent():
            crsCanvas = self.canvas.mapSettings().destinationCrs()
            ct = QgsCoordinateTransform(crsCanvas, self.crsCatalog,
                                        self.project)
            extent = self.canvas.extent(
            ) if crsCanvas == self.crsCatalog else ct.transform(
                self.canvas.extent())
            return extent.asWktPolygon()

        def populate(features):
            provider = self.alert.dataProvider()
            for item in features:
                atts = [item[k] for k in self.apiMB.fields]
                feat = QgsFeature()
                feat.setAttributes(atts)
                geom = item['geometry']
                if not geom is None:
                    feat.setGeometry(geom)
                provider.addFeature(feat)
                del item

        def finished(response):
            self.response = response
            if not response['isOk']:
                self.message.emit(Qgis.Critical, response['message'])
                return
            if len(self.response['features']) == 0:
                self.message.emit(Qgis.Warning,
                                  "Inside this view don't have alerts")
                del response['features']
                return
            populate(response['features'])
            del response['features']
            self.message.emit(Qgis.Success, 'Finished OK')

        def closeTableAttribute():
            layer_id = self.alert_id
            widgets = QApplication.instance().allWidgets()
            for tb in filter(
                    lambda w: isinstance(w, QDialog) and layer_id in w.
                    objectName(), widgets):
                tb.close()

        self.currentProcess.emit('Populate Catalog')
        existsCatalog = not self.alert is None and not self.project.mapLayer(
            self.alert_id) is None
        if not existsCatalog:
            self._createCatalog()
        else:
            name = "Receiving... - {}".format(self.nameCatalog)
            self.alert.setName(name)
            self.alert.dataProvider().truncate()  # Delete all features
            closeTableAttribute()
        self.response = None
        self.calculateMetadata = True
        self.message.emit(Qgis.Info, 'Request features...')
        url = self.apiMB.getUrlAlerts(getWktExtent())
        self.apiMB.getAlerts(url, finished)
        self.calculateMetadata = False
        if self.response['isOk']:
            if not existsCatalog:
                self.project.addMapLayer(self.alert, addToLegend=False)
                root = self.project.layerTreeRoot()
                root.insertLayer(0, self.alert).setCustomProperty(
                    "showFeatureCount", True)
            else:
                self.alert.setName(self.nameCatalog)
                self.alert.triggerRepaint()
        else:
            root = self.project.layerTreeRoot()
            if not root.findLayer(self.alert) is None:
                root.removeLayer(self.alert)

    def searchAlert(self):
        self.currentProcess.emit('Check server')
        self.apiMB.isHostLive(self._responseFinished)
        if not self.response['isOk']:
            self.message.emit(Qgis.Critical, 'MapBioma server is out')
            self.finishedProcess.emit()
            return

        self.currentProcess.emit('Search alerts')
        if self.canvas.layerCount() == 0:
            msg = 'Need layer(s) in map'
            self.message.emit(Qgis.Warning, msg)
        else:
            self.apiMB.access.isKill = False
            self.showExtentProcess.emit()
            self.requestPopulateCatalog()
        self.finishedProcess.emit()

    @pyqtSlot()
    def onCancel(self):
        self.killProcess.emit()
Exemple #15
0
def open_file(
        dialog=None,
        osm_file=None,
        output_geom_types=None,
        white_list_column=None,
        layer_name="OsmFile",
        config_outputs=None,
        output_dir=None,
        final_query=None,
        prefix_file=None):
    """
    Open an osm file.

    Memory layer if no output directory is set, or Geojson in the output
    directory.

    :param final_query: The query where the file comes from. Might be empty if
    it's a local OSM file.
    :type final_query: basestring
    """
    outputs = {}
    if output_dir:
        for layer in ['points', 'lines', 'multilinestrings', 'multipolygons']:
            if not prefix_file:
                prefix_file = layer_name

            outputs[layer] = join(
                output_dir, prefix_file + "_" + layer + ".geojson")

            if isfile(outputs[layer]):
                raise FileOutPutException(suffix='(' + outputs[layer] + ')')

    # Legacy, waiting to remove the OsmParser for QGIS >= 3.6
    # Change in osm_file_dialog.py L131 too
    output_geom_legacy = [l.value.lower() for l in output_geom_types]
    if not white_list_column:
        white_list_column = {}
    white_list_legacy = (
        {l.value.lower(): csv for l, csv in white_list_column.items()}
    )

    LOGGER.info('The OSM file is: {}'.format(osm_file))

    # Parsing the file
    osm_parser = OsmParser(
        osm_file=osm_file,
        layers=output_geom_legacy,
        white_list_column=white_list_legacy)

    osm_parser.signalText.connect(dialog.set_progress_text)
    osm_parser.signalPercentage.connect(dialog.set_progress_percentage)

    start_time = time.time()
    layers = osm_parser.parse()
    elapsed_time = time.time() - start_time
    parser_time = time.strftime("%Hh %Mm %Ss", time.gmtime(elapsed_time))
    LOGGER.info('The OSM parser took: {}'.format(parser_time))

    # Finishing the process with geojson or memory layer
    num_layers = 0

    for i, (layer, item) in enumerate(layers.items()):
        dialog.set_progress_percentage(i / len(layers) * 100)
        QApplication.processEvents()
        if item['featureCount'] and (
                LayerType(layer.capitalize()) in output_geom_types):

            final_layer_name = layer_name
            # If configOutputs is not None (from My Queries)
            if config_outputs:
                if config_outputs[layer]['namelayer']:
                    final_layer_name = config_outputs[layer]['namelayer']

            if output_dir:
                dialog.set_progress_text(
                    tr('From memory layer to GeoJSON: ' + layer))
                # Transforming the vector file
                osm_geometries = {
                    'points': QgsWkbTypes.Point,
                    'lines': QgsWkbTypes.LineString,
                    'multilinestrings': QgsWkbTypes.MultiLineString,
                    'multipolygons': QgsWkbTypes.MultiPolygon}
                memory_layer = item['vector_layer']

                encoding = get_default_encoding()
                writer = QgsVectorFileWriter(
                    outputs[layer],
                    encoding,
                    memory_layer.fields(),
                    osm_geometries[layer],
                    memory_layer.crs(),
                    "GeoJSON")

                for f in memory_layer.getFeatures():
                    writer.addFeature(f)

                del writer

                # Loading the final vector file
                new_layer = QgsVectorLayer(
                    outputs[layer], final_layer_name, "ogr")
            else:
                new_layer = item['vector_layer']
                new_layer.setName(final_layer_name)

            # Try to set styling if defined
            if config_outputs and config_outputs[layer]['style']:
                new_layer.loadNamedStyle(config_outputs[layer]['style'])
            else:
                # Loading default styles
                if layer == "multilinestrings" or layer == "lines":
                    if "colour" in item['tags']:
                        new_layer.loadNamedStyle(
                            join(dirname(dirname(abspath(__file__))),
                                 "styles",
                                 layer + "_colour.qml"))

            # Add action about OpenStreetMap
            add_actions(new_layer, item['tags'])

            if final_query:
                QgsExpressionContextUtils.setLayerVariable(
                    new_layer, 'quickosm_query', final_query)

            QgsProject.instance().addMapLayer(new_layer)
            num_layers += 1

    return num_layers
Exemple #16
0
    def get_layer_description_from_browser(self, category):
        """Obtain the description of the browser layer selected by user.

        :param category: The category of the layer to get the description.
        :type category: string

        :returns: Tuple of boolean and string. Boolean is true if layer is
            validated as compatible for current role (impact function and
            category) and false otherwise. String contains a description
            of the selected layer or an error message.
        :rtype: tuple
        """

        if category == 'hazard':
            browser = self.tvBrowserHazard
        elif category == 'exposure':
            browser = self.tvBrowserExposure
        elif category == 'aggregation':
            browser = self.tvBrowserAggregation
        else:
            raise InaSAFEError

        index = browser.selectionModel().currentIndex()
        if not index:
            return False, ''

        # Map the proxy model index to the source model index
        index = browser.model().mapToSource(index)
        item = browser.model().sourceModel().dataItem(index)
        if not item:
            return False, ''

        item_class_name = item.metaObject().className()
        # if not itemClassName.endswith('LayerItem'):
        if not item.type() == QgsDataItem.Layer:
            if item_class_name == 'QgsPGRootItem' and not item.children():
                return False, create_postGIS_connection_first
            else:
                return False, ''

        if item_class_name not in [
                'QgsOgrLayerItem',
                'QgsGdalLayerItem',
                'QgsPGLayerItem',
                'QgsLayerItem',
        ]:
            return False, ''

        path = item.path()

        if item_class_name in [
                'QgsOgrLayerItem', 'QgsGdalLayerItem', 'QgsLayerItem'
        ] and not os.path.exists(path):
            return False, ''

        # try to create the layer
        if item_class_name == 'QgsOgrLayerItem':
            layer = QgsVectorLayer(path, '', 'ogr')
        elif item_class_name == 'QgsPGLayerItem':
            uri = self.postgis_path_to_uri(path)
            if uri:
                layer = QgsVectorLayer(uri.uri(), uri.table(), 'postgres')
            else:
                layer = None
        else:
            layer = QgsRasterLayer(path, '', 'gdal')

        if not layer or not layer.isValid():
            return False, self.tr('Not a valid layer.')

        try:
            keywords = self.keyword_io.read_keywords(layer)
            if 'layer_purpose' not in keywords:
                keywords = None
        except (HashNotFoundError, OperationalError, NoKeywordsFoundError,
                KeywordNotFoundError, InvalidParameterError,
                UnsupportedProviderError, MissingMetadata):
            keywords = None

        # set the layer name for further use in the step_fc_summary
        if keywords:
            if qgis_version() >= 21800:
                layer.setName(keywords.get('title'))
            else:
                layer.setLayerName(keywords.get('title'))

        if not self.parent.is_layer_compatible(layer, category, keywords):
            label_text = '%s<br/>%s' % (
                self.tr('This layer\'s keywords or type are not suitable:'),
                self.unsuitable_layer_description_html(layer, category,
                                                       keywords))
            return False, label_text

        # set the current layer (e.g. for the keyword creation sub-thread
        #                          or for adding the layer to mapCanvas)
        self.parent.layer = layer
        if category == 'hazard':
            self.parent.hazard_layer = layer
        elif category == 'exposure':
            self.parent.exposure_layer = layer
        else:
            self.parent.aggregation_layer = layer

        # Check if the layer is keywordless
        if keywords and 'keyword_version' in keywords:
            kw_ver = str(keywords['keyword_version'])
            self.parent.is_selected_layer_keywordless = (
                not is_keyword_version_supported(kw_ver))
        else:
            self.parent.is_selected_layer_keywordless = True

        desc = layer_description_html(layer, keywords)
        return True, desc
Exemple #17
0
def add_design_to_map(qris_project, item, node):
    """adds designs to the map"""
    # Establish paths to layers
    design_id = item.data(item_code['feature_id'])
    subset_string = ("design_id = " + str(design_id))
    design_name = item.text()
    geopackage_path = qris_project.project_designs.geopackage_path(
        qris_project.project_path)
    designs_layer = QgsVectorLayer(geopackage_path + "|layername=designs",
                                   "Designs", "ogr")
    structure_types_layer = QgsVectorLayer(
        geopackage_path + "|layername=structure_types", "Structure Types",
        "ogr")
    phases_layer = QgsVectorLayer(geopackage_path + "|layername=phases",
                                  "Implementation Phases", "ogr")
    zoi_layer = QgsVectorLayer(geopackage_path + "|layername=zoi", "ZOI",
                               "ogr")
    zoi_types_layer = QgsVectorLayer(geopackage_path + "|layername=zoi_types",
                                     "ZOI", "ogr")
    complexes_layer = QgsVectorLayer(geopackage_path + "|layername=complexes",
                                     "Complexes", "ogr")
    structure_points_layer = QgsVectorLayer(
        geopackage_path + "|layername=structure_points", "Structures", "ogr")
    structure_lines_layer = QgsVectorLayer(
        geopackage_path + "|layername=structure_lines", "Structures", "ogr")

    # Get the structure geometry type
    # Could also do this with SQL
    design_iterator = designs_layer.getFeatures(
        QgsFeatureRequest().setFilterFid(design_id))
    design_feature = next(design_iterator)
    structure_geometry = design_feature['structure_geometry']

    def add_design_table(display_name, table_name, qml_name, read_only,
                         group_node):
        """A handy way to add design layers and tables to the map"""
        if not any([c.name() == display_name for c in group_node.children()]):
            layer = QgsProject.instance().addMapLayer(
                QgsVectorLayer(geopackage_path + "|layername=" + table_name,
                               display_name, "ogr"), False)
            if qml_name:
                layer_qml = os.path.join(symbology_path, 'symbology', qml_name)
                layer.loadNamedStyle(layer_qml)
            if read_only:
                layer.setReadOnly()
            group_node.addLayer(layer)

    # Summary tables (views)
    if any([c.name() == "Low-Tech Tables" for c in node.children()]):
        # if is there set it to the design node
        table_node = next(n for n in node.children()
                          if n.name() == "Low-Tech Tables")
    else:
        # if not add the node as a group
        table_node = node.addGroup("Low-Tech Tables")
        table_node.setExpanded(False)

    # Summary tables (views)
    if any([c.name() == "Summary Tables" for c in table_node.children()]):
        # if is there set it to the design node
        summary_node = next(n for n in table_node.children()
                            if n.name() == "Summary Tables")
    else:
        # if not add the node as a group
        summary_node = table_node.addGroup("Summary Tables")
        summary_node.setExpanded(False)

    add_design_table('Structure Totals - Points',
                     'qry_total_structures_points', None, True, summary_node)
    add_design_table('Structure Totals - Lines', 'qry_total_structures_lines',
                     None, True, summary_node)
    add_design_table('Structure Summary - Points',
                     'qry_structure_summary_points', None, True, summary_node)
    add_design_table('Structure Summary - Lines',
                     'qry_structure_summary_lines', None, True, summary_node)
    add_design_table('Complexes Summary - Points',
                     'qry_complexes_by_type_points', None, True, summary_node)
    add_design_table('Complexes Summary - Lines',
                     'qry_complexes_by_type_lines', None, True, summary_node)
    add_design_table('ZOI Summary', 'qry_zoi_summary', None, True,
                     summary_node)

    # Lookup Tables
    if any([c.name() == "Lookup Tables" for c in table_node.children()]):
        # if is there set it to the design node
        lookup_node = next(n for n in table_node.children()
                           if n.name() == "Lookup Tables")
    else:
        # if not add the node as a group
        lookup_node = table_node.addGroup("Lookup Tables")
        lookup_node.setExpanded(False)

    add_design_table('Design Status', 'lkp_design_status',
                     'lkp_design_status.qml', True, lookup_node)
    add_design_table('Phase Action', 'lkp_phase_action',
                     'lkp_phase_action.qml', True, lookup_node)
    add_design_table('ZOI Influence', 'lkp_zoi_influence',
                     'lkp_zoi_influence.qml', True, lookup_node)
    add_design_table('ZOI Stage', 'lkp_zoi_stage', 'lkp_zoi_stage.qml', True,
                     lookup_node)
    add_design_table('Structure Mimics', 'lkp_structure_mimics',
                     'lkp_structure_mimics.qml', True, lookup_node)

    # Add Design Tables
    if any([c.name() == "Design Tables" for c in table_node.children()]):
        # if is there set it to the design node
        design_tables_node = next(n for n in table_node.children()
                                  if n.name() == "Design Tables")
    else:
        # if not add the node as a group
        design_tables_node = table_node.addGroup("Design Tables")
        design_tables_node.setExpanded(False)

    # Check if the designs table has been added and if not add it.
    add_design_table('Designs', 'designs', 'designs.qml', False,
                     design_tables_node)
    add_design_table('Structure Types', 'structure_types',
                     'structure_types.qml', False, design_tables_node)
    add_design_table('ZOI Types', 'zoi_types', 'zoi_types.qml', False,
                     design_tables_node)
    add_design_table('Phases', 'phases', 'phases.qml', False,
                     design_tables_node)

    # Check if the design node is already added
    design_group_name = str(design_id) + "-" + item.text()
    if any([c.name() == design_group_name for c in node.children()]):
        # if is there set it to the design node
        design_node = next(n for n in node.children()
                           if n.name() == design_group_name)
    else:
        # if not add the node as a group
        design_node = node.addGroup(design_group_name)

    # Add structures
    structure_layer_name = str(design_id) + "-Structures"
    if structure_geometry == 'Point':
        # Start setting custom symbology
        # TODO Refactor into a functio
        unique_values = []
        for feature in structure_types_layer.getFeatures():
            values = (feature["fid"], feature["name"])
            unique_values.append(values)
        categories = []
        for value in unique_values:
            layer_style = {}
            layer_style["color"] = '%d, %d, %d' % (randrange(
                0, 256), randrange(0, 256), randrange(0, 256))
            layer_style['size'] = '3'
            layer_style['outline_color'] = 'black'
            symbol_layer = QgsMarkerSymbol.createSimple(layer_style)
            category = QgsRendererCategory(str(value[0]), symbol_layer,
                                           value[1])
            categories.append(category)
        renderer = QgsCategorizedSymbolRenderer('structure_type_id',
                                                categories)

        if not any(
            [c.name() == structure_layer_name
             for c in design_node.children()]):
            # Adding the type suffix as I could see adding qml that symbolizes on other attributes
            structure_points_qml = os.path.join(symbology_path, 'symbology',
                                                'structure_points.qml')
            structure_points_layer.loadNamedStyle(structure_points_qml)
            QgsExpressionContextUtils.setLayerVariable(structure_points_layer,
                                                       'parent_id', design_id)
            structure_points_layer.setSubsetString(subset_string)
            QgsProject.instance().addMapLayer(structure_points_layer, False)
            structure_points_layer.setName(structure_layer_name)
            design_node.addLayer(structure_points_layer)
            layer_node = design_node.findLayer(structure_points_layer.id())
            layer_node.setExpanded(False)
        else:
            structure_points_layer = QgsProject.instance().mapLayersByName(
                structure_layer_name)[0]
        if renderer is not None:
            structure_points_layer.setRenderer(renderer)
            structure_points_layer.triggerRepaint()

    else:
        # Add line structures
        # Start setting custom symbology
        # TODO Refactor into a function
        unique_values = []
        for feature in structure_types_layer.getFeatures():
            values = (feature["fid"], feature["name"])
            unique_values.append(values)

        categories = []
        for value in unique_values:
            layer_style = {}
            layer_style["color"] = '%d, %d, %d' % (randrange(
                0, 256), randrange(0, 256), randrange(0, 256))
            layer_style['width'] = '1'
            layer_style['capstyle'] = 'round'
            symbol_layer = QgsLineSymbol.createSimple(layer_style)
            category = QgsRendererCategory(str(value[0]), symbol_layer,
                                           value[1])
            categories.append(category)
        renderer = QgsCategorizedSymbolRenderer('structure_type_id',
                                                categories)
        # end custom symbology

        if not any(
            [c.name() == structure_layer_name
             for c in design_node.children()]):
            structures_lines_qml = os.path.join(symbology_path, 'symbology',
                                                'structure_lines.qml')
            structure_lines_layer.loadNamedStyle(structures_lines_qml)
            QgsExpressionContextUtils.setLayerVariable(structure_lines_layer,
                                                       'parent_id', design_id)
            structure_lines_layer.setSubsetString(subset_string)
            QgsProject.instance().addMapLayer(structure_lines_layer, False)
            structure_lines_layer.setName(structure_layer_name)
            design_node.addLayer(structure_lines_layer)
            layer_node = design_node.findLayer(structure_lines_layer.id())
            layer_node.setExpanded(False)
        else:
            structure_lines_layer = QgsProject.instance().mapLayersByName(
                structure_layer_name)[0]
        if renderer is not None:
            structure_lines_layer.setRenderer(renderer)
            structure_lines_layer.triggerRepaint()

    # Add zoi
    zoi_layer_name = str(design_id) + "-ZOI"
    # TODO Refactor into a function
    # TODO Refactor into sql query
    unique_values = []
    for feature in zoi_types_layer.getFeatures():
        values = (feature["fid"], feature["name"])
        unique_values.append(values)
    categories = []
    for value in unique_values:
        layer_style = {}
        alpha = 60
        layer_style["color"] = "{}, {}, {}, {}".format(randrange(0, 256),
                                                       randrange(0, 256),
                                                       randrange(0, 256),
                                                       alpha)
        layer_style["outline_width"] = '0.50'
        layer_style["outline_style"] = 'dash'
        symbol_layer = QgsFillSymbol.createSimple(layer_style)
        category = QgsRendererCategory(str(value[0]), symbol_layer, value[1])
        categories.append(category)
    renderer = QgsCategorizedSymbolRenderer('influence_type_id', categories)
    # End custom symbology

    # check for the zoi layer, and if it is not there symbolize and add it
    if not any([c.name() == zoi_layer_name for c in design_node.children()]):
        zoi_qml = os.path.join(symbology_path, 'symbology', 'zoi.qml')
        zoi_layer.loadNamedStyle(zoi_qml)
        QgsExpressionContextUtils.setLayerVariable(zoi_layer, 'parent_id',
                                                   design_id)
        zoi_layer.setSubsetString(subset_string)
        QgsProject.instance().addMapLayer(zoi_layer, False)
        zoi_layer.setName(zoi_layer_name)
        design_node.addLayer(zoi_layer)
        layer_node = design_node.findLayer(zoi_layer.id())
        layer_node.setExpanded(False)
    else:
        zoi_layer = QgsProject.instance().mapLayersByName(zoi_layer_name)[0]
    if renderer is not None:
        zoi_layer.setRenderer(renderer)
    zoi_layer.triggerRepaint()

    # Add complexes
    complex_layer_name = str(design_id) + "-Complexes"
    if not any(
        [c.name() == complex_layer_name for c in design_node.children()]):
        complex_qml = os.path.join(symbology_path, 'symbology',
                                   'complexes.qml')
        complexes_layer.loadNamedStyle(complex_qml)
        QgsExpressionContextUtils.setLayerVariable(complexes_layer,
                                                   'parent_id', design_id)
        complexes_layer.setSubsetString(subset_string)
        QgsProject.instance().addMapLayer(complexes_layer, False)
        complexes_layer.setName(complex_layer_name)
        design_node.addLayer(complexes_layer)