def loadLayerTable(self, carhabLayer, tableName):

        # Retrieve layer from provider.
        uri = QgsDataSourceURI()
        uri.setDatabase(carhabLayer.dbPath)

        schema = ''
        geom_column = 'the_geom'
        uri.setDataSource(schema, tableName, geom_column)

        display_name = carhabLayer.getName()+'_'+tableName

        layer = QgsVectorLayer(uri.uri(), display_name, 'spatialite')
        crsType = QgsCoordinateReferenceSystem.EpsgCrsId
        crsVal = 2154
        crs = QgsCoordinateReferenceSystem(crsVal, crsType)
        layer.setCrs(crs)

        # "Bind" layer to carhab layer.
        if self.getCarhabLayerByDbPath(carhabLayer.dbPath):
            layer.setCustomProperty('carhabLayer', carhabLayer.id)

        # Add layer to map (False to add to group)
        QgsMapLayerRegistry.instance().addMapLayer(layer, False)

        iface.mapCanvas().setExtent(layer.extent())

        return layer
    def _add_vector_layer_to_qgis(self, json_src, layer_name, zoom_level, layer_target_group, 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_with_zoom, "ogr")

        if merge_features and geo_type in [GeoTypes.LINE_STRING, GeoTypes.POLYGON]:
            layer = FeatureMerger().merge_features(layer)
            layer.setName(layer_name)

        QgsMapLayerRegistry.instance().addMapLayer(layer, False)
        layer_target_group.addLayer(layer)
        layer.setCustomProperty("vector_tile_source", self.source.source())

        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/")

        return layer
    def _create_layer(self, type, srid, attributes, title, tag):
        """
        Creates an empty spatialite layer
        :param type: 'Point', 'LineString', 'Polygon', etc.
        :param srid: CRS ID of the layer
        :param attributes: list of (attribute_name, attribute_type, attribute_typename)
        :param title: title of the layer
        """
        driver = ogr.GetDriverByName('GPKG')
        fn = "{}_{}.gpkg".format(self.output_local_file, tag)
        ds = driver.CreateDataSource(fn)
        layer = ds.CreateLayer("meta", geom_type=ogr.wkbNone)
        layer.CreateField(ogr.FieldDefn('key', ogr.OFTString))
        layer.CreateField(ogr.FieldDefn('value', ogr.OFTString))

        if srid:
            wkbType = {
                'point': ogr.wkbPoint25D,
                'multipoint': ogr.wkbMultiPoint25D,
                'linestring': ogr.wkbLineString25D,
                'multilinestring': ogr.wkbMultiLineString25D,
                'polygon': ogr.wkbPolygon25D,
                'multipolygon': ogr.wkbMultiPolygon25D,
                'compoundcurve': ogr.wkbCompoundCurveZ,
                'curvepolygon': ogr.wkbCurvePolygonZ,
                'multicurve': ogr.wkbMultiCurveZ,
                'multisurface': ogr.wkbMultiSurfaceZ
            }[type]
            srs = osr.SpatialReference()
            srs.ImportFromEPSGA(int(srid))
        else:
            wkbType = ogr.wkbNone
            srs = None
        layer = ds.CreateLayer("data", srs, wkbType, ['FID=id'])
        layer.CreateField(ogr.FieldDefn('id', ogr.OFTInteger64))
        layer.CreateField(ogr.FieldDefn('fid', ogr.OFTString))
        layer.CreateField(ogr.FieldDefn('_xml_', ogr.OFTString))

        att_type_map = {
            QVariant.String: ogr.OFTString,
            QVariant.Int: ogr.OFTInteger,
            QVariant.Double: ogr.OFTReal,
            QVariant.DateTime: ogr.OFTDateTime
        }
        for aname, atype in attributes:
            layer.CreateField(ogr.FieldDefn(aname, att_type_map[atype]))

        # update fields
        layer.ResetReading()

        del layer
        del ds

        qgs_layer = QgsVectorLayer("{}|layername=data".format(fn), title,
                                   "ogr")
        qgs_layer.setCustomProperty("tag", tag)
        return qgs_layer
 def default_layer_factory(cls, name = "Wyniki wyszukiwania ULDK",
         epsg = 2180, custom_properties = {"ULDK":"plots_layer"},
         additional_fields = [], base_fields = PLOTS_LAYER_DEFAULT_FIELDS ):
     fields = base_fields + additional_fields
     layer = QgsVectorLayer("Polygon?crs=EPSG:{}".format(epsg), name, "memory")
     layer.startEditing()
     for prop, value in custom_properties.items():
         layer.setCustomProperty(prop, value)
     layer.dataProvider().addAttributes(fields)
     layer.commitChanges()
     return layer
    def _create_layer(self, type, srid, attributes, title, tag):
        """
        Creates an empty spatialite layer
        :param type: 'Point', 'LineString', 'Polygon', etc.
        :param srid: CRS ID of the layer
        :param attributes: list of (attribute_name, attribute_type, attribute_typename)
        :param title: title of the layer
        """
        driver = ogr.GetDriverByName('GPKG')
        fn = "{}_{}.gpkg".format(self.output_local_file, tag)
        ds = driver.CreateDataSource(fn)
        layer = ds.CreateLayer("meta", geom_type = ogr.wkbNone)
        layer.CreateField(ogr.FieldDefn('key', ogr.OFTString))
        layer.CreateField(ogr.FieldDefn('value', ogr.OFTString))

        if srid:
            wkbType = { 'point': ogr.wkbPoint25D,
                        'multipoint': ogr.wkbMultiPoint25D,
                        'linestring': ogr.wkbLineString25D,
                        'multilinestring': ogr.wkbMultiLineString25D,
                        'polygon': ogr.wkbPolygon25D,
                        'multipolygon': ogr.wkbMultiPolygon25D,
                        'compoundcurve': ogr.wkbCompoundCurveZ,
                        'curvepolygon': ogr.wkbCurvePolygonZ,
                        'multicurve': ogr.wkbMultiCurveZ,
                        'multisurface': ogr.wkbMultiSurfaceZ
            }[type]
            srs = osr.SpatialReference()
            srs.ImportFromEPSGA(int(srid))
        else:
            wkbType = ogr.wkbNone
            srs = None
        layer = ds.CreateLayer("data", srs, wkbType, ['FID=id'])
        layer.CreateField(ogr.FieldDefn('id', ogr.OFTInteger64))
        layer.CreateField(ogr.FieldDefn('fid', ogr.OFTString))
        layer.CreateField(ogr.FieldDefn('_xml_', ogr.OFTString))

        att_type_map = {QVariant.String : ogr.OFTString,
                        QVariant.Int : ogr.OFTInteger,
                        QVariant.Double: ogr.OFTReal,
                        QVariant.DateTime: ogr.OFTDateTime}
        for aname, atype in attributes:
            layer.CreateField(ogr.FieldDefn(aname, att_type_map[atype]))

        # update fields
        layer.ResetReading()

        del layer
        del ds

        qgs_layer = QgsVectorLayer("{}|layername=data".format(fn), title, "ogr")
        qgs_layer.setCustomProperty("tag", tag)
        return qgs_layer
Exemple #6
0
 def _propagate_meta_vlayer(self,
                            vlayer: QgsVectorLayer,
                            style_category=None):
     if style_category not in (vlayer.CustomProperties,
                               vlayer.AllStyleCategories, None):
         return
     qnode = self.qgroups.get("main")
     if not qnode:
         return
     for k in qnode.customProperties():
         v = get_customProperty_str(qnode, k)
         vlayer.setCustomProperty(k, v)
Exemple #7
0
 def build_custom_site_ids_layer(self, lons, lats, custom_site_ids):
     layer_name = 'custom_site_ids_%s' % self.calc_id
     custom_site_id_layer = QgsVectorLayer(
         "%s?crs=epsg:4326" % 'point', layer_name, "memory")
     add_attribute('custom_site_id', 'I', custom_site_id_layer)
     custom_site_id_layer = self.read_custom_site_ids_into_layer(
         custom_site_id_layer, lons, lats, custom_site_ids)
     custom_site_id_layer.setCustomProperty(
         'output_type', '%s-%s' % (self.output_type, 'custom_site_ids'))
     if self.engine_version is not None:
         custom_site_id_layer.setCustomProperty(
             'engine_version', self.engine_version)
     irmt_version = get_irmt_version()
     custom_site_id_layer.setCustomProperty('irmt_version', irmt_version)
     custom_site_id_layer.setCustomProperty('calc_id', self.calc_id)
     if self.mode != 'testing':
         # NOTE: the following commented line would cause (unexpectedly)
         #       "QGIS died on signal 11" and double creation of some
         #       layers during integration tests
         QgsProject.instance().addMapLayer(custom_site_id_layer, False)
     tree_node = QgsProject.instance().layerTreeRoot()
     tree_node.insertLayer(0, custom_site_id_layer)
     # self.iface.setActiveLayer(custom_site_id_layer)
     log_msg('Layer %s was created successfully' % layer_name, level='S',
             message_bar=self.iface.messageBar(),
             print_to_stdout=True)
    def createLayer(self):
        '''Create a layer with the required attributes and add the layer to the canvas.
        The database is taken from database combobox. The database needs to be registered.
        '''
        sql = u'create table ' + self.quotedIdentifier(self.leLayerName.text()) + '('
        sql += u'pkuid integer primary key autoincrement,'
        sql += u'name text,description text,class text, timestamp text)'

        sqlGeom = u'select AddGeometryColumn(%s,%s,%d,%s,2)' % (self.quotedValue(self.leLayerName.text()),
                                                                self.quotedValue('Geometry'),
                                                                4326,
                                                                self.quotedValue('POINT'))

        sqlIndex = u'select CreateSpatialIndex(%s,%s)' % (self.quotedValue(self.leLayerName.text()),
                                                         self.quotedValue('Geometry'))

        try:
            db = sqlite.connect(self.mDatabaseComboBox.currentText())
            cur = db.cursor()
            cur.execute(sql)
            cur.execute(sqlGeom)
            cur.execute(sqlIndex)
            db.commit()
            db.close()
        except:
            self.iface.messageBar().pushMessage(self.tr("SpatiaLite Database"), self.tr("Could not create a new layer!"),
                                 level=QgsMessageBar.CRITICAL, duration=5)
            return

        uri = QgsDataSourceURI()
        uri.setDatabase(self.mDatabaseComboBox.currentText())
        schema = ''
        table = self.leLayerName.text()
        geom_column = 'Geometry'
        uri.setDataSource(schema, table, geom_column)
        display_name = self.leLayerName.text()
        layer = QgsVectorLayer(uri.uri(), display_name, 'spatialite')

        if layer.isValid():
            for k, v in self.DEFAULT_PROPERTIES.iteritems():
                layer.setCustomProperty(k, v)
            QgsMapLayerRegistry.instance().addMapLayer(layer)
Exemple #9
0
def _qgis_layer(
    uri,
    schema_name,
    layer_name,
    geometry_column,
    provider,
    qgis_layer_name,
    layer_xpath,
    layer_pkid,
):
    if geometry_column is not None:
        g_column = "({})".format(geometry_column)
    else:
        g_column = ""
    if provider == "SQLite":
        # use OGR for spatialite loading
        couche = QgsVectorLayer(
            "{}|layername={}{}".format(uri, layer_name, g_column),
            qgis_layer_name,
            "ogr",
        )
        couche.setProviderEncoding("UTF-8")
    else:
        if schema_name is not None:
            s_table = '"{}"."{}"'.format(schema_name, layer_name)
        else:
            s_table = '"{}"'.format(layer_name)
        # remove "PG:" in front of the uri
        uri = uri[3:]
        couche = QgsVectorLayer(
            "{} table={} {} sql=".format(uri, s_table, g_column),
            qgis_layer_name,
            "postgres",
        )

    # sets xpath
    if layer_xpath:
        couche.setCustomProperty("xpath", layer_xpath)
    couche.setCustomProperty("pkid", layer_pkid)
    return couche
def _qgis_layer(uri, schema_name, layer_name, geometry_column, provider, qgis_layer_name, layer_xpath, layer_pkid):
    if geometry_column is not None:
        g_column = "({})".format(geometry_column)
    else:
        g_column = ""
    if provider == "SQLite":
        # use OGR for spatialite loading
        l = QgsVectorLayer("{}|layername={}{}".format(uri, layer_name, g_column), qgis_layer_name, "ogr")
        l.setProviderEncoding("UTF-8")
    else:
        if schema_name is not None:
            s_table = '"{}"."{}"'.format(schema_name, layer_name)
        else:
            s_table = '"{}"'.format(layer_name)
        # remove "PG:" in front of the uri
        uri = uri[3:]
        l = QgsVectorLayer("{} table={} {} sql=".format(uri, s_table, g_column), qgis_layer_name, "postgres")

    # sets xpath
    if layer_xpath:
        l.setCustomProperty("xpath", layer_xpath)
    l.setCustomProperty("pkid", layer_pkid)
    return l
Exemple #11
0
    def _create_named_layer(self, json_src, layer_name, geo_type, zoom_level):
        """
        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
        """

        source_url = self._source.source()
        options = QgsVectorLayer.LayerOptions(loadDefaultStyle=False)
        layer = QgsVectorLayer(json_src, layer_name, "ogr", options=options)

        layer.setCustomProperty("VectorTilesReader/vector_tile_source", self._connection["name"])
        layer.setCustomProperty("VectorTilesReader/vector_tile_url", source_url)
        layer.setCustomProperty("VectorTilesReader/zoom_level", zoom_level)
        layer.setCustomProperty("VectorTilesReader/geo_type", geo_type)
        layer.setShortName(layer_name)
        layer.setDataUrl(remove_key(source_url))
        layer.setAttribution(self._source.attribution())
        # layer.setAttributionUrl("")
        # layer.setAbstract()
        return layer
Exemple #12
0
    def checkRemovingNonLabeledLayerKeepsLabelCache(self, job_type):
        """ removing a previously used non-labeled layer should keep any previous label caches"""
        layer = QgsVectorLayer("Point?field=fldtxt:string",
                               "layer1", "memory")

        layer.setCustomProperty("labeling", "pal")
        layer.setCustomProperty("labeling/enabled", True)
        layer.setCustomProperty("labeling/fieldName", "fldtxt")

        layer2 = QgsVectorLayer("Point?field=fldtxt:string",
                                "layer2", "memory")

        settings = QgsMapSettings()
        settings.setExtent(QgsRectangle(5, 25, 25, 45))
        settings.setOutputSize(QSize(600, 400))
        settings.setLayers([layer, layer2])

        # with cache - first run should populate cache
        cache = QgsMapRendererCache()
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        self.assertFalse(job.usedCachedLabels())
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())

        self.assertEqual(set(cache.dependentLayers('_labels_')), {layer})

        # remove a previously labeled layer
        settings.setLayers([layer])

        # second job should be able to use label cache, since only a non-labeled layer was removed
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        # should use cache
        self.assertTrue(job.usedCachedLabels())
        # results should have been cached
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertEqual(set(cache.dependentLayers('_labels_')), {layer})
        self.assertTrue(job.takeLabelingResults())
    def checkRemovingNonLabeledLayerKeepsLabelCache(self, job_type):
        """ removing a previously used non-labeled layer should keep any previous label caches"""
        layer = QgsVectorLayer("Point?field=fldtxt:string",
                               "layer1", "memory")

        layer.setCustomProperty("labeling", "pal")
        layer.setCustomProperty("labeling/enabled", True)
        layer.setCustomProperty("labeling/fieldName", "fldtxt")

        layer2 = QgsVectorLayer("Point?field=fldtxt:string",
                                "layer2", "memory")

        settings = QgsMapSettings()
        settings.setExtent(QgsRectangle(5, 25, 25, 45))
        settings.setOutputSize(QSize(600, 400))
        settings.setLayers([layer, layer2])

        # with cache - first run should populate cache
        cache = QgsMapRendererCache()
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        self.assertFalse(job.usedCachedLabels())
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())

        self.assertEqual(set(cache.dependentLayers('_labels_')), {layer})

        # remove a previously labeled layer
        settings.setLayers([layer])

        # second job should be able to use label cache, since only a non-labeled layer was removed
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        # should use cache
        self.assertTrue(job.usedCachedLabels())
        # results should have been cached
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertEqual(set(cache.dependentLayers('_labels_')), {layer})
        self.assertTrue(job.takeLabelingResults())
Exemple #14
0
    def checkRepaintLabeledLayerInvalidatesLabelCache(self, job_type):
        layer = QgsVectorLayer("Point?field=fldtxt:string",
                               "layer1", "memory")

        layer.setCustomProperty("labeling", "pal")
        layer.setCustomProperty("labeling/enabled", True)
        layer.setCustomProperty("labeling/fieldName", "fldtxt")

        settings = QgsMapSettings()
        settings.setExtent(QgsRectangle(5, 25, 25, 45))
        settings.setOutputSize(QSize(600, 400))
        settings.setLayers([layer])

        # with cache - first run should populate cache
        cache = QgsMapRendererCache()
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        self.assertFalse(job.usedCachedLabels())
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())

        self.assertEqual(cache.dependentLayers('_labels_'), [layer])

        # trigger repaint on layer - should invalidate cache and block use of cached labels
        layer.triggerRepaint()
        self.assertFalse(cache.hasCacheImage('_labels_'))

        # second job should not use label cache, since layer was repainted
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        # shouldn't use cache
        self.assertFalse(job.usedCachedLabels())
        # but results should have been cached
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())
    def checkRepaintLabeledLayerInvalidatesLabelCache(self, job_type):
        layer = QgsVectorLayer("Point?field=fldtxt:string",
                               "layer1", "memory")

        layer.setCustomProperty("labeling", "pal")
        layer.setCustomProperty("labeling/enabled", True)
        layer.setCustomProperty("labeling/fieldName", "fldtxt")

        settings = QgsMapSettings()
        settings.setExtent(QgsRectangle(5, 25, 25, 45))
        settings.setOutputSize(QSize(600, 400))
        settings.setLayers([layer])

        # with cache - first run should populate cache
        cache = QgsMapRendererCache()
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        self.assertFalse(job.usedCachedLabels())
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())

        self.assertEqual(cache.dependentLayers('_labels_'), [layer])

        # trigger repaint on layer - should invalidate cache and block use of cached labels
        layer.triggerRepaint()
        self.assertFalse(cache.hasCacheImage('_labels_'))

        # second job should not use label cache, since layer was repainted
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        # shouldn't use cache
        self.assertFalse(job.usedCachedLabels())
        # but results should have been cached
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())
Exemple #16
0
def points_along_line(layerout,
                      startpoint,
                      endpoint,
                      distance,
                      label,
                      layer,
                      selected_only=True,
                      force=False,
                      divide=0):
    """Adding Points along the line
    """
    # Create a new memory layer and add a distance attribute self.layerNameLine
    #layer_crs = virt_layer.setCrs(layer.crs())
    virt_layer = QgsVectorLayer("Point?crs=%s" % layer.crs().authid(),
                                layerout,
                                "memory")
    provider = virt_layer.dataProvider()
    virt_layer.startEditing()   # actually writes attributes
    units = layer.crs().mapUnits()
    unit_dic = {
        QGis.Degrees: 'Degrees',
        QGis.Meters: 'Meters',
        QGis.Feet: 'Feet',
        QGis.UnknownUnit: 'Unknown'}
    unit = unit_dic.get(units, 'Unknown')
    provider.addAttributes([QgsField("fid", QVariant.Int)])
    provider.addAttributes([QgsField("cng_("+unit+")", QVariant.Int)])

    def get_features():
        """Getting the features
        """
        if selected_only:
            return layer.selectedFeatures()
        else:
            return layer.getFeatures()

    # Loop through all (selected) features
    for feature in get_features():
        geom = feature.geometry()
        # Add feature ID of selected feature
        fid = feature.id()
        if not geom:
            QgsMessageLog.logMessage("No geometry", "QChainage")
            continue

        features = create_points_at(startpoint, endpoint, distance, geom,
                                    fid, force, divide)
        provider.addFeatures(features)
        virt_layer.updateExtents()

    QgsMapLayerRegistry.instance().addMapLayers([virt_layer])
    virt_layer.commitChanges()
    virt_layer.reload()

    #from here Add labeling
    #generic labeling properties
    if label:
        virt_layer.setCustomProperty("labeling", "pal")
        virt_layer.setCustomProperty("labeling/enabled", "true")
        virt_layer.setCustomProperty("labeling/fieldName", "cng_("+unit+")")
        virt_layer.setCustomProperty("labeling/fontSize", "10")
        virt_layer.setCustomProperty("labeling/multiLineLabels", "true")

        #virt_layer.setCustomProperty("labeling/Size", "5")
    # symbol = QgsMarkerSymbolV2.createSimple({"name": "capital"})
    # virt_layer.setRendererV2(QgsSingleSymbolRendererV2(symbol))
    virt_layer.triggerRepaint()
    return
class LoadOutputAsLayerDialog(QDialog, FORM_CLASS):
    """
    Modal dialog to load an oq-engine output as layer
    """
    def __init__(self,
                 iface,
                 viewer_dock,
                 session,
                 hostname,
                 calc_id,
                 output_type=None,
                 path=None,
                 mode=None,
                 zonal_layer_path=None,
                 engine_version=None):
        # sanity check
        if output_type not in OQ_TO_LAYER_TYPES:
            raise NotImplementedError(output_type)
        self.iface = iface
        self.viewer_dock = viewer_dock
        self.path = path
        self.session = session
        self.hostname = hostname
        self.calc_id = calc_id
        self.output_type = output_type
        self.mode = mode  # if 'testing' it will avoid some user interaction
        self.zonal_layer_path = zonal_layer_path
        self.engine_version = engine_version
        QDialog.__init__(self)
        # Set up the user interface from Designer.
        self.setupUi(self)
        # Disable ok_button until all user options are set
        self.ok_button = self.buttonBox.button(QDialogButtonBox.Ok)
        self.ok_button.setDisabled(True)

    def create_file_hlayout(self):
        self.file_hlayout = QHBoxLayout()
        self.file_lbl = QLabel('File to load')
        self.file_browser_tbn = QToolButton()
        self.file_browser_tbn.setText('...')
        self.file_browser_tbn.clicked.connect(self.on_file_browser_tbn_clicked)
        self.path_le = QLineEdit()
        self.path_le.setEnabled(False)
        self.file_hlayout.addWidget(self.file_lbl)
        self.file_hlayout.addWidget(self.file_browser_tbn)
        self.file_hlayout.addWidget(self.path_le)
        self.vlayout.addLayout(self.file_hlayout)

    def create_num_sites_indicator(self):
        self.num_sites_msg = 'Number of sites: %s'
        self.num_sites_lbl = QLabel(self.num_sites_msg % '')
        self.vlayout.addWidget(self.num_sites_lbl)

    def create_file_size_indicator(self):
        self.file_size_msg = 'File size: %s'
        self.file_size_lbl = QLabel(self.file_size_msg % '')
        self.vlayout.addWidget(self.file_size_lbl)

    def create_rlz_or_stat_selector(self, label='Realization'):
        self.rlz_or_stat_lbl = QLabel(label)
        self.rlz_or_stat_cbx = QComboBox()
        self.rlz_or_stat_cbx.setEnabled(False)
        self.rlz_or_stat_cbx.currentIndexChanged['QString'].connect(
            self.on_rlz_or_stat_changed)
        self.vlayout.addWidget(self.rlz_or_stat_lbl)
        self.vlayout.addWidget(self.rlz_or_stat_cbx)

    def create_imt_selector(self):
        self.imt_lbl = QLabel('Intensity Measure Type')
        self.imt_cbx = QComboBox()
        self.imt_cbx.setEnabled(False)
        self.imt_cbx.currentIndexChanged['QString'].connect(
            self.on_imt_changed)
        self.vlayout.addWidget(self.imt_lbl)
        self.vlayout.addWidget(self.imt_cbx)

    def create_poe_selector(self):
        self.poe_lbl = QLabel('Probability of Exceedance')
        self.poe_cbx = QComboBox()
        self.poe_cbx.setEnabled(False)
        self.poe_cbx.currentIndexChanged['QString'].connect(
            self.on_poe_changed)
        self.vlayout.addWidget(self.poe_lbl)
        self.vlayout.addWidget(self.poe_cbx)

    def create_loss_type_selector(self):
        self.loss_type_lbl = QLabel('Loss Type')
        self.loss_type_cbx = QComboBox()
        self.loss_type_cbx.setEnabled(False)
        self.loss_type_cbx.currentIndexChanged['QString'].connect(
            self.on_loss_type_changed)
        self.vlayout.addWidget(self.loss_type_lbl)
        self.vlayout.addWidget(self.loss_type_cbx)

    def create_eid_selector(self):
        self.eid_lbl = QLabel('Event ID')
        self.eid_sbx = QSpinBox()
        self.eid_sbx.setEnabled(False)
        self.vlayout.addWidget(self.eid_lbl)
        self.vlayout.addWidget(self.eid_sbx)

    def create_dmg_state_selector(self):
        self.dmg_state_lbl = QLabel('Damage state')
        self.dmg_state_cbx = QComboBox()
        self.dmg_state_cbx.setEnabled(False)
        self.dmg_state_cbx.currentIndexChanged['QString'].connect(
            self.on_dmg_state_changed)
        self.vlayout.addWidget(self.dmg_state_lbl)
        self.vlayout.addWidget(self.dmg_state_cbx)

    def create_taxonomy_selector(self):
        self.taxonomy_lbl = QLabel('Taxonomy')
        self.taxonomy_cbx = QComboBox()
        self.taxonomy_cbx.setEnabled(False)
        self.vlayout.addWidget(self.taxonomy_lbl)
        self.vlayout.addWidget(self.taxonomy_cbx)

    def create_style_by_selector(self):
        self.style_by_lbl = QLabel('Style by')
        self.style_by_cbx = QComboBox()
        self.vlayout.addWidget(self.style_by_lbl)
        self.vlayout.addWidget(self.style_by_cbx)

    def create_load_selected_only_ckb(self):
        self.load_selected_only_ckb = QCheckBox("Load only the selected items")
        self.load_selected_only_ckb.setChecked(True)
        self.vlayout.addWidget(self.load_selected_only_ckb)

    def create_save_as_shp_ckb(self):
        self.save_as_shp_ckb = QCheckBox("Save the loaded layer as shapefile")
        self.save_as_shp_ckb.setChecked(False)
        self.vlayout.addWidget(self.save_as_shp_ckb)

    def create_zonal_layer_selector(self):
        self.zonal_layer_gbx = QGroupBox()
        self.zonal_layer_gbx.setTitle('Aggregate by zone (optional)')
        self.zonal_layer_gbx.setCheckable(True)
        self.zonal_layer_gbx.setChecked(False)
        self.zonal_layer_gbx_v_layout = QVBoxLayout()
        self.zonal_layer_gbx.setLayout(self.zonal_layer_gbx_v_layout)
        self.zonal_layer_cbx = QComboBox()
        self.zonal_layer_cbx.addItem('')
        self.zonal_layer_lbl = QLabel('Zonal layer')
        self.zonal_layer_tbn = QToolButton()
        self.zonal_layer_tbn.setText('...')
        self.zonal_layer_h_layout = QHBoxLayout()
        self.zonal_layer_h_layout.addWidget(self.zonal_layer_cbx)
        self.zonal_layer_h_layout.addWidget(self.zonal_layer_tbn)
        self.zonal_layer_gbx_v_layout.addWidget(self.zonal_layer_lbl)
        self.zonal_layer_gbx_v_layout.addLayout(self.zonal_layer_h_layout)
        self.zone_id_field_lbl = QLabel('Field containing zone ids')
        self.zone_id_field_cbx = QComboBox()
        self.zonal_layer_gbx_v_layout.addWidget(self.zone_id_field_lbl)
        self.zonal_layer_gbx_v_layout.addWidget(self.zone_id_field_cbx)
        self.vlayout.addWidget(self.zonal_layer_gbx)
        self.zonal_layer_tbn.clicked.connect(self.on_zonal_layer_tbn_clicked)
        self.zonal_layer_cbx.currentIndexChanged[int].connect(
            self.on_zonal_layer_cbx_currentIndexChanged)
        self.zonal_layer_gbx.toggled[bool].connect(
            self.on_zonal_layer_gbx_toggled)

    def pre_populate_zonal_layer_cbx(self):
        for key, layer in \
                QgsMapLayerRegistry.instance().mapLayers().iteritems():
            # populate loss cbx only with layers containing points
            if layer.type() != QgsMapLayer.VectorLayer:
                continue
            if layer.geometryType() == QGis.Polygon:
                self.zonal_layer_cbx.addItem(layer.name())
                self.zonal_layer_cbx.setItemData(
                    self.zonal_layer_cbx.count() - 1, layer.id())

    def on_zonal_layer_cbx_currentIndexChanged(self, new_index):
        self.zone_id_field_cbx.clear()
        zonal_layer = None
        if not self.zonal_layer_cbx.currentText():
            if self.zonal_layer_gbx.isChecked():
                self.ok_button.setEnabled(False)
            return
        zonal_layer_id = self.zonal_layer_cbx.itemData(new_index)
        zonal_layer = QgsMapLayerRegistry.instance().mapLayer(zonal_layer_id)
        # if the zonal_layer doesn't have a field containing a unique zone id,
        # the user can choose to add such unique id
        self.zone_id_field_cbx.addItem("Add field with unique zone id")
        for field in zonal_layer.fields():
            # for the zone id accept both numeric or textual fields
            self.zone_id_field_cbx.addItem(field.name())
            # by default, set the selection to the first textual field
        self.set_ok_button()

    def on_zonal_layer_gbx_toggled(self, on):
        if on and not self.zonal_layer_cbx.currentText():
            self.ok_button.setEnabled(False)
        else:
            self.set_ok_button()

    def on_output_type_changed(self):
        if self.output_type in OQ_TO_LAYER_TYPES:
            self.create_load_selected_only_ckb()
        elif self.output_type in OQ_COMPLEX_CSV_TO_LAYER_TYPES:
            self.create_save_as_shp_ckb()
        self.set_ok_button()

    @pyqtSlot()
    def on_file_browser_tbn_clicked(self):
        path = self.open_file_dialog()
        if path:
            self.populate_out_dep_widgets()
        self.set_ok_button()

    def on_rlz_or_stat_changed(self):
        self.dataset = self.npz_file[self.rlz_or_stat_cbx.currentText()]
        self.set_ok_button()

    def on_loss_type_changed(self):
        self.set_ok_button()

    def on_imt_changed(self):
        self.set_ok_button()

    def on_poe_changed(self):
        self.set_ok_button()

    def on_eid_changed(self):
        self.set_ok_button()

    def on_dmg_state_changed(self):
        self.set_ok_button()

    def open_file_dialog(self):
        """
        Open a file dialog to select the data file to be loaded
        """
        text = self.tr('Select the OQ-Engine output file to import')
        if self.output_type in OQ_CSV_TO_LAYER_TYPES:
            filters = self.tr('CSV files (*.csv)')
        else:
            raise NotImplementedError(self.output_type)
        default_dir = QSettings().value('irmt/load_as_layer_dir',
                                        QDir.homePath())
        path = QFileDialog.getOpenFileName(self, text, default_dir, filters)
        if not path:
            return
        selected_dir = QFileInfo(path).dir().path()
        QSettings().setValue('irmt/load_as_layer_dir', selected_dir)
        self.path = path
        self.path_le.setText(self.path)
        return path

    def populate_out_dep_widgets(self):
        self.populate_rlz_or_stat_cbx()
        self.show_num_sites()

    def get_taxonomies(self):
        raise NotImplementedError()

    def populate_rlz_or_stat_cbx(self):
        self.rlzs_or_stats = [
            key for key in sorted(self.npz_file)
            if key not in ('imtls', 'array')
        ]
        self.rlz_or_stat_cbx.clear()
        self.rlz_or_stat_cbx.setEnabled(True)
        self.rlz_or_stat_cbx.addItems(self.rlzs_or_stats)

    def populate_loss_type_cbx(self, loss_types):
        self.loss_type_cbx.clear()
        self.loss_type_cbx.setEnabled(True)
        self.loss_type_cbx.addItems(loss_types)

    def show_num_sites(self):
        # NOTE: we are assuming all realizations have the same number of sites,
        #       which currently is always true.
        #       If different realizations have a different number of sites, we
        #       need to move this block of code inside on_rlz_or_stat_changed()
        rlz_or_stat_data = self.npz_file[self.rlz_or_stat_cbx.currentText()]
        self.num_sites_lbl.setText(self.num_sites_msg % rlz_or_stat_data.shape)

    def set_ok_button(self):
        raise NotImplementedError()

    def build_layer_name(self, *args, **kwargs):
        raise NotImplementedError()

    def get_field_names(self, **kwargs):
        raise NotImplementedError()

    def add_field_to_layer(self, field_name):
        raise NotImplementedError()

    def read_npz_into_layer(self, field_names, **kwargs):
        raise NotImplementedError()

    def load_from_npz(self):
        raise NotImplementedError()

    def get_investigation_time(self):
        if self.output_type in ('hcurves', 'uhs', 'hmaps'):
            try:
                investigation_time = self.npz_file['investigation_time']
            except KeyError:
                msg = ('investigation_time not found. It is mandatory for %s.'
                       ' Please check if the ouptut was produced by an'
                       ' obsolete version of the OpenQuake Engine'
                       ' Server.') % self.output_type
                log_msg(msg, level='C', message_bar=self.iface.messageBar())
            else:
                return investigation_time
        else:
            # some outputs do not need the investigation time
            return None

    def build_layer(self,
                    rlz_or_stat=None,
                    taxonomy=None,
                    poe=None,
                    loss_type=None,
                    dmg_state=None,
                    gsim=None):
        layer_name = self.build_layer_name(rlz_or_stat=rlz_or_stat,
                                           taxonomy=taxonomy,
                                           poe=poe,
                                           loss_type=loss_type,
                                           dmg_state=dmg_state,
                                           gsim=gsim)
        field_names = self.get_field_names(rlz_or_stat=rlz_or_stat,
                                           taxonomy=taxonomy,
                                           poe=poe,
                                           loss_type=loss_type,
                                           dmg_state=dmg_state)

        # create layer
        self.layer = QgsVectorLayer("Point?crs=epsg:4326", layer_name,
                                    "memory")
        for field_name in field_names:
            if field_name in ['lon', 'lat']:
                continue
            added_field_name = self.add_field_to_layer(field_name)
            if field_name != added_field_name:
                if field_name == self.default_field_name:
                    self.default_field_name = added_field_name
                # replace field_name with the actual added_field_name
                field_name_idx = field_names.index(field_name)
                field_names.remove(field_name)
                field_names.insert(field_name_idx, added_field_name)

        self.read_npz_into_layer(field_names,
                                 rlz_or_stat=rlz_or_stat,
                                 taxonomy=taxonomy,
                                 poe=poe,
                                 loss_type=loss_type,
                                 dmg_state=dmg_state)
        self.layer.setCustomProperty('output_type', self.output_type)
        investigation_time = self.get_investigation_time()
        if investigation_time is not None:
            self.layer.setCustomProperty('investigation_time',
                                         investigation_time)
        if self.engine_version is not None:
            self.layer.setCustomProperty('engine_version', self.engine_version)
        irmt_version = get_irmt_version()
        self.layer.setCustomProperty('irmt_version', irmt_version)
        self.layer.setCustomProperty('calc_id', self.calc_id)
        QgsMapLayerRegistry.instance().addMapLayer(self.layer)
        self.iface.setActiveLayer(self.layer)
        self.iface.zoomToActiveLayer()

    def _set_symbol_size(self, symbol):
        if self.iface.mapCanvas().mapUnits() == QGis.Degrees:
            point_size = 0.05
        elif self.iface.mapCanvas().mapUnits() == QGis.Meters:
            point_size = 4000
        else:
            # it is not obvious how to choose the point size in the other
            # cases, so we conservatively keep the default sizing
            return
        symbol.symbolLayer(0).setSizeUnit(symbol.MapUnit)
        symbol.symbolLayer(0).setSize(point_size)
        map_unit_scale = QgsMapUnitScale()
        map_unit_scale.maxSizeMMEnabled = True
        map_unit_scale.minSizeMMEnabled = True
        map_unit_scale.minSizeMM = 0.5
        map_unit_scale.maxSizeMM = 10
        symbol.symbolLayer(0).setSizeMapUnitScale(map_unit_scale)

    def style_maps(self, layer=None, style_by=None):
        if layer is None:
            layer = self.layer
        if style_by is None:
            style_by = self.default_field_name
        symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
        # see properties at:
        # https://qgis.org/api/qgsmarkersymbollayerv2_8cpp_source.html#l01073
        symbol.setAlpha(1)  # opacity
        if isinstance(symbol, QgsMarkerSymbolV2):
            # do it only for the layer with points
            self._set_symbol_size(symbol)
            symbol.symbolLayer(0).setOutlineStyle(Qt.PenStyle(Qt.NoPen))

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

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

        # in most cases, we override the user-specified setting, and use
        # instead a setting that was required by scientists
        if self.output_type in OQ_TO_LAYER_TYPES:
            default_qgs_style = QgsStyleV2().defaultStyle()
            default_color_ramp_names = default_qgs_style.colorRampNames()
            if self.output_type in ('dmg_by_asset', 'losses_by_asset',
                                    'avg_losses-stats'):
                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                # jenks = natural breaks
                mode = QgsGraduatedSymbolRendererV2.Jenks
                ramp_type_idx = default_color_ramp_names.index('Reds')
                inverted = False
            elif self.output_type in ('hmaps', 'gmf_data', 'ruptures'):
                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                if self.output_type == 'ruptures':
                    mode = QgsGraduatedSymbolRendererV2.Pretty
                else:
                    mode = QgsGraduatedSymbolRendererV2.EqualInterval
                ramp_type_idx = default_color_ramp_names.index('Spectral')
                inverted = True
            ramp = default_qgs_style.colorRamp(
                default_color_ramp_names[ramp_type_idx])
        graduated_renderer = QgsGraduatedSymbolRendererV2.createRenderer(
            layer,
            style_by,
            style['classes'],
            mode,
            symbol,
            ramp,
            inverted=inverted)
        label_format = graduated_renderer.labelFormat()
        # label_format.setTrimTrailingZeroes(True)  # it might be useful
        label_format.setPrecision(2)
        graduated_renderer.setLabelFormat(label_format, updateRanges=True)
        # add a class for 0 values, unless while styling ruptures
        if self.output_type != 'ruptures':
            VERY_SMALL_VALUE = 1e-20
            graduated_renderer.updateRangeLowerValue(0, VERY_SMALL_VALUE)
            symbol_zeros = QgsSymbolV2.defaultSymbol(layer.geometryType())
            symbol_zeros.setColor(QColor(240, 240, 240))  # very light grey
            if isinstance(symbol, QgsMarkerSymbolV2):
                # do it only for the layer with points
                self._set_symbol_size(symbol_zeros)
                symbol_zeros.symbolLayer(0).setOutlineStyle(
                    Qt.PenStyle(Qt.NoPen))
            zeros_min = 0.0
            zeros_max = VERY_SMALL_VALUE
            range_zeros = QgsRendererRangeV2(
                zeros_min, zeros_max, symbol_zeros,
                " %.2f - %.2f" % (zeros_min, zeros_max), True)
            graduated_renderer.addClassRange(range_zeros)
            graduated_renderer.moveClass(
                len(graduated_renderer.ranges()) - 1, 0)
        layer.setRendererV2(graduated_renderer)
        layer.setLayerTransparency(30)  # percent
        layer.triggerRepaint()
        self.iface.legendInterface().refreshLayerSymbology(layer)
        self.iface.mapCanvas().refresh()

    def style_categorized(self, layer, style_by):
        # get unique values
        fni = layer.fieldNameIndex(style_by)
        unique_values = layer.dataProvider().uniqueValues(fni)
        # define categories
        categories = []
        for unique_value in unique_values:
            # initialize the default symbol for this geometry type
            symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
            # configure a symbol layer
            layer_style = {}
            layer_style['color'] = '%d, %d, %d' % (randrange(
                0, 256), randrange(0, 256), randrange(0, 256))
            layer_style['outline'] = '#000000'
            symbol_layer = QgsSimpleFillSymbolLayerV2.create(layer_style)
            # replace default symbol layer with the configured one
            if symbol_layer is not None:
                symbol.changeSymbolLayer(0, symbol_layer)
            # create renderer object
            category = QgsRendererCategoryV2(unique_value, symbol,
                                             str(unique_value))
            # entry for the list of category items
            categories.append(category)
        # create renderer object
        renderer = QgsCategorizedSymbolRendererV2(style_by, categories)
        # assign the created renderer to the layer
        if renderer is not None:
            layer.setRendererV2(renderer)
        layer.triggerRepaint()
        self.iface.legendInterface().refreshLayerSymbology(layer)
        self.iface.mapCanvas().refresh()

    def style_curves(self):
        registry = QgsSymbolLayerV2Registry.instance()
        cross = registry.symbolLayerMetadata("SimpleMarker").createSymbolLayer(
            {
                'name': 'cross2',
                'color': '0,0,0',
                'color_border': '0,0,0',
                'offset': '0,0',
                'size': '1.5',
                'angle': '0'
            })
        symbol = QgsSymbolV2.defaultSymbol(self.layer.geometryType())
        symbol.deleteSymbolLayer(0)
        symbol.appendSymbolLayer(cross)
        self._set_symbol_size(symbol)
        renderer = QgsSingleSymbolRendererV2(symbol)
        effect = QgsOuterGlowEffect()
        effect.setSpread(0.5)
        effect.setTransparency(0)
        effect.setColor(QColor(255, 255, 255))
        effect.setBlurLevel(1)
        renderer.paintEffect().appendEffect(effect)
        renderer.paintEffect().setEnabled(True)
        self.layer.setRendererV2(renderer)
        self.layer.setLayerTransparency(30)  # percent
        self.layer.triggerRepaint()
        self.iface.legendInterface().refreshLayerSymbology(self.layer)
        self.iface.mapCanvas().refresh()

    def open_zonal_layer_dialog(self):
        """
        Open a file dialog to select the zonal layer to be loaded
        :returns: the zonal layer
        """
        text = self.tr('Select zonal layer to import')
        filters = self.tr('Vector shapefiles (*.shp);;SQLite (*.sqlite);;'
                          'All files (*.*)')
        default_dir = QSettings().value('irmt/select_layer_dir',
                                        QDir.homePath())
        file_name, file_type = QFileDialog.getOpenFileNameAndFilter(
            self, text, default_dir, filters)
        if not file_name:
            return None
        selected_dir = QFileInfo(file_name).dir().path()
        QSettings().setValue('irmt/select_layer_dir', selected_dir)
        zonal_layer_plus_stats = self.load_zonal_layer(file_name)
        return zonal_layer_plus_stats

    def load_zonal_layer(self, zonal_layer_path, make_a_copy=False):
        # Load zonal layer
        zonal_layer = QgsVectorLayer(zonal_layer_path, tr('Zonal data'), 'ogr')
        if not zonal_layer.geometryType() == QGis.Polygon:
            msg = 'Zonal layer must contain zone polygons'
            log_msg(msg, level='C', message_bar=self.iface.messageBar())
            return False
        if make_a_copy:
            # Make a copy, where stats will be added
            zonal_layer_plus_stats = ProcessLayer(
                zonal_layer).duplicate_in_memory()
        else:
            zonal_layer_plus_stats = zonal_layer
        # Add zonal layer to registry
        if zonal_layer_plus_stats.isValid():
            QgsMapLayerRegistry.instance().addMapLayer(zonal_layer_plus_stats)
        else:
            msg = 'Invalid zonal layer'
            log_msg(msg, level='C', message_bar=self.iface.messageBar())
            return None
        return zonal_layer_plus_stats

    def on_zonal_layer_tbn_clicked(self):
        zonal_layer_plus_stats = self.open_zonal_layer_dialog()
        if (zonal_layer_plus_stats
                and zonal_layer_plus_stats.geometryType() == QGis.Polygon):
            self.populate_zonal_layer_cbx(zonal_layer_plus_stats)

    def populate_zonal_layer_cbx(self, zonal_layer_plus_stats):
        cbx = self.zonal_layer_cbx
        cbx.addItem(zonal_layer_plus_stats.name())
        last_index = cbx.count() - 1
        cbx.setItemData(last_index, zonal_layer_plus_stats.id())
        cbx.setCurrentIndex(last_index)

    def show_file_size(self):
        file_size = get_file_size(self.path)
        self.file_size_lbl.setText(self.file_size_msg % file_size)

    def accept(self):
        if self.output_type in OQ_EXTRACT_TO_LAYER_TYPES:
            self.load_from_npz()
            if self.output_type in ('losses_by_asset', 'dmg_by_asset',
                                    'avg_losses-stats'):
                # check if also aggregating by zone or not
                if (not self.zonal_layer_cbx.currentText()
                        or not self.zonal_layer_gbx.isChecked()):
                    super(LoadOutputAsLayerDialog, self).accept()
                    return
                loss_layer = self.layer
                self.iface.legendInterface().setLayerVisible(loss_layer, False)
                zonal_layer_id = self.zonal_layer_cbx.itemData(
                    self.zonal_layer_cbx.currentIndex())
                zonal_layer = QgsMapLayerRegistry.instance().mapLayer(
                    zonal_layer_id)
                # if the two layers have different projections, display an
                # error message and return
                have_same_projection, check_projection_msg = ProcessLayer(
                    loss_layer).has_same_projection_as(zonal_layer)
                if not have_same_projection:
                    log_msg(check_projection_msg,
                            level='C',
                            message_bar=self.iface.messageBar())
                    # TODO: load only loss layer
                    super(LoadOutputAsLayerDialog, self).accept()
                    return
                loss_attr_names = [
                    field.name() for field in loss_layer.fields()
                ]
                zone_id_in_losses_attr_name = None
                # index 0 is for "Add field with unique zone id"
                if self.zone_id_field_cbx.currentIndex() == 0:
                    zone_id_in_zones_attr_name = None
                else:
                    zone_id_in_zones_attr_name = \
                        self.zone_id_field_cbx.currentText()
                # aggregate losses by zone (calculate count of points in the
                # zone, sum and average loss values for the same zone)
                loss_layer_is_vector = True
                try:
                    res = calculate_zonal_stats(loss_layer,
                                                zonal_layer,
                                                loss_attr_names,
                                                loss_layer_is_vector,
                                                zone_id_in_losses_attr_name,
                                                zone_id_in_zones_attr_name,
                                                self.iface,
                                                extra=False)
                except TypeError as exc:
                    log_msg(str(exc),
                            level='C',
                            message_bar=self.iface.messageBar())
                    return
                (loss_layer, zonal_layer, loss_attrs_dict) = res
                # sanity check
                assert len(loss_attrs_dict) == 1, (
                    "Only one attribute should be added to the zonal layer."
                    " %s were added insted" % len(loss_attrs_dict))
                # NOTE: in scenario damage, keys are like
                #       u'structural_no_damage_mean', and not just
                #       u'structural', therefore we can't just use the selected
                #       loss type, but we must use the actual only key in the
                #       dict
                [added_loss_attr] = loss_attrs_dict
                style_by = loss_attrs_dict[added_loss_attr]['sum']
                self.style_maps(layer=zonal_layer, style_by=style_by)
        elif self.output_type in OQ_CSV_TO_LAYER_TYPES:
            self.load_from_csv()
        super(LoadOutputAsLayerDialog, self).accept()

    def reject(self):
        if (hasattr(self, 'npz_file') and self.npz_file is not None
                and self.output_type in OQ_TO_LAYER_TYPES):
            self.npz_file.close()
        super(LoadOutputAsLayerDialog, self).reject()
Exemple #18
0
class LayerImportWorker(QObject):

    finished = pyqtSignal(QgsVectorLayer, QgsVectorLayer)
    interrupted = pyqtSignal(QgsVectorLayer, QgsVectorLayer)
    progressed = pyqtSignal(QgsVectorLayer, QgsVectorLayer, bool, int, bool,
                            bool)

    def __init__(self,
                 source_layer,
                 selected_only,
                 layer_name,
                 additional_output_fields=None):
        super().__init__()
        self.source_layer = source_layer
        self.selected_only = selected_only
        self.additional_output_fields = additional_output_fields if additional_output_fields else []

        self.layer_found = QgsVectorLayer(f"Polygon?crs=EPSG:{2180}",
                                          layer_name, "memory")
        self.layer_found.setCustomProperty("ULDK",
                                           f"{layer_name} point_import_found")

        self.layer_not_found = QgsVectorLayer(f"Point?crs=EPSG:{2180}",
                                              f"{layer_name} (nieznalezione)",
                                              "memory")
        self.layer_not_found.setCustomProperty(
            "ULDK", f"{layer_name} point_import_not_found")

    @pyqtSlot()
    def search(self):
        fields = PLOTS_LAYER_DEFAULT_FIELDS + self.additional_output_fields

        self.layer_found.startEditing()
        self.layer_found.dataProvider().addAttributes(fields)

        self.layer_not_found.startEditing()
        self.layer_not_found.dataProvider().addAttributes([
            QgsField("tresc_bledu", QVariant.String),
        ])

        self.uldk_search = ULDKSearchPoint(
            "dzialka", ("geom_wkt", "wojewodztwo", "powiat", "gmina", "obreb",
                        "numer", "teryt"))

        self.uldk_search = ULDKSearchLogger(self.uldk_search)

        feature_iterator = self.source_layer.getSelectedFeatures(
        ) if self.selected_only else self.source_layer.getFeatures()
        source_geom_type = self.source_layer.wkbType()
        source_crs = self.source_layer.sourceCrs()
        self.geometries = []
        self.not_found_geometries = []
        self.parcels_geometry = QgsGeometry.fromMultiPolygonXY([])

        self.transformation = None
        if source_crs != CRS_2180:
            self.transformation = QgsCoordinateTransform(
                source_crs, CRS_2180, QgsCoordinateTransformContext())

        geom_type = self._get_non_z_geom_type(source_geom_type)
        if geom_type == QgsWkbTypes.Point or geom_type == QgsWkbTypes.MultiPoint:
            self.count_not_found_as_progressed = True

            for index, f in enumerate(feature_iterator):
                point = f.geometry().asPoint()

                if self.transformation:
                    point = self.transformation.transform(point)

                f.setGeometry(QgsGeometry.fromPointXY(point))
                self._process_feature(f, True)
        else:
            self.count_not_found_as_progressed = False

            if self.additional_output_fields:
                self.fields_to_add = QgsFields()
                for field in self.additional_output_fields:
                    self.fields_to_add.append(field)

            for f in feature_iterator:
                additional_attributes = []

                if self.additional_output_fields:
                    additional_attributes = [
                        f.attribute(field.name())
                        for field in self.additional_output_fields
                    ]

                points = self._feature_to_points(f, source_geom_type,
                                                 additional_attributes)
                continue_search = True

                while points != []:
                    saved_features = [
                        self._process_feature(point) for point in points
                    ]
                    if any(saved_features):
                        points = self._feature_to_points(
                            f, source_geom_type, additional_attributes)
                    else:
                        points = []
                self.__commit()
                self.progressed.emit(self.layer_found, self.layer_not_found,
                                     True, 0, False, True)

        self.finished.emit(self.layer_found, self.layer_not_found)

    def __make_not_found_feature(self, geometry, e):
        error_message = str(e)
        feature = QgsFeature()
        feature.setGeometry(geometry)
        feature.setAttributes([error_message])

        return feature

    def __commit(self):
        self.layer_found.commitChanges()
        self.layer_not_found.commitChanges()

    def _process_feature(self, source_feature, made_progress=False):

        if QThread.currentThread().isInterruptionRequested():
            self.__commit()
            self.interrupted.emit(self.layer_found, self.layer_not_found)
            self.layer_found.stopEditing()
            return

        point = source_feature.geometry().asPoint()
        if self.parcels_geometry.contains(point):
            if made_progress:
                self.__commit()
                self.progressed.emit(self.layer_found, self.layer_not_found,
                                     True, 1, False, made_progress)
            return

        uldk_point = ULDKPoint(point.x(), point.y(), 2180)
        found_parcels_geometries = []
        saved = False

        try:
            uldk_response_row = self.uldk_search.search(uldk_point)
            additional_attributes = []
            for field in self.additional_output_fields:
                additional_attributes.append(source_feature[field.name()])
            try:
                found_feature = uldk_response_to_qgs_feature(
                    uldk_response_row, additional_attributes)
                geometry_wkt = found_feature.geometry().asWkt()
            except BadGeometryException:
                raise BadGeometryException("Niepoprawna geometria")
            if geometry_wkt not in self.geometries:
                saved = True
                self.layer_found.dataProvider().addFeature(found_feature)
                self.geometries.append(geometry_wkt)
                found_parcels_geometries.append(
                    found_feature.geometry().asPolygon())
                self.progressed.emit(self.layer_found, self.layer_not_found,
                                     True, 0, saved, made_progress)
        except Exception as e:
            geometry = source_feature.geometry()
            geometry_wkt = geometry.asWkt()
            if geometry_wkt not in self.not_found_geometries:
                not_found_feature = self.__make_not_found_feature(geometry, e)
                self.layer_not_found.dataProvider().addFeature(
                    not_found_feature)
                self.progressed.emit(self.layer_found, self.layer_not_found,
                                     False, 0, saved, made_progress)
                self.not_found_geometries.append(geometry_wkt)

        self.parcels_geometry.addPartGeometry(
            QgsGeometry.fromMultiPolygonXY(found_parcels_geometries))
        self.__commit()
        return saved

    def _feature_to_points(self, feature, geom_type, additional_attributes):
        geometry = feature.geometry()

        if QgsWkbTypes.hasZ(geom_type):
            geometry, geom_type = self.drop_z_from_geom(geometry, geom_type)

        features = []
        points_number = 0

        if self.transformation is not None:
            geometry.transform(self.transformation)

        if geom_type == QgsWkbTypes.LineString or geom_type == QgsWkbTypes.MultiLineString:
            points_number = 10
            geometry = geometry.buffer(0.001, 2)

        if self.parcels_geometry:
            if self.parcels_geometry.contains(geometry):
                return []
            else:
                if not geometry.isMultipart():
                    geometry.convertToMultiType()

                multi_polygon = QgsGeometry.fromMultiPolygonXY(
                    geometry.asMultiPolygon())
                geometry = multi_polygon.difference(
                    self.parcels_geometry.buffer(0.001, 2))
                if not geometry:
                    return []

        da = QgsDistanceArea()
        da.setSourceCrs(CRS_2180, QgsProject.instance().transformContext())
        da.setEllipsoid(QgsProject.instance().ellipsoid())

        points_number = 20 if not points_number else points_number
        area = int(da.measureArea(geometry)) / 10000
        if area > 1:
            points_number *= area
        points = geometry.randomPointsInPolygon(points_number)

        for point in points:
            feature = QgsFeature()

            if additional_attributes:
                feature.setFields(self.fields_to_add)
                feature.setAttributes(additional_attributes)

            feature.setGeometry(QgsGeometry.fromPointXY(point))
            features.append(feature)

        return features

    @classmethod
    def drop_z_from_geom(cls, geom: QgsGeometry, geom_type: QgsWkbTypes):
        target_type = cls._get_non_z_geom_type(geom_type)
        type_to_convert = QgsWkbTypes.geometryType(target_type)
        return geom.convertToType(type_to_convert), target_type

    @staticmethod
    def _get_non_z_geom_type(geom_type: QgsWkbTypes):
        if not QgsWkbTypes.hasZ(geom_type):
            return geom_type
        else:
            return QgsWkbTypes.dropZ(geom_type)
Exemple #19
0
    def checkLabeledLayerWithBlendModesCannotBeCached(self, job_type):
        """ any labeled layer utilising blending modes cannot be cached"""
        layer = QgsVectorLayer("Point?field=fldtxt:string", "layer1", "memory")

        layer.setCustomProperty("labeling", "pal")
        layer.setCustomProperty("labeling/enabled", True)
        layer.setCustomProperty("labeling/fieldName", "fldtxt")

        layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2",
                                "memory")
        layer2.setCustomProperty("labeling", "pal")
        layer2.setCustomProperty("labeling/enabled", True)
        layer2.setCustomProperty("labeling/fieldName", "fldtxt")
        layer2.setCustomProperty("labeling/blendMode", 5)

        settings = QgsMapSettings()
        settings.setExtent(QgsRectangle(5, 25, 25, 45))
        settings.setOutputSize(QSize(600, 400))
        settings.setLayers([layer, layer2])

        # with cache - cache should not be populated!
        cache = QgsMapRendererCache()
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        self.assertFalse(job.usedCachedLabels())
        self.assertFalse(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())

        # second job should also not be able to use label cache
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        # shouldn't use cache
        self.assertFalse(job.usedCachedLabels())
        # and results should not have been cached
        self.assertFalse(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())
Exemple #20
0
class LoadDisaggAsLayerDialog(LoadOutputAsLayerDialog):
    """
    Dialog to load disaggregation from an oq-engine output, as layer
    """

    def __init__(self, drive_engine_dlg, iface, viewer_dock, session, hostname,
                 calc_id, output_type='disagg', path=None, mode=None,
                 engine_version=None, calculation_mode=None):
        assert output_type == 'disagg'
        super().__init__(
            drive_engine_dlg, iface, viewer_dock, session, hostname,
            calc_id, output_type=output_type, path=path, mode=mode,
            engine_version=engine_version, calculation_mode=calculation_mode)
        self.disagg = None
        # self.setWindowTitle('Load disaggregation as layer')
        # self.populate_out_dep_widgets()
        # self.adjustSize()
        self.ok_button.setEnabled(True)
        log_msg('Extracting disagg. Watch progress in QGIS task bar',
                level='I', message_bar=self.iface.messageBar())
        self.extract_npz_task = ExtractNpzTask(
            'Extract disagg', QgsTask.CanCancel, self.session,
            self.hostname, self.calc_id, 'disagg_layer', self.finalize_init,
            self.on_extract_error)
        QgsApplication.taskManager().addTask(self.extract_npz_task)

    def finalize_init(self, extracted_disagg):
        self.disagg = extracted_disagg
        self.init_done.emit(self)
        if self.mode != 'testing':
            self.accept()

    def accept(self):
        with WaitCursorManager(
                'Extracting custom site ids...',
                message_bar=self.iface.messageBar()):
            log_msg('Extracting custom site ids', level='I',
                    print_to_stdout=True)
            sitecol = extract_npz(
                self.session, self.hostname, self.calc_id,
                'sitecol', message_bar=self.iface.messageBar())
            try:
                custom_site_ids = sitecol['array']['custom_site_id']
            except ValueError:
                custom_site_ids = sitecol['array']['sids']
                msg = ('Missing field "custom_site_id", needed by some '
                       'OQ-GeoViewer projects. Using "sids" instead.')
                log_msg(msg, level='W', print_to_stdout=True,
                        message_bar=self.iface.messageBar())
        with WaitCursorManager(
                'Creating disaggregation layer',
                self.iface.messageBar()):
            log_msg('Creating disagg_layer', level='I',
                    print_to_stdout=True)
            log_msg('Getting disagg array', level='I', print_to_stdout=True)
            disagg_array = self.disagg['array']
            lons = disagg_array['lon']
            lats = disagg_array['lat']
            self.layer = self.build_layer(
                self.disagg, disagg_array, lons, lats, custom_site_ids)
            if custom_site_ids is not None:
                self.build_custom_site_ids_layer(lons, lats, custom_site_ids)
            self.style_curves()

    def get_field_types(self, disagg_array):
        # field_types = {name: disagg['array'][name].dtype.char
        #                for name in disagg['array'].dtype.names}
        field_types = {}
        for field_name in disagg_array.dtype.names:
            field_type = disagg_array[field_name].dtype.char
            # FIXME: lists of floats are declared as 'f', but we need to store
            # them as strings
            if field_type == 'f' and field_name not in ('lon', 'lat'):
                field_type = 'S'
            field_types[field_name] = field_type
        return field_types

    def build_custom_site_ids_layer(self, lons, lats, custom_site_ids):
        layer_name = 'custom_site_ids_%s' % self.calc_id
        custom_site_id_layer = QgsVectorLayer(
            "%s?crs=epsg:4326" % 'point', layer_name, "memory")
        add_attribute('custom_site_id', 'I', custom_site_id_layer)
        custom_site_id_layer = self.read_custom_site_ids_into_layer(
            custom_site_id_layer, lons, lats, custom_site_ids)
        custom_site_id_layer.setCustomProperty(
            'output_type', '%s-%s' % (self.output_type, 'custom_site_ids'))
        if self.engine_version is not None:
            custom_site_id_layer.setCustomProperty(
                'engine_version', self.engine_version)
        irmt_version = get_irmt_version()
        custom_site_id_layer.setCustomProperty('irmt_version', irmt_version)
        custom_site_id_layer.setCustomProperty('calc_id', self.calc_id)
        if self.mode != 'testing':
            # NOTE: the following commented line would cause (unexpectedly)
            #       "QGIS died on signal 11" and double creation of some
            #       layers during integration tests
            QgsProject.instance().addMapLayer(custom_site_id_layer, False)
        tree_node = QgsProject.instance().layerTreeRoot()
        tree_node.insertLayer(0, custom_site_id_layer)
        # self.iface.setActiveLayer(custom_site_id_layer)
        log_msg('Layer %s was created successfully' % layer_name, level='S',
                message_bar=self.iface.messageBar(),
                print_to_stdout=True)

    def read_custom_site_ids_into_layer(
            self, custom_site_id_layer, lons, lats, custom_site_ids):
        with edit(custom_site_id_layer):
            feats = []
            for row_idx, row in enumerate(custom_site_ids):
                feat = QgsFeature(custom_site_id_layer.fields())
                value = int(custom_site_ids[row_idx])
                feat.setAttribute('custom_site_id', value)
                feat.setGeometry(QgsGeometry.fromPointXY(
                    QgsPointXY(lons[row_idx], lats[row_idx])))
                feats.append(feat)
            added_ok = custom_site_id_layer.addFeatures(feats)
            if not added_ok:
                msg = 'There was a problem adding features to the layer.'
                log_msg(msg, level='C', message_bar=self.iface.messageBar())
        return custom_site_id_layer

    def build_layer(self, disagg, disagg_array, lons, lats, custom_site_ids):
        log_msg('Done getting disagg array', level='I', print_to_stdout=True)
        layer_name = '%s_%s' % (self.output_type, self.calc_id)
        # log_msg('Getting field types', level='I', print_to_stdout=True)
        field_types = self.get_field_types(disagg_array)
        # NOTE: just "if custom_site_id:" would raise the following error:
        # ValueError: The truth value of an array with more than one
        # element is ambiguous. Use a.any() or a.all()
        if custom_site_ids is not None:
            field_types['custom_site_id'] = 'I'
        self.layer = QgsVectorLayer(
            "%s?crs=epsg:4326" % 'point', layer_name, "memory")
        modified_field_types = copy.copy(field_types)
        for field_name, field_type in field_types.items():
            if field_name in ['lon', 'lat']:
                continue
            # log_msg('Adding field %s of type %s' % (field_name, field_type),
            #         level='I', print_to_stdout=True)
            added_field_name = self.add_field_to_layer(field_name, field_type)
            # log_msg('\tDone adding field', level='I', print_to_stdout=True)
            if field_name != added_field_name:
                # replace field_name with the actual added_field_name
                del modified_field_types[field_name]
                modified_field_types[added_field_name] = field_type
        field_types = copy.copy(modified_field_types)

        self.layer = self.read_npz_into_layer(
            field_types, disagg_array, lons, lats, custom_site_ids)
        self.layer.setCustomProperty('output_type', self.output_type)
        if self.engine_version is not None:
            self.layer.setCustomProperty('engine_version', self.engine_version)
        irmt_version = get_irmt_version()
        self.layer.setCustomProperty('irmt_version', irmt_version)
        self.layer.setCustomProperty('calc_id', self.calc_id)
        self.layer.setCustomProperty(
            'investigation_time', self.oqparam['investigation_time'])
        disagg_params = {}
        for k, v in disagg.items():
            if k == 'array':
                continue
            v = [value.item().decode('utf8')
                 if isinstance(value.item(), bytes)
                 else value.item()
                 for value in v]
            self.layer.setCustomProperty(k, v)
            disagg_params[k] = v
        write_metadata_to_layer(
            self.drive_engine_dlg, self.output_type, self.layer,
            disagg_params=disagg_params)
        if self.mode != 'testing':
            # NOTE: the following commented line would cause (unexpectedly)
            #       "QGIS died on signal 11" and double creation of some
            #       layers during integration tests
            QgsProject.instance().addMapLayer(self.layer, False)
        tree_node = QgsProject.instance().layerTreeRoot()
        tree_node.insertLayer(0, self.layer)
        self.iface.setActiveLayer(self.layer)
        log_msg('Layer %s was created successfully' % layer_name, level='S',
                message_bar=self.iface.messageBar(),
                print_to_stdout=True)
        return self.layer

    def read_npz_into_layer(
            self, field_types, disagg_array, lons, lats, custom_site_ids):
        with edit(self.layer):
            feats = []
            # tot_feats = len(disagg_array)
            for row_idx, row in enumerate(disagg_array):
                # log_msg('Site %s of %s' % (row_idx, tot_feats), level='I',
                #         print_to_stdout=True)
                feat = QgsFeature(self.layer.fields())
                for field_name_idx, field_name in enumerate(field_types):
                    if field_name in ('lon', 'lat'):
                        continue
                    if field_name == 'custom_site_id':
                        value = int(custom_site_ids[row_idx])
                    elif isinstance(disagg_array[field_name][row_idx],
                                    np.ndarray):
                        value = disagg_array[field_name][row_idx]
                        # log_msg('\t\tDumping json', level='I',
                        #         print_to_stdout=True)
                        if (field_name.startswith('Dist-') and
                                np.any(value < 0)):
                            log_msg('Negative values were found for field %s:'
                                    ' %s' % (field_name, value),
                                    level='I', print_to_stdout=True)
                        value = json.dumps(value.tolist())
                        # log_msg('\t\tDone dumping json', level='I',
                        #         print_to_stdout=True)
                    else:  # scalar
                        value = disagg_array[field_name][row_idx].item()
                    feat.setAttribute(field_name, value)
                feat.setGeometry(QgsGeometry.fromPointXY(
                    QgsPointXY(lons[row_idx], lats[row_idx])))
                feats.append(feat)
                # log_msg('\tAdding feature', level='I',
                #         print_to_stdout=True)
                # added_ok = self.layer.addFeature(feat)
                # log_msg('\t\tDone adding feature', level='I',
                #         print_to_stdout=True)
                # if not added_ok:
                #     msg = 'There was a problem adding features to the layer.'
                #     log_msg(msg, level='C',
                #             message_bar=self.iface.messageBar(),
                #             print_to_stderr=True)
            added_ok = self.layer.addFeatures(feats)
            if not added_ok:
                msg = 'There was a problem adding features to the layer.'
                log_msg(msg, level='C', message_bar=self.iface.messageBar())
        return self.layer
Exemple #21
0
    def json2features(self, url, data_layer=None):
        try:
            (response, content) = self.nam.request(url, method="GET")
        except RequestsException as e:
            self.info('ERROR: {}'.format(e))
            return

        jsons = content.decode('utf-8')
        jsono = json.loads(jsons)

        #print(json.dumps(jsono, sort_keys=True, indent=4))
        #print(jsono['height'])

        # creating memory layer with uri:
        # https://qgis.org/api/qgsmemoryproviderutils_8cpp_source.html
        if data_layer is None:
            data_layer = QgsVectorLayer('none', 'DHIS2 data', 'memory')

        fields = QgsFields()
        fields.append(QgsField('id', QVariant.String))
        fields.append(QgsField('name', QVariant.String))

        metadata_items = jsono['metaData']['items']

        # create as much fields as there are pe_dx combinations
        # eg: 2017_birth, 2016_birth, 2017_measels, 2016_measels
        for pe in jsono['metaData']['dimensions']['pe']:
            for dx in jsono['metaData']['dimensions']['dx']:
                #field_alias = '{} {} ({})'.format(pe, metadata_items[dx]['name'], dx)
                field_alias = '{} {}'.format(pe, metadata_items[dx]['name'])
                field = QgsField('{}_{}'.format(pe, dx), QVariant.Double, comment=field_alias)
                field.setAlias(field_alias)
                fields.append(field)

        #self.info('Fields: {}'.format(fields))

        # clean up first
        data_layer.dataProvider().deleteAttributes(data_layer.dataProvider().attributeIndexes())
        data_layer.updateFields()

        # set new attributes
        data_layer.dataProvider().addAttributes(fields)
        data_layer.updateFields()

        # array with all features
        features = []
        # map which maps the every feature to its OrganisationalUnit
        feature_map = {}
        # create a feature for every ou/OrganisationUnit
        # AND make sure it has the pe_dx combination fields
        for ou in jsono['metaData']['dimensions']['ou']:
            #print(ou)
            f = QgsFeature()
            # set fields
            f.setFields(fields)
            # set id and name of the feature
            f.setAttribute('id', ou)
            f.setAttribute('name', jsono['metaData']['items'][ou]['name'])
            # append feature to features array and feature map
            features.append(f)
            feature_map[ou] = f

        # dynamic?
        # currently the order in which they are in the url is below
        dx_idx = 0
        pe_idx = 1
        ou_idx = 2
        value_idx = 3

        # now every cell in the table has a 'row in the json data', with dx, pe, ou and value
        for row in jsono['rows']:
            # pick feature based on OrganisationalUnit-key from the feature_map
            f = feature_map[row[ou_idx]]
            # attribute key is created from pe_dx string
            attr = '{}_{}'.format(row[pe_idx], row[dx_idx])
            # Births attended by skilled health personnel (estimated pregancies)
            f.setAttribute(attr, row[value_idx])

        data_layer.dataProvider().addFeatures(features)

        # add it to the project
        QgsProject.instance().addMapLayer(data_layer)
        # 'save' the data url of the layer into the project properties
        # https://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/settings.html
        data_layer.setCustomProperty("dhis2_url", url)
Exemple #22
0
def points_along_line(layerout,
                      startpoint,
                      endpoint,
                      distance,
                      label,
                      layer,
                      selected_only=True,
                      force=False,
                      divide=0):
    """Adding Points along the line
    """
    # Create a new memory layer and add a distance attribute self.layerNameLine
    #layer_crs = virt_layer.setCrs(layer.crs())
    virt_layer = QgsVectorLayer("Point?crs=%s" % layer.crs().authid(),
                                layerout, "memory")
    provider = virt_layer.dataProvider()
    virt_layer.startEditing()  # actually writes attributes
    units = layer.crs().mapUnits()
    unit_dic = {
        QGis.Degrees: 'Degrees',
        QGis.Meters: 'Meters',
        QGis.Feet: 'Feet',
        QGis.UnknownUnit: 'Unknown'
    }
    unit = unit_dic.get(units, 'Unknown')
    provider.addAttributes([QgsField("fid", QVariant.Int)])
    provider.addAttributes([QgsField("cng_(" + unit + ")", QVariant.Int)])

    def get_features():
        """Getting the features
        """
        if selected_only:
            return layer.selectedFeatures()
        else:
            return layer.getFeatures()

    # Loop through all (selected) features
    for feature in get_features():
        geom = feature.geometry()
        # Add feature ID of selected feature
        fid = feature.id()
        if not geom:
            QgsMessageLog.logMessage("No geometry", "QChainage")
            continue

        features = create_points_at(startpoint, endpoint, distance, geom, fid,
                                    force, divide)
        provider.addFeatures(features)
        virt_layer.updateExtents()

    QgsMapLayerRegistry.instance().addMapLayers([virt_layer])
    virt_layer.commitChanges()
    virt_layer.reload()

    #from here Add labeling
    #generic labeling properties
    if label:
        virt_layer.setCustomProperty("labeling", "pal")
        virt_layer.setCustomProperty("labeling/enabled", "true")
        virt_layer.setCustomProperty("labeling/fieldName",
                                     "cng_(" + unit + ")")
        virt_layer.setCustomProperty("labeling/fontSize", "10")
        virt_layer.setCustomProperty("labeling/multiLineLabels", "true")

        #virt_layer.setCustomProperty("labeling/Size", "5")
    # symbol = QgsMarkerSymbolV2.createSimple({"name": "capital"})
    # virt_layer.setRendererV2(QgsSingleSymbolRendererV2(symbol))
    virt_layer.triggerRepaint()
    return
class GoogleDriveLayer(QObject):
    """ Pretend we are a data provider """

    invalidEdit = pyqtSignal()
    deferredEdit = pyqtSignal()
    dirty = False
    restyled = False
    doing_attr_update = False
    geom_types = ("Point", "LineString", "Polygon","Unknown","NoGeometry")

    def __init__(self, parent, authorization, layer_name, spreadsheet_id = None, loading_layer = None, importing_layer = None, crs_def = None, geom_type = None, test = None, precision=17):
        '''
        Initialize the layer by reading the Google drive sheet, creating a memory
        layer, and adding records to it, optionally used fo layer export to google drive
        :param parent:
        :param authorization: google authorization object
        :param layer_name: the layer name
        :param spreadsheet_id: the spreadsheetId of the table to download and load as qgis layer; default to None
        :param loading_layer: the layer loading from project file; default to None
        :param importing_layer: the layer that is being imported; default to None
        :param test: used for testing
        '''

        super(GoogleDriveLayer, self).__init__()
        # Save the path to the file soe we can update it in response to edits
        self.test = test
        self.parent = parent
        self.iface = parent.iface
        self.precision = None
        bar = progressBar(self, 'loading google drive layer')
        self.service_drive = service_drive(authorization)
        self.client_id = authorization.client_id
        self.authorization = authorization
        if spreadsheet_id:
            self.spreadsheet_id = spreadsheet_id
            self.service_sheet = service_spreadsheet(authorization, self.spreadsheet_id)
            self.precision = self.service_sheet.precision()
        elif importing_layer:
            self.precision = precision
            layer_as_list = self.qgis_layer_to_list(importing_layer)
            logger("1")
            self.service_sheet = service_spreadsheet(authorization, new_sheet_name=importing_layer.name(),new_sheet_data=True)
            self.spreadsheet_id = self.service_sheet.spreadsheetId
            logger("2")
            self.service_sheet.set_crs(importing_layer.crs().authid())
            self.service_sheet.set_geom_type(self.geom_types[importing_layer.geometryType()])
            self.service_sheet.set_style_qgis(self.layer_style_to_xml(importing_layer))
            self.service_sheet.set_style_sld(self.SLD_to_xml(importing_layer))
            self.service_sheet.set_style_mapbox(self.layer_style_to_json(importing_layer))
            self.service_sheet.set_precision(precision)
            self.dirty = True
            self.saveFieldTypes(importing_layer.fields())  
            logger("5")
            self.service_sheet.upload_rows(layer_as_list)
            logger("6")

        self.service_sheet.lockedEntry.connect(self.rollbackRow)

        self.reader = self.service_sheet.get_sheet_values()
        self.header = self.reader[0]
        self.service_sheet.update_header()

        self.crs_def = self.service_sheet.crs()
        self.geom_type = self.service_sheet.geom_type()
        logger("LOADED GOOGLE SHEET LAYER: %s CRS_ID:%s GEOM_type:%s" % (self.service_sheet.name,self.crs_def, self.geom_type))
        # Build up the URI needed to create memory layer
        if loading_layer:
            self.lyr = loading_layer
            attrIds = [i for i in range (0, self.lyr.fields().count())]
            self.lyr.dataProvider().deleteAttributes(attrIds)
            self.lyr.updateFields()
        else:
            self.uri = self.uri = "Multi%s?crs=%s&index=yes" % (self.geom_type, self.crs_def)
            #logger(self.uri)
            self.lyr = QgsVectorLayer(self.uri, layer_name, 'memory')

        if importing_layer:
            logger("3")
            self.update_summary_sheet() 
            logger("4")
            self.saveMetadataState(importing_layer)

        '''
        #check if public layer
        meta = self.service_drive.getFileMetadata(self.spreadsheet_id)
        permissions = meta["metadata"]["permissions"]
        self.is_public_layer = False
        for permission in permissions:
            if permission["id"] == "anyoneWithLink":
                self.is_public_layer = True
                logger("is public layer")
        '''

        fields_types = self.service_sheet.get_line("ROWS", 1, sheet="settings")
        attributes = []
        for i in range(2,len(self.header)):
            if self.header[i][:8] != 'DELETED_':
                type_pack = fields_types[i].split("|")
                attributes.append(QgsField(name=self.header[i],type=int(type_pack[0]), len=int(type_pack[1]), prec=int(type_pack[2])))
                #self.uri += u'&field={}:{}'.format(fld.decode('utf8'), field_name_types[fld])
        self.lyr.dataProvider().addAttributes(attributes)
        self.lyr.updateFields()

        self.xml_to_layer_style(self.lyr,self.service_sheet.style())
        #disable memory layers save checking when closing project
        self.lyr.setCustomProperty("googleDriveId", self.spreadsheet_id)
        self.lyr.setCustomProperty("skipMemoryLayersCheck", 1)

        self.add_records()

        # Make connections
        self.makeConnections(self.lyr)

        # Add the layer the map
        if not loading_layer:
            QgsProject.instance().addMapLayer(self.lyr)
        
        self.lyr.setAbstract(self.service_sheet.abstract())
        self.lyr.gdrive_control = self

        bar.stop("Layer %s succesfully loaded" % layer_name)

    def makeConnections(self,lyr):
        '''
        The method handle default signal connections to the connected qgis memory layer
        :param lyr: qgis layer
        :return:
        '''
        self.deferredEdit.connect(self.apply_locks)
        lyr.editingStarted.connect(self.editing_started)
        lyr.editingStopped.connect(self.editing_stopped)
        lyr.committedAttributesDeleted.connect(self.attributes_deleted)
        lyr.committedAttributesAdded .connect(self.attributes_added)
        lyr.committedFeaturesAdded.connect(self.features_added)
        lyr.committedGeometriesChanges.connect(self.geometry_changed)
        lyr.committedAttributeValuesChanges.connect(self.attributes_changed)
        lyr.destroyed.connect(self.unsubscribe)
        lyr.beforeCommitChanges.connect(self.inspect_changes)
        lyr.styleChanged.connect(self.style_changed)
        #add contextual menu
        self.sync_with_google_drive_action = QAction(QIcon(os.path.join(self.parent.plugin_dir,'sync.png')), "Sync with Google drive", self.iface )
        self.iface.addCustomActionForLayerType(self.sync_with_google_drive_action, "", QgsMapLayer.VectorLayer, allLayers=False)
        self.iface.addCustomActionForLayer(self.sync_with_google_drive_action, lyr)
        self.sync_with_google_drive_action.triggered.connect(self.sync_with_google_drive)

        lyr.gDriveInterface = self

    def add_records(self):
        '''
        Add records to the memory layer by reading the Google Sheet
        '''
        self.lyr.startEditing()

        for i, row in enumerate(self.reader[1:]):
            flds = collections.OrderedDict(list(zip(self.header, row)))
            status = flds.pop('STATUS')

            if status != 'D': #non caricare i deleted
                wkt_geom = unpack(flds.pop('WKTGEOMETRY'))
                #fid = int(flds.pop('FEATUREID'))
                feature = QgsFeature()
                geometry = QgsGeometry.fromWkt(wkt_geom)
                feature.setGeometry(geometry)
                cleared_row = [] #[fid]
                for field, attribute in flds.items():
                    if field[:8] != 'DELETED_': #skip deleted fields
                        if attribute == '()':
                            cleared_row.append(qgis.core.NULL)
                        else:
                            cleared_row.append(attribute)
                    else:
                        logger( "DELETED " + field)
                feature.setAttributes(cleared_row)
                self.lyr.addFeature(feature)
        self.lyr.commitChanges()

    def saveMetadataState(self,lyr=None,metadata=None):
        logger ("metadata changed")
        if not metadata:
            metadata = self.get_layer_metadata(lyr)
        self.service_sheet.update_metadata(self.spreadsheet_id, metadata)

    def style_changed(self):
        '''
        landing method for rendererChanged signal. It stores xml qgis style definition to the setting sheet
        '''
        logger( "style changed")
        self.dirty = True
        self.restyled = True

    def renew_connection(self):
        '''
        when connection stay alive too long we have to rebuild service
        '''
        self.service_drive.renew_connection()

    def sync_with_google_drive(self):
        self.renew_connection()
        self.update_from_subscription()
        self.update_summary_sheet()

    def update_from_subscription(self):
        '''
        The method updates qgis memory layer with changes made by other users and sincronize the local qgis layer with google sheet spreadsheet
        '''
        self.renew_connection()
        bar = progressBar(self, 'updating local layer from remote')
        # fix_print_with_import
        if self.service_sheet.canEdit:
            updates = self.service_sheet.get_line('COLUMNS','A', sheet=self.client_id)
            if updates:
                self.service_sheet.erase_cells(self.client_id)
        else:
            new_changes_log_rows = self.service_sheet.get_line("COLUMNS",'A',sheet="changes_log")
            if len(new_changes_log_rows) > len(self.service_sheet.changes_log_rows):
                updates = new_changes_log_rows[-len(new_changes_log_rows)+len(self.service_sheet.changes_log_rows):]
                self.service_sheet.changes_log_rows = new_changes_log_rows
            else:
                updates = []
        # fix_print_with_import
        for update in updates:
            decode_update = update.split("|")
            if decode_update[0] in ('new_feature', 'delete_feature', 'update_geometry', 'update_attributes'):
                sheet_feature = self.service_sheet.get_line('ROWS',decode_update[1])
                if decode_update[0] == 'new_feature':
                    feat = QgsFeature()
                    geom = QgsGeometry().fromWkt(unpack(sheet_feature[0]))
                    feat.setGeometry(geom)
                    feat.setAttributes(sheet_feature[2:])
                    logger(( "updating from subscription, new_feature: " + str(self.lyr.dataProvider().addFeatures([feat]))))
                else:
                    sheet_feature_id = decode_update[1]
                    feat = next(self.lyr.getFeatures(QgsFeatureRequest(QgsExpression(' "FEATUREID" = %s' % sheet_feature_id))))
                    if   decode_update[0] == 'delete_feature':
                        # fix_print_with_import
                        logger("updating from subscription, delete_feature: " + str(self.lyr.dataProvider().deleteFeatures([feat.id()])))
                    elif decode_update[0] == 'update_geometry':
                        update_set = {feat.id(): QgsGeometry().fromWkt(unpack(sheet_feature[0]))}
                        # fix_print_with_import
                        logger("updating from subscription, update_geometry: " + str(self.lyr.dataProvider().changeGeometryValues(update_set)))
                    elif decode_update[0] == 'update_attributes':
                        new_attributes = sheet_feature_id[2:]
                        attributes_map = {}
                        for i in range(0, len(new_attributes)):
                            attributes_map[i] = new_attributes[i]
                        update_map = {feat.id(): attributes_map,}
                        # fix_print_with_import
                        logger("updating from subscription, update_attributes: " +(self.lyr.dataProvider().changeAttributeValues(update_map)))
            elif decode_update[0] == 'add_field':
                field_a1_notation = self.service_sheet.header_map[decode_update[1]]
                type_def = self.service_sheet.sheet_cell('settings!%s1' % field_a1_notation)
                type_def_decoded = type_def.split("|")
                new_field = QgsField(name=decode_update[1],type=int(type_def_decoded[0]), len=int(type_def_decoded[1]), prec=int(type_def_decoded[2]))
                # fix_print_with_import
                logger("updating from subscription, add_field: ", + (self.lyr.dataProvider().addAttributes([new_field])))
                self.lyr.updateFields()
            elif decode_update[0] == 'delete_field':
                # fix_print_with_import
                logger("updating from subscription, delete_field: " + str(self.lyr.dataProvider().deleteAttributes([self.lyr.dataProvider().fields().fieldNameIndex(decode_update[1])])))
                self.lyr.updateFields()
        self.lyr.triggerRepaint()
        bar.stop("local layer updated")

    def editing_started(self):
        '''
        Connect to the edit buffer so we can capture geometry and attribute
        changes
        '''
        # fix_print_with_import
        logger("editing")
        self.update_from_subscription()
        self.bar = None
        if self.service_sheet.canEdit:
            self.activeThreads = 0
            self.editing = True
            self.lyr.geometryChanged.connect(self.buffer_geometry_changed)
            self.lyr.attributeValueChanged.connect(self.buffer_attributes_changed)
            self.lyr.beforeCommitChanges.connect(self.catch_deleted)
            self.lyr.beforeRollBack.connect(self.rollBack)
            self.invalidEdit.connect(self.rollBack)
            self.changes_log=[]
            self.locking_queue = []
            self.timer = 0
        else: #refuse editing if file is read only
            self.lyr.rollBack()

    def buffer_geometry_changed(self,fid,geom):
        '''
        Landing method for geometryChanged signal.
        When a geometry is modified, the row related to the modified feature is marked as modified by local user.
        Further edits to the modified feature are denied to other concurrent users
        :param fid:
        :param geom:
        '''
        if self.editing:
            self.lock_feature(fid)

    def buffer_attributes_changed(self,fid,attr_id,value):
        '''
        Landing method for attributeValueChanged signal.
        When an attribute is modified, the row related to the modified feature is marked as modified by local user.
        Further edits to the modified feature are denied to other concurrent users
        :param fid:
        :param attr_id:
        :param value:
        '''
        if self.editing:
            self.lock_feature(fid)


    def lock_feature(self, fid):
        """
        The row in google sheet linked to feature that has been modified is locked
        Filling the the STATUS column with the client_id.
        Further edits to the modified feature are denied to other concurrent users
        """
        if fid >= 0: # fid <0 means that the change relates to newly created features not yet present in the sheet
            self.locks_applied = None
            feature_locking = next(self.lyr.getFeatures(QgsFeatureRequest(fid)))
            locking_row_id = feature_locking[0]
            self.locking_queue.append(locking_row_id)
            _thread.start_new_thread(self.deferred_apply_locks, ())


    def deferred_apply_locks(self):
        if self.timer > 0:
            self.timer = 0
            return
        else:
            while self.timer < 100:
                self.timer += 1
                sleep(0.01)
            #APPLY_LOCKS
            self.deferredEdit.emit()
            #self.apply_locks()

    def apply_locks(self):
        if self.locks_applied:
            return
        self.locks_applied = True
        status_range = []
        for row_id in self.locking_queue:
            status_range.append(['STATUS', row_id])
        status_control = self.service_sheet.multicell(status_range)
        if "valueRanges" in status_control:
            mods = []
            for valueRange in status_control["valueRanges"]:
                if valueRange["values"][0][0] in ('()', None):
                    mods.append([valueRange["range"],0,self.client_id])
                    row_id = valueRange["range"].split('B')[-1]
            if mods:
                self.service_sheet.set_multicell(mods, A1notation=True)
        self.locking_queue = []
        self.timer = 0

    def rollbackRow(self,row):
        logger(str(row))
        rollback_row = self.service_sheet.get_line('ROWS',row)
        #print ("Original row", rollback_row)
        wkt_geom = unpack(rollback_row[0])
        attrs = {}
        for attr in range(2,len(rollback_row)):
            attrs[attr-2] = rollback_row[attr]

        try:
            #identify feature to rollback
            feat = next(self.lyr.getFeatures(QgsFeatureRequest(QgsExpression(' "FEATUREID" = %s' % rollback_row[2]))))
            geom_update = {feat.id(): QgsGeometry.fromWkt(wkt_geom)}
            #rollback geometry
            self.lyr.dataProvider().changeGeometryValues(geom_update)
            #rollback attributes
            attrs_update = {feat.id():attrs}
            #print ("UPDATES", geom_update, attrs_update)
            self.lyr.dataProvider().changeFeatures(attrs_update, geom_update)
            
        except:
            logger("RESTORING DELETED FEATURE")
            #rollback deleted feat
            restore_feat = QgsFeature(self.lyr.fields())
            restore_feat.setGeometry(QgsGeometry.fromWkt(wkt_geom))
            for key,attr in attrs.items():
                logger(str(key)+str(attr))
                restore_feat.setAttribute(key, attr)
            self.lyr.dataProvider().addFeatures([restore_feat])
            
        self.lyr.triggerRepaint()
        message = self.iface.messageBar().createMessage("GooGIS plugin:","Feature %d is locked by %s: pending edits not applied" % (rollback_row[2], rollback_row[1]))
        self.iface.messageBar().pushWidget(message, Qgis.Warning, 5)


    def rollBack(self):
        """
        before rollback changes status field is cleared and the edits from concurrent user are allowed
        """
        # fix_print_with_import
        logger("ROLLBACK")
        try:
            self.lyr.geometryChanged.disconnect(self.buffer_geometry_changed)
        except:
            pass
        try:
            self.lyr.attributeValueChanged.disconnect(self.buffer_attributes_changed)
        except:
            pass
        self.renew_connection()
        self.clean_status_row()
        try:
            self.lyr.beforeRollBack.disconnect(self.rollBack)
        except:
            pass

        #self.lyr.geometryChanged.disconnect(self.buffer_geometry_changed)
        #self.lyr.attributeValueChanged.disconnect(self.buffer_attributes_changed)
        self.editing = False

    def editing_stopped(self):
        """
        Update the remote sheet if changes were committed
        """
        # fix_print_with_import
        logger("EDITING_STOPPED")
        self.renew_connection()
        self.clean_status_row()
        if self.service_sheet.canEdit:
            self.service_sheet.advertise(self.changes_log)
        self.editing = False
        #if self.dirty:
        #    self.update_summary_sheet()
        #    self.dirty = None
        if self.bar:
            self.bar.stop("update to remote finished")

    def inspect_changes(self):
        '''
        here we can inspect changes before commit them
        self.deleted_list = []
        for deleted in self.lyr.editBuffer().deletedAttributeIds():
            self.deleted_list.append(self.lyr.fields().at(deleted).name())
        print self.deleted_list

        logger("attributes_added")
        for field in self.lyr.editBuffer().addedAttributes():
            print "ADDED FIELD", field.name()
            self.service_sheet.add_column([field.name()], fill_with_null = True)
        '''
        # fix_print_with_import
        logger("INSPECT_CHANGES")
        pass

    def attributes_added(self, layer, added):
        """
        Landing method for attributeAdded.
        Fields (attribute) changed
        New colums are appended to the google drive spreadsheets creating remote colums syncronized with the local layer fields.
        Edits are advertized to other concurrent users for subsequent syncronization with remote table
        """
        logger("attributes_added")
        for field in added:
            logger( "ADDED FIELD %s" % field.name())
            self.service_sheet.add_column([field.name()], fill_with_null = True)
            self.service_sheet.add_column(["%d|%d|%d" % (field.type(), field.length(), field.precision())],child_sheet="settings", fill_with_null = None)
            self.changes_log.append('%s|%s' % ('add_field', field.name()))
        self.dirty = True

    def attributes_deleted(self, layer, deleted_ids):
        """
        Landing method for attributeDeleted.
        Fields (attribute) are deleted
        New colums are marked as deleted in the google drive spreadsheets.
        Edits are advertized to other concurrent users for subsequent syncronization with remote table
        """
        logger("attributes_deleted")
        for deleted in deleted_ids:
            deleted_name = self.service_sheet.mark_field_as_deleted(deleted)
            self.changes_log.append('%s|%s' % ('delete_field', deleted_name))
        self.dirty = True


    def features_added(self, layer, features):
        """
        Landing method for featureAdded.
        The new features are written adding rows to the google drive spreadsheets .
        Edits are advertized to other concurrent users for subsequent syncronization with remote table
        """
        logger("features added")

        for count,feature in enumerate(features):
            new_fid = self.service_sheet.new_fid()
            self.lyr.dataProvider().changeAttributeValues({feature.id() : {0: new_fid}})
            feature.setAttribute(0, new_fid+count)
            new_row_dict = {}.fromkeys(self.service_sheet.header,'()')
            new_row_dict['WKTGEOMETRY'] = pack(feature.geometry().asWkt(precision=self.precision))
            new_row_dict['STATUS'] = '()'
            for i,item in enumerate(feature.attributes()):
                fieldName = self.lyr.fields().at(i).name()
                try:
                    new_row_dict[fieldName] = item.toString(format = Qt.ISODate)
                except:
                    if not item or item == qgis.core.NULL:
                        new_row_dict[fieldName] = '()'
                    else:
                        new_row_dict[fieldName] = item
            new_row_dict['FEATUREID'] = '=ROW()' #assure correspondance between feature and sheet row
            result = self.service_sheet.add_row(new_row_dict)
            sheet_new_row = int(result['updates']['updatedRange'].split('!A')[1].split(':')[0])
            self.changes_log.append('%s|%s' % ('new_feature', str(new_fid)))
        self.dirty = True

    def catch_deleted(self):
        """
        Landing method for beforeCommitChanges signal.
        The method intercepts edits before they were written to the layer so from deleted features
        can be extracted the feature id of the google drive spreadsheet related rows.
        The affected rows are marked as deleted and hidden away from the layer syncronization
        """
        self.bar = progressBar(self, 'updating local edits to remote')
        """ Features removed; but before commit """
        deleted_ids = self.lyr.editBuffer().deletedFeatureIds()
        if deleted_ids:
            deleted_mods = []
            for fid in deleted_ids:
                removed_feat = next(self.lyr.dataProvider().getFeatures(QgsFeatureRequest(fid)))
                removed_row = removed_feat[0]
                logger ("Deleting FEATUREID %s" % removed_row)
                deleted_mods.append(("STATUS",removed_row,'D'))
                self.changes_log.append('%s|%s' % ('delete_feature', str(removed_row)))
            if deleted_mods:
                self.service_sheet.set_protected_multicell(deleted_mods)
            self.dirty = True

    def geometry_changed(self, layer, geom_map):
        """
        Landing method for geometryChange signal.
        Features geometries changed
        The edited geometry, not locked by other users, are written to the google drive spreadsheets modifying the related rows.
        the WKT geometry definition is zipped and then base64 encoded for a compact storage
        (sigle cells string contents can't be larger the 50000 bytes)
        Edits are advertized to other concurrent users for subsequent syncronization with remote table
        """
        geometry_mod = []
        for fid,geom in geom_map.items():
            feature_changing = next(self.lyr.getFeatures(QgsFeatureRequest(fid)))
            row_id = feature_changing[0]
            wkt = geom.asWkt(precision=self.precision)
            geometry_mod.append(('WKTGEOMETRY',row_id, pack(wkt) ))
            logger ("Updated FEATUREID %s geometry" % row_id)
            self.changes_log.append('%s|%s' % ('update_geometry', str(row_id)))

        value_mods_result = self.service_sheet.set_protected_multicell(geometry_mod, lockBy=self.client_id)
        self.dirty = True

    def attributes_changed(self, layer, changes):
        """
        Landing method for attributeChange.
        Attribute values changed
        Edited feature, not locked by other users, are written to the google drive spreadsheets modifying the related rows.
        Edits are advertized to other concurrent users for subsequent syncronization with remote table
        """
        if not self.doing_attr_update:
            #print "changes",changes
            attribute_mods = []
            for fid,attrib_change in changes.items():
                feature_changing = next(self.lyr.getFeatures(QgsFeatureRequest(fid)))
                row_id = feature_changing[0]
                logger ( "Attribute changing FEATUREID: %s" % row_id)
                for attrib_idx, new_value in attrib_change.items():
                    fieldName = QgsProject.instance().mapLayer(layer).fields().field(attrib_idx).name()
                    if fieldName == 'FEATUREID':
                        logger("can't modify FEATUREID")
                        continue
                    try:
                        cleaned_value = new_value.toString(format = Qt.ISODate)
                    except:
                        if not new_value or new_value == qgis.core.NULL:
                            cleaned_value = '()'
                        else:
                            cleaned_value = new_value
                    attribute_mods.append((fieldName,row_id, cleaned_value))
                self.changes_log.append('%s|%s' % ('update_attributes', str(row_id)))

            if attribute_mods:
                attribute_mods_result = self.service_sheet.set_protected_multicell(attribute_mods, lockBy=self.client_id)
                print (attribute_mods)
                print (attribute_mods_result)
            self.dirty = True

    def clean_status_row(self):
        status_line = self.service_sheet.get_line("COLUMNS","B")
        clean_status_mods = []
        for row_line, row_value in enumerate(status_line):
            if row_value == self.client_id:
                clean_status_mods.append(("STATUS",row_line+1,'()'))
        value_mods_result = self.service_sheet.set_multicell(clean_status_mods)
        return value_mods_result

    def unsubscribe(self):
        '''
        When a read/write layer is removed from the legend the remote subscription sheet is removed and update summary sheet if dirty
        '''
        self.renew_connection()
        self.service_sheet.unsubscribe()

    def qgis_layer_to_csv(self,qgis_layer):
        '''
        method to transform the specified qgis layer in a csv object for uploading
        :param qgis_layer:
        :return: csv object
        '''
        stream = io.BytesIO()
        writer = csv.writer(stream, delimiter=',', quotechar='"', lineterminator='\n')
        row = ["WKTGEOMETRY","FEATUREID","STATUS"]
        for feat in qgis_layer.getFeatures():
            for field in feat.fields().toList():
                row.append(field.name().encode("utf-8"))
            break
        writer.writerow(row)
        for feat in qgis_layer.getFeatures():
            row = [pack(feat.geometry().asWkt(precision=self.precision)),feat.id(),"()"]
            for field in feat.fields().toList():
                if feat[field.name()] == qgis.core.NULL:
                    content = "()"
                else:
                    if type(feat[field.name()]) == str:
                        content = feat[field.name()].encode("utf-8")
                    else:
                        content = feat[field.name()]
                row.append(content)
            writer.writerow(row)
        stream.seek(0)
        #csv.reader(stream, delimiter=',', quotechar='"', lineterminator='\n')
        return stream

    def qgis_layer_to_list(self,qgis_layer):
        '''
        method to transform the specified qgis layer in list of rows (field/value) dicts for uploading
        :param qgis_layer:
        :return: row list object
        '''
        row = ["WKTGEOMETRY","STATUS","FEATUREID"]
        for feat in qgis_layer.getFeatures():
            for field in feat.fields().toList():
                row.append(field.name())
                #row.append(str(field.name()).encode("utf-8"))# slugify(field.name())
            break
        rows = [row]
        for feat in qgis_layer.getFeatures():
            row = [pack(feat.geometry().asWkt(precision=self.precision)),"()","=ROW()"] # =ROW() perfect row/featureid correspondance
            if len(row[0]) > 50000: # ignore features with geometry > 50000 bytes zipped
                continue
            for field in feat.fields().toList():
                if feat[field.name()] == qgis.core.NULL:
                    content = "()"
                else:
                    if type(feat[field.name()]) == str:
                        content = feat[field.name()] #feat[field.name()].encode("utf-8")
                    elif field.typeName() in ('Date', 'Time'):
                        content = feat[field.name()].toString(format = Qt.ISODate)
                    else:
                        content = feat[field.name()]
                row.append(content)
            rows.append(row)
        #csv.reader(stream, delimiter=',', quotechar='"', lineterminator='\n')
        return rows

    def saveFieldTypes(self,fields):
        '''
        writes the layer field types to the setting sheet
        :param fields:
        :return:
        '''
        types_array = ["s1","s2","4|4|0"] #default featureId type to longint
        for field in fields.toList():
            types_array.append("%d|%d|%d" % (field.type(), field.length(), field.precision()))
        # fix_print_with_import
        self.service_sheet.update_cells('settings!A1',types_array)

    def layer_style_to_xml(self,qgis_layer):
        '''
        saves qgis style to the setting sheet
        :param qgis_layer:
        :return:
        '''
        XMLDocument = QDomDocument("qgis_style")
        XMLStyleNode = XMLDocument.createElement("style")
        XMLDocument.appendChild(XMLStyleNode)
        error = None
        rw_context = QgsReadWriteContext()
        rw_context.setPathResolver( QgsProject.instance().pathResolver() )
        qgis_layer.writeSymbology(XMLStyleNode, XMLDocument, error,rw_context)
        xmldoc = XMLDocument.toString(1)
        return xmldoc

    def SLD_to_xml(self,qgis_layer):
        '''
        saves SLD style to the setting sheet. Not used, keeped here for further extensions.
        :param qgis_layer:
        :return:
        '''
        XMLDocument = QDomDocument("sld_style")
        error = None
        qgis_layer.exportSldStyle(XMLDocument, error)
        xmldoc = XMLDocument.toString(1)
        return xmldoc

    def xml_to_layer_style(self,qgis_layer,xml):
        '''
        retrieve qgis style from the setting sheet
        :param qgis_layer:
        :return:
        '''
        XMLDocument = QDomDocument()
        error = None
        XMLDocument.setContent(xml)
        XMLStyleNode = XMLDocument.namedItem("style") 
        rw_context = QgsReadWriteContext()
        rw_context.setPathResolver( QgsProject.instance().pathResolver() )
        qgis_layer.readSymbology(XMLStyleNode, error, rw_context)

    def layer_style_to_json(self, qgis_layer):

        #old_mapbox_style = toMapboxgl([qgis_layer])
        mapbox_style,icons,warnings = layerStyleAsMapbox(qgis_layer)
        if warnings:
            logger("Warning exporting to mapboxgl style: " + json.dumps(warnings))
        else:
            logger("mapboxgl style ok")
        return mapbox_style

    def get_gdrive_id(self):
        '''
        returns spreadsheet_id associated with layer
        :return: spreadsheet_id associated with layer
        '''
        return self.spreadsheet_id

    def get_service_drive(self):
        '''
        returns the google drive wrapper object associated with layer
        :return: google drive wrapper object
        '''
        return self.service_drive

    def get_service_sheet(self):
        '''
        returns the google spreadsheet wrapper object associated with layer
        :return: google spreadsheet wrapper object
        '''
        return self.service_sheet

    def wgs84_extent(self,extent):
        llp = self.transformToWGS84(QgsPointXY(extent.xMinimum(),extent.yMinimum()))
        rtp = self.transformToWGS84(QgsPointXY(extent.xMaximum(),extent.yMaximum()))
        return QgsRectangle(llp,rtp)

    def transformToWGS84(self, pPoint):
        crsDest = QgsCoordinateReferenceSystem(4326)  # WGS 84
        xform = QgsCoordinateTransform(self.lyr.crs(), crsDest, QgsProject.instance())
        return xform.transform(pPoint) # forward transformation: src -> dest

    def get_layer_metadata(self,lyr=None):
        '''
        builds a metadata dict of the current layer to be stored in summary sheet
        '''

        if not lyr:
            lyr = self.lyr

        #fields = collections.OrderedDict()
        fields = ""
        for field in lyr.fields().toList():
            fields += field.name()+'_'+QVariant.typeToName(field.type())+'|'+str(field.length())+'|'+str(field.precision())+' '
        #metadata = collections.OrderedDict()
        metadata = [
            ['layer_name', lyr.name(),],
            ['gdrive_id', self.service_sheet.spreadsheetId,],
            ['geometry_type', self.geom_types[lyr.geometryType()],],
            ['features', "'%s" % str(lyr.featureCount()),],
            ['extent', self.wgs84_extent(lyr.extent()).asWktCoordinates(),],
            #['fields', fields,],
            ['abstract', lyr.abstract(),],
            ['srid', lyr.crs().authid(),],
            ['proj4_def', "'%s" % lyr.crs().toProj4(),],
        ]

        return metadata

    def update_summary_sheet(self,lyr=None, force=None):
        '''
        Creates a summary sheet with thumbnail, layer metadata and online view link
        '''
        #create a layer snapshot and upload it to google drive

        if not lyr:
            lyr = self.lyr

        mapbox_style = self.service_sheet.sheet_cell('settings!A5')
        if not mapbox_style:
            logger("migrating mapbox style")
            self.service_sheet.set_style_mapbox(self.layer_style_to_json(self.lyr))
        
        if not force and not self.dirty and not self.restyled:
            return

        if self.restyled:
            self.service_sheet.set_style_qgis(self.layer_style_to_xml(self.lyr))
            self.service_sheet.set_style_sld(self.SLD_to_xml(self.lyr))
            self.service_sheet.set_style_mapbox(self.layer_style_to_json(self.lyr))
            self.saveMetadataState()
        
        canvas = QgsMapCanvas()
        canvas.resize(QSize(600,600))
        canvas.setCanvasColor(Qt.white)
        canvas.setExtent(lyr.extent())
        canvas.setLayers([lyr])
        canvas.refresh()
        canvas.update()
        settings = canvas.mapSettings()
        settings.setLayers([lyr])
        job = QgsMapRendererParallelJob(settings)
        job.start()
        job.waitForFinished()
        image = job.renderedImage()

        transparent_image = QImage(image.width(), image.height(), QImage.Format_ARGB32)
        transparent_image.fill(Qt.transparent)
        p = QPainter(transparent_image)
        mask = image.createMaskFromColor(QColor(255, 255, 255).rgb(), Qt.MaskInColor)
        p.setClipRegion(QRegion(QBitmap(QPixmap.fromImage(mask))))
        p.drawPixmap(0, 0, QPixmap.fromImage(image))
        p.end()

        tmp_path = os.path.join(self.parent.plugin_dir,self.service_sheet.name+".png")
        transparent_image.save(tmp_path,"PNG")
        image_istances = self.service_drive.list_files(mimeTypeFilter='image/png',filename=self.service_sheet.name+".png")
        for imagename, image_props in image_istances.items():
            self.service_drive.delete_file(image_props['id'])
        result = self.service_drive.upload_image(tmp_path)
        self.service_drive.add_permission(result['id'],'anyone','reader')
        webLink = result['webContentLink'] #'https://drive.google.com/uc?export=view&id='+result['id']
        logger("webLink:" + webLink)
        canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326))
        worldfile = QgsMapSettingsUtils.worldFileContent(settings)
        lonlat_min = self.transformToWGS84(QgsPointXY(canvas.extent().xMinimum(), canvas.extent().yMinimum()))
        lonlat_max = self.transformToWGS84(QgsPointXY(canvas.extent().xMaximum(), canvas.extent().yMaximum()))
        keymap_extent = [lonlat_min.x(),lonlat_min.y(),lonlat_max.x(),lonlat_max.y()]
        
        os.remove(tmp_path)
        #update layer metadata
        summary_id = self.service_sheet.add_sheet('summary', no_grid=True)
        appPropsUpdate = [
            ["keymap",webLink],
            ["worldfile",pack(worldfile)],
            ["keymap_extent", json.dumps(keymap_extent)]
        ]
        res = self.service_sheet.update_appProperties(self.spreadsheet_id,appPropsUpdate)
        
        self.saveMetadataState(metadata=self.get_layer_metadata())
        self.parent.public_db.setKey(self.spreadsheet_id, dict(self.get_layer_metadata()+appPropsUpdate),only_update=True)
        #merge cells to visualize snapshot and aaply image snapshot
        request_body = {
            'requests': [{
                'mergeCells': {
                    "range": {
                        "sheetId": summary_id,
                        "startRowIndex": 9,
                        "endRowIndex": 32,
                        "startColumnIndex": 0,
                        "endColumnIndex": 9,
                    },
                "mergeType": 'MERGE_ALL'
                }
            }]
        }
        self.service_sheet.service.spreadsheets().batchUpdate(spreadsheetId=self.spreadsheet_id, body=request_body).execute()
        self.service_sheet.set_sheet_cell('summary!A10','=IMAGE("%s",3)' % webLink)

        permissions = self.service_drive.file_property(self.spreadsheet_id,'permissions')
        for permission in permissions:
            if permission['type'] == 'anyone':
                public = True
                break
            else:
                public = False
        if public:
            update_range = 'summary!A9:B9'
            update_body = {
                "range": update_range,
                "values": [['public link', "https://enricofer.github.io/gdrive_provider/weblink/converter.html?spreadsheet_id="+self.spreadsheet_id]]
            }
            self.service_sheet.service.spreadsheets().values().update(spreadsheetId=self.spreadsheet_id,range=update_range, body=update_body, valueInputOption='USER_ENTERED').execute()

        #hide worksheets except summary
        sheets = self.service_sheet.get_sheets()
        #self.service_sheet.toggle_sheet('summary', sheets['summary'], hidden=None)
        for sheet_name,sheet_id in sheets.items():
            if not sheet_name == 'summary':
                self.service_sheet.toggle_sheet(sheet_name, sheet_id, hidden=True)
Exemple #24
0
class WGS84Layer(object):

    "..."

    def __init__(self,
                 iface,
                 layer_name,
                 layer_type,
                 visible,
                 group=None,
                 show_count=True):
        "..."
        self.iface = iface
        self.layer_name = layer_name
        self.visible = visible
        self.layer = QgsVectorLayer(layer_type + "?crs=EPSG:4326", layer_name,
                                    "memory")
        self.provider = self.layer.dataProvider()
        self.layer = QgsProject.instance().addMapLayer(self.layer)
        self.canvas = iface.mapCanvas()
        if group is not None:
            layer_t = QgsProject.instance().mapLayersByName(layer_name)[0]
            root = QgsProject.instance().layerTreeRoot()

            myLayer = root.findLayer(layer_t.id())
            myClone = myLayer.clone()
            parent = myLayer.parent()

            group.insertChildNode(0, myClone)
            parent.removeChildNode(myLayer)

            # collapse groups
            for child in QgsProject.instance().layerTreeRoot().children():
                if isinstance(child, QgsLayerTreeGroup):
                    child.setExpanded(False)

        if show_count:
            root = QgsProject.instance().layerTreeRoot()
            leaf = root.findLayer(self.layer.id())
            leaf.setCustomProperty("showFeatureCount", True)

    def show(self, show_it):
        "..."
        node = QgsProject.instance().layerTreeRoot().findLayer(self.layer)
        if node:
            node.setItemVisibilityChecked(show_it)

    def remove(self):
        "..."
        # It seems QGis does already delete the layers
        # Ensure that the layer is still in the registry before calling removeMapLayer
        if self.layer is not None and len(
                QgsProject.instance().mapLayersByName(self.layer_name)) > 0:
            QgsProject.instance().removeMapLayer(self.layer)
            self.layer = None

    def refresh(self):
        "..."
        if not self.canvas.isDrawing():
            self.layer.triggerRepaint(True)
            self.canvas.refresh()

    def refresh_legend(self):
        "..."
        root = QgsProject.instance().layerTreeRoot().findLayer(self.layer.id())
        self.iface.layerTreeView().layerTreeModel().refreshLayerLegend(root)
        self.iface.mapCanvas().refresh()

    def poly_marker(self, placement, qcolor, width):
        "..."
        marker = QgsMarkerLineSymbolLayer()
        marker.setColor(qcolor)
        marker.setPlacement(placement)
        marker.setWidth(width)
        self.layer.renderer().symbol().appendSymbolLayer(marker)

    def poly_markers(self, qcolor, width):
        "..."
        self.poly_marker(QgsMarkerLineSymbolLayer.Vertex, qcolor, width * 1.33)
        self.poly_marker(QgsMarkerLineSymbolLayer.FirstVertex, qcolor,
                         width * 2.25)
        self.poly_marker(QgsMarkerLineSymbolLayer.LastVertex, qcolor,
                         width * 2.25)

    def set_attributes(self, attributes):
        "..."
        self.provider.addAttributes(attributes)
        self.layer.updateFields()

    def add_feature(self, geometry, attributes):
        "..."
        added_feature = QgsFeature()
        added_feature.setGeometry(geometry)
        added_feature.setAttributes(attributes)
        success, feat = self.provider.addFeatures([added_feature])
        if success:
            self.layer.updateExtents()
            return feat[0]
        return None

    def remove_feature(self, feature):
        "..."
        self.provider.deleteFeatures([feature.id()])

    def remove_all_features(self):
        "..."
        for feature in self.layer.getFeatures():
            self.remove_feature(feature)

    def enable_labeling(self,
                        field,
                        font="Arial",
                        size=10,
                        weight=50,
                        rgb=(0, 0, 0),
                        placement=1):
        "..."
        self.layer.setCustomProperty("labeling", "pal")
        self.layer.setCustomProperty("labeling/enabled", "true")
        self.layer.setCustomProperty("labeling/fontFamily", font)
        self.layer.setCustomProperty("labeling/fontSize", str(size))
        self.layer.setCustomProperty("labeling/fontWeight", str(weight))
        self.layer.setCustomProperty("labeling/textColorR", str(rgb[0]))
        self.layer.setCustomProperty("labeling/textColorG", str(rgb[1]))
        self.layer.setCustomProperty("labeling/textColorB", str(rgb[2]))
        self.layer.setCustomProperty("labeling/placement", str(placement))
        self.layer.setCustomProperty("labeling/fieldName", field)
Exemple #25
0
class PointLayerImportWorker(QObject):

    finished = pyqtSignal(QgsVectorLayer, QgsVectorLayer)
    interrupted = pyqtSignal(QgsVectorLayer, QgsVectorLayer)
    progressed = pyqtSignal(bool, int)

    def __init__(self,
                 uldk_api,
                 source_layer,
                 selected_only,
                 layer_name,
                 skip_duplicates,
                 additional_output_fields=[]):
        super().__init__()
        self.source_layer = source_layer
        self.uldk_api = uldk_api
        self.selected_only = selected_only
        self.skip_duplicates = skip_duplicates
        self.additional_output_fields = additional_output_fields

        self.layer_found = QgsVectorLayer(f"Polygon?crs=EPSG:{2180}",
                                          layer_name, "memory")
        self.layer_found.setCustomProperty("ULDK",
                                           f"{layer_name} point_import_found")

        self.layer_not_found = QgsVectorLayer(f"Point?crs=EPSG:{2180}",
                                              f"{layer_name} (nieznalezione)",
                                              "memory")
        self.layer_not_found.setCustomProperty(
            "ULDK", f"{layer_name} point_import_not_found")

    @pyqtSlot()
    def search(self):
        plots_found = []
        features_not_found = []

        fields = PLOTS_LAYER_DEFAULT_FIELDS + self.additional_output_fields

        self.layer_found.startEditing()
        self.layer_found.dataProvider().addAttributes(fields)

        self.layer_not_found.startEditing()
        self.layer_not_found.dataProvider().addAttributes([
            QgsField("tresc_bledu", QVariant.String),
        ])

        features = []
        features_iterator = self.source_layer.getSelectedFeatures(
        ) if self.selected_only else self.source_layer.getFeatures()
        source_crs = self.source_layer.sourceCrs()
        if source_crs != CRS_2180:
            transformation = (QgsCoordinateTransform(
                source_crs, CRS_2180, QgsCoordinateTransformContext()))
            for f in features_iterator:
                point = f.geometry().asPoint()
                point = transformation.transform(point)
                f.setGeometry(QgsGeometry.fromPointXY(point))
                features.append(f)
        else:
            transformation = None
            features = features_iterator

        uldk_search = self.uldk_api.ULDKSearchPoint(
            "dzialka", ("geom_wkt", "wojewodztwo", "powiat", "gmina", "obreb",
                        "numer", "teryt"))

        found_features = []

        for source_feature in features:
            if QThread.currentThread().isInterruptionRequested():
                self.__commit()
                self.interrupted.emit(self.layer_found, self.layer_not_found)
                return

            skip = False
            for found_feature in found_features:
                if found_feature.geometry().intersects(
                        source_feature.geometry()):
                    if not self.skip_duplicates:
                        self.layer_found.dataProvider().addFeature(
                            found_feature)
                    skip = True
            if skip:
                self.progressed.emit(True, 1 if self.skip_duplicates else 0)
                continue

            point = source_feature.geometry().asPoint()
            uldk_point = self.uldk_api.ULDKPoint(point.x(), point.y(), 2180)

            try:
                uldk_response_row = uldk_search.search(uldk_point)
                additional_attributes = []
                for field in self.additional_output_fields:
                    additional_attributes.append(source_feature[field.name()])
                found_feature = uldk_response_to_qgs_feature(
                    uldk_response_row, additional_attributes)
                found_features.append(found_feature)
                self.layer_found.dataProvider().addFeature(found_feature)
                self.progressed.emit(True, 0)
            except Exception as e:
                not_found_feature = self.__make_not_found_feature(
                    source_feature.geometry(), e)
                self.layer_not_found.dataProvider().addFeature(
                    not_found_feature)
                self.progressed.emit(False, 0)

        self.__commit()
        self.finished.emit(self.layer_found, self.layer_not_found)

    def __make_not_found_feature(self, geometry, e):
        error_message = str(e)
        feature = QgsFeature()
        feature.setGeometry(geometry)
        feature.setAttributes([error_message])

        return feature

    def __commit(self):
        self.layer_found.commitChanges()
        self.layer_not_found.commitChanges()
 def loadLayerType(self, item, geom_type):
     layer = QgsVectorLayer("%s?crs=epsg:4326" % geom_type, item.name, "memory")
     layer.setCustomProperty('DiviId', item.id)
     item.setQgisStyle(layer)
     with Cache(self):
         self.loadLayer(layer, add_empty=True)
    def checkLabeledLayerWithBlendModesCannotBeCached(self, job_type):
        """ any labeled layer utilising blending modes cannot be cached"""
        layer = QgsVectorLayer("Point?field=fldtxt:string",
                               "layer1", "memory")

        layer.setCustomProperty("labeling", "pal")
        layer.setCustomProperty("labeling/enabled", True)
        layer.setCustomProperty("labeling/fieldName", "fldtxt")

        layer2 = QgsVectorLayer("Point?field=fldtxt:string",
                                "layer2", "memory")
        layer2.setCustomProperty("labeling", "pal")
        layer2.setCustomProperty("labeling/enabled", True)
        layer2.setCustomProperty("labeling/fieldName", "fldtxt")
        layer2.setCustomProperty("labeling/blendMode", 5)

        settings = QgsMapSettings()
        settings.setExtent(QgsRectangle(5, 25, 25, 45))
        settings.setOutputSize(QSize(600, 400))
        settings.setLayers([layer, layer2])

        # with cache - cache should not be populated!
        cache = QgsMapRendererCache()
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        self.assertFalse(job.usedCachedLabels())
        self.assertFalse(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())

        # second job should also not be able to use label cache
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        # shouldn't use cache
        self.assertFalse(job.usedCachedLabels())
        # and results should not have been cached
        self.assertFalse(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())
 def run(self):
     """Run method that performs all the real work"""
     self.dlg.layerComboBox.clear()
     self.dlg.sheetTypeComboBox.clear()
     self.dlg.scaleComboBox.clear()  
     #self.dlg.label_2.setText('')        
     Alllayers = self.iface.legendInterface().layers()
     #layer_list = []
     lcount=0
     for layer in Alllayers:
         xyCrs = layer.crs()
         if xyCrs.projectionAcronym()=='longlat':
             lcount=lcount+1
             self.dlg.layerComboBox.addItem(layer.name(),layer)
             
     self.dlg.sheetTypeComboBox.addItem('Indian and Adjoining Countries Series Maps')    
     self.dlg.sheetTypeComboBox.addItem('Open Series Maps')    
     
     scale_list=['1:1M','1:250,000','1:125,000','1:50,000','1:25,000']
     self.dlg.scaleComboBox.addItems(scale_list)
     # show the dialog
     self.dlg.show()
     # Run the dialog event loop
     result = self.dlg.exec_()
     # See if OK was pressed
     if result:
         global scaleID
         global sheetw
         global sheeth
         if lcount>0:
             scaleID=self.dlg.scaleComboBox.currentIndex()+1
             maptype= self.dlg.sheetTypeComboBox.currentIndex()+1
             if maptype==1:                
                 sheetw,sheeth=setFactor1(scaleID)
             if maptype==2:
                 sheetw,sheeth=setFactor2(scaleID)               
             index = self.dlg.layerComboBox.currentIndex()
             clayer = self.dlg.layerComboBox.itemData(index)
             e=clayer.extent()
             a,b,c=degDec2dms(e.xMinimum())
             slongs=dmstosec(a,b,c)
             a,b,c=degDec2dms(e.xMaximum())
             elongs=dmstosec(a,b,c)
             a,b,c=degDec2dms(e.yMinimum())
             slats=dmstosec(a,b,c)
             a,b,c=degDec2dms(e.yMaximum())
             elats=dmstosec(a,b,c)
             slongs,elongs,slats,elats=CalculateExtentInTermsOfSheet(slongs,elongs,slats,elats)
             Xgrids=int(elongs-slongs)//sheetw # // is used for modular division
             Ygrids=int(elats-slats)//sheeth
             layer = QgsVectorLayer("Polygon?crs=EPSG:4326", "TopoSheets", "memory")
             global poly
             global pr
             pr = layer.dataProvider() 
             pr.addAttributes([QgsField("id",QVariant.Int),QgsField("SheetNo",QVariant.String),QgsField("Inside",QVariant.String)])
             layer.updateFields()
            
             poly = QgsFeature()
             CalculateSheet(maptype,slats,slongs,Xgrids,Ygrids)
             n=0
             # check intersection of selected layer feature with sheets
             fieldIdx = pr.fields().indexFromName('Inside' )
             updateMap = {}
             for f in clayer.getFeatures():
                 for a in layer.getFeatures():
                     if a.geometry().intersects(f.geometry()):
                         n=n+1
                         updateMap[a.id()] = { fieldIdx:1 }
             pr.changeAttributeValues(updateMap)
             
             # set the layer symbology
             values = (
             ('In', True,True,QColor.fromRgb(95,254,99)),
             ('Out', False,False,'yellow'),
             )
             # create a category for each item in values
             ranges=[]
             for label, lower, upper, color in values:
                 symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
                 symbol.setColor(QColor(color))
                 rng = QgsRendererRangeV2(lower, upper, symbol, label)
                 ranges.append(rng)
             expression = 'Inside' # field name
             renderer = QgsGraduatedSymbolRendererV2(expression, ranges)
             layer.setRendererV2(renderer)
             
             # set layer transparence and dolabelling
             layer.setLayerTransparency(65)              
             layer.updateExtents()
             QgsMapLayerRegistry.instance().addMapLayers([layer])
             layer.setCustomProperty("labeling", "pal")
             layer.setCustomProperty("labeling/enabled", "true")
             layer.setCustomProperty("labeling/fontFamily", "Arial")
             layer.setCustomProperty("labeling/fontSize", "10")
             layer.setCustomProperty("labeling/fieldName", "SheetNo")
             layer.setCustomProperty("labeling/placement", "1")
             iface.mapCanvas().refresh()
         else:
             iface.messageBar().pushMessage("Error", "No layers loaded, Load layer with Geographic Corrdinates", level=QgsMessageBar.CRITICAL)
         pass
    def compute_and_display_cadastral_vulnerability(self, cadastral_layer,
                                                    output_grid_points,
                                                    output_cadastral_points,
                                                    crop_index, crop_name,
                                                    base_path):
        cadastral_points_per_plot = {}
        for p in (output_grid_points + output_cadastral_points):
            if p.cadastral_polygon is None: continue
            if p.lulc_type not in ['agriculture', 'fallow land']: continue
            if p.cadastral_polygon.id() in cadastral_points_per_plot:
                cadastral_points_per_plot[p.cadastral_polygon.id()].append(
                    p.budget.PET_minus_AET_crop_end[crop_index])
            else:
                cadastral_points_per_plot[p.cadastral_polygon.id()] = [
                    p.budget.PET_minus_AET_crop_end[crop_index]
                ]
        for k, v in cadastral_points_per_plot.items():
            if len(v) > 0:
                cadastral_points_per_plot[k] = sum(v) / len(v)
            else:
                del cadastral_points_per_plot[k]

        # print cadastral_points_per_plot
        #	Create duplicate cadastral layer in memory
        memory_cadastral_layer = QgsVectorLayer(
            'Polygon?crs=epsg:32643',
            crop_name + ' Cadastral Level Vulnerability', 'memory')
        memory_cadastral_layer.startEditing()
        memory_cadastral_layer.dataProvider().addAttributes(
            cadastral_layer.qgsLayer.dataProvider().fields().toList())
        memory_cadastral_layer.updateFields()
        dict_new_feature_id_to_old_feature_id = {}
        for old_plot_id in cadastral_points_per_plot:
            result, output_features = memory_cadastral_layer.dataProvider(
            ).addFeatures([cadastral_layer.feature_dict[old_plot_id]])
            dict_new_feature_id_to_old_feature_id[
                output_features[0].id()] = old_plot_id
        memory_cadastral_layer.dataProvider().addAttributes(
            [QgsField('Deficit', QVariant.Double)])
        memory_cadastral_layer.updateFields()
        for new_feature in memory_cadastral_layer.getFeatures():
            new_feature['Deficit'] = cadastral_points_per_plot[
                dict_new_feature_id_to_old_feature_id[new_feature.id()]]
            memory_cadastral_layer.updateFeature(new_feature)
        memory_cadastral_layer.commitChanges()

        #	Graduated Rendering
        graduated_symbol_renderer_range_list = []
        ET_D_max = max(cadastral_points_per_plot.values())
        opacity = 1
        geometry_type = memory_cadastral_layer.geometryType()
        intervals_count = CADASTRAL_VULNERABILITY_DISPLAY_COLOUR_INTERVALS_COUNT
        dict_interval_colour = CADASTRAL_VULNERABILITY_DISPLAY_COLOURS_DICT
        for i in range(intervals_count):
            interval_min = 0 if i == 0 else (ET_D_max * float(i) /
                                             intervals_count) + 0.01
            interval_max = ET_D_max * float(i + 1) / intervals_count
            label = "{0:.2f} - {1:.2f}".format(interval_min, interval_max)
            colour = QColor(*dict_interval_colour[i])
            symbol = QgsSymbolV2.defaultSymbol(geometry_type)
            symbol.setColor(colour)
            symbol.setAlpha(opacity)
            interval_range = QgsRendererRangeV2(interval_min, interval_max,
                                                symbol, label)
            graduated_symbol_renderer_range_list.append(interval_range)
        renderer = QgsGraduatedSymbolRendererV2(
            '', graduated_symbol_renderer_range_list)
        renderer.setMode(QgsGraduatedSymbolRendererV2.EqualInterval)
        renderer.setClassAttribute('Deficit')
        memory_cadastral_layer.setRendererV2(renderer)

        QgsMapLayerRegistry.instance().addMapLayer(memory_cadastral_layer)
        memory_cadastral_layer.setCustomProperty('labeling', 'pal')
        memory_cadastral_layer.setCustomProperty('labeling/enabled', 'true')
        memory_cadastral_layer.setCustomProperty('labeling/fieldName',
                                                 'Number')
        memory_cadastral_layer.setCustomProperty('labeling/fontSize', '10')
        memory_cadastral_layer.setCustomProperty('labeling/placement', '0')

        memory_cadastral_layer.dataProvider().forceReload()
        memory_cadastral_layer.triggerRepaint()

        QgsVectorFileWriter.writeAsVectorFormat(
            memory_cadastral_layer, base_path + '/kharif_' + crop_name +
            '_cadastral_level_vulnerability.shp', "utf-8", None,
            "ESRI Shapefile")
    def checkAddingNewLabeledLayerInvalidatesLabelCache(self, job_type):
        """ adding a new labeled layer should invalidate any previous label caches"""
        layer = QgsVectorLayer("Point?field=fldtxt:string",
                               "layer1", "memory")

        layer.setCustomProperty("labeling", "pal")
        layer.setCustomProperty("labeling/enabled", True)
        layer.setCustomProperty("labeling/fieldName", "fldtxt")

        settings = QgsMapSettings()
        settings.setExtent(QgsRectangle(5, 25, 25, 45))
        settings.setOutputSize(QSize(600, 400))
        settings.setLayers([layer])

        # with cache - first run should populate cache
        cache = QgsMapRendererCache()
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        self.assertFalse(job.usedCachedLabels())
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())

        self.assertEqual(cache.dependentLayers('_labels_'), [layer])

        # add another labeled layer
        layer2 = QgsVectorLayer("Point?field=fldtxt:string",
                                "layer2", "memory")
        layer2.setCustomProperty("labeling", "pal")
        layer2.setCustomProperty("labeling/enabled", True)
        layer2.setCustomProperty("labeling/fieldName", "fldtxt")
        settings.setLayers([layer, layer2])

        # second job should not be able to use label cache, since a new layer was added
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        # shouldn't use cache
        self.assertFalse(job.usedCachedLabels())
        # but results should have been cached
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertEqual(set(cache.dependentLayers('_labels_')), {layer, layer2})
        self.assertTrue(job.takeLabelingResults())
class MissionTrack(QObject):
    mission_changed = pyqtSignal(int)
    step_removed = pyqtSignal(int)

    def __init__(self,
                 mission_name="Mission",
                 mission_filename=None,
                 mission_layer=None,
                 mission_renderer=None,
                 canvas=None):
        super(MissionTrack, self).__init__()
        self.mission_filename = mission_filename
        self.mission_name = mission_name
        self.mission_layer = mission_layer
        self.mission_renderer = mission_renderer
        self.mission = Mission()
        self.canvas = canvas
        self.start_end_marker = StartEndMarker(
            canvas, self.find_waypoints_in_mission(), QColor(200, 0, 0))

        self.saved = False
        self.modified = False

    def is_saved(self):
        """
        :return: return True if mission is saved,  otherwise False
        """
        return self.saved

    def is_modified(self):
        """
        :return: return True if mission is modified,  otherwise False
        """
        return self.modified

    def set_modified(self, modified):
        """ Set modified state"""
        self.modified = modified

    def load_mission(self, filename):
        """ Load mission with name 'filename'"""
        try:
            self.mission.load_mission(filename)
            self.mission_filename = filename
            self.update_start_end_markers()
            self.saved = True
            self.modifed = False
        except:
            logger.error("Invalid mission file")
            raise Exception("Invalid mission file")

    def set_mission(self, mission):
        """ Set Mission."""
        self.mission = mission

    def set_mission_filename(self, filename):
        """ Set Mission Filename"""
        self.mission_filename = filename

    def set_mission_name(self, name):
        """ Set Mission name."""
        self.mission_name = name

    def set_mission_layer(self, layer):
        """ Set mission layer."""
        self.mission_layer.name = self.mission_name
        self.mission_layer = layer

    def set_mission_renderer(self, renderer):
        """ Set mission renderer."""
        self.mission_renderer = renderer
        self.mission_layer.setRenderer(self.mission_renderer)

    def get_mission(self):
        """ Returns the mission"""
        return self.mission

    def get_step(self, step):
        """ Returns the step 'step' of the mission"""
        return self.mission.get_step(step)

    def get_mission_length(self):
        """ Returns mission length"""
        return self.mission.num_steps

    def get_mission_layer(self):
        """ Returns mission layer"""
        return self.mission_layer

    def get_mission_name(self):
        """ Returns mission name"""
        return self.mission_name

    def get_mission_filename(self):
        """ Returns mission filename"""
        return self.mission_filename

    def copy_mission(self):
        """ Returns a copy of the current mission"""
        return copy.deepcopy(self.mission)

    def change_position(self, wp_id, point):
        """
        Changes a position of the waypoint 'wp_id' with a new position 'point'
        :param wp_id: Current step
        :param point: Point position
        """
        position = self.mission.get_step(wp_id).get_maneuver().get_position()
        position.set_lat_lon(point.y(), point.x())
        if wp_id != self.mission.get_length() - 1:  # not last point
            if self.mission.get_step(
                    wp_id +
                    1).get_maneuver().get_maneuver_type() == SECTION_MANEUVER:
                logger.debug("Next step is a section")
                # if next step is a section change its initial position
                position_next = self.mission.get_step(
                    wp_id + 1).get_maneuver().get_initial_position()
                position_next.set_lat_lon(point.y(), point.x())
        self.mission_changed.emit(wp_id)
        self.update_start_end_markers()
        self.modified = True

    def remove_step(self, wp_id):
        """
        Remove step 'wp_id'
        :param wp_id: the step number to delete
        """
        initial_num_steps = self.mission.num_steps
        # if is first point and next waypoint is a Section, show warning message
        if (wp_id == 0 and initial_num_steps > 1 and
                self.mission.get_step(wp_id +
                                      1).get_maneuver().get_maneuver_type()
                == SECTION_MANEUVER):
            reply = QMessageBox.warning(
                None, "Mission Error",
                "Impossible to remove the point because the next point is a section"
            )

        else:

            if (initial_num_steps - 1 != wp_id
                    and self.mission.get_step(wp_id + 1).get_maneuver(
                    ).get_maneuver_type() == SECTION_MANEUVER):
                logger.debug("Next step is a section")
                # if it's not the last step
                # if next step is a section change its initial position to the one of the point before
                position_previous = self.mission.get_step(
                    wp_id - 1).get_maneuver().get_position()
                position_next = self.mission.get_step(
                    wp_id + 1).get_maneuver().get_initial_position()
                position_next.set_lat_lon(position_previous.latitude,
                                          position_previous.longitude)

            self.mission.remove_step(wp_id)
            self.step_removed.emit(wp_id)

            # When point is deleted, layer may need a geometry type change
            if initial_num_steps <= 2:
                self.update_layer_geometry()

            if wp_id == initial_num_steps - 1:
                # if last waypoint is removed show previous
                self.mission_changed.emit(wp_id - 1)
            else:
                self.mission_changed.emit(wp_id)

            self.update_start_end_markers()

            self.modified = True

    def update_steps(self, id_list, mission_step_list):
        """
        Update the number of the step 'wp_id' with new number 'step'
        :param id_list: list of step ids to update
        :param mission_step_list: list of mission steps to update
        :return:
        """
        if len(id_list) > 0:
            for i in range(0, len(id_list)):
                self.mission.update_step(id_list[i], mission_step_list[i])
            self.mission_changed.emit(id_list[0])
            self.update_start_end_markers()
            self.modified = True

    def add_step(self, wp_id, point):
        """ Add new step"""
        initial_num_steps = self.mission.num_steps
        logger.debug("Add step: Mission has {} steps and wp_id is {}".format(
            initial_num_steps, wp_id))
        step = MissionStep()
        position = MissionPosition()
        tolerance = MissionTolerance()
        # If more than one point, get maneuver from surrounding waypoint and copy data
        if initial_num_steps > 0:
            if wp_id != 0:

                # if point is at the end or in the middle copy from the previous
                manv_to_copy = self.mission.get_step(wp_id - 1).get_maneuver()

                if manv_to_copy.get_maneuver_type() == SECTION_MANEUVER:
                    # if section, initial pos will be the final pos of the previous
                    manv = MissionSection()
                    position.copy(manv_to_copy.get_position())
                    tolerance.copy(manv_to_copy.get_tolerance())
                    manv.set(manv_to_copy.get_final_position(), position,
                             manv_to_copy.get_speed(), tolerance)
                    manv.get_final_position().set_lat_lon(point.y(), point.x())

                if wp_id != initial_num_steps:
                    # if point in the middle check if next is also section, we need to modify it
                    manv_next = self.mission.get_step(wp_id).get_maneuver()
                    if manv_next.get_maneuver_type() == SECTION_MANEUVER:
                        manv_next.get_initial_position().set_lat_lon(
                            point.y(), point.x())

            else:  # wp_id == 0
                # copy from the next wp, next cannot be a section if was first waypoint until now, so no need to check
                manv_to_copy = self.mission.get_step(wp_id).get_maneuver()

            if manv_to_copy.get_maneuver_type() == WAYPOINT_MANEUVER:
                manv = MissionWaypoint()
                position.copy(manv_to_copy.get_position())
                tolerance.copy(manv_to_copy.get_tolerance())
                manv.set(position, manv_to_copy.get_speed(), tolerance)
                manv.get_position().set_lat_lon(point.y(), point.x())

            elif manv_to_copy.get_maneuver_type() == PARK_MANEUVER:
                manv = MissionPark()
                position.copy(manv_to_copy.get_position())
                tolerance.copy(manv_to_copy.get_tolerance())
                manv.set(position, manv_to_copy.get_speed(),
                         manv_to_copy.get_time(), tolerance)
                manv.get_position().set_lat_lon(point.y(), point.x())

        else:
            # is first point of the mission, fill maneuver by default type waypoint with clicked position
            manv = MissionWaypoint(
                MissionPosition(point.y(), point.x(), 0.0, False), 0.5,
                MissionTolerance(
                    2.0, 2.0,
                    1.0))  # todo: define default z, mode, speed and tolerance

        step.add_maneuver(manv)

        self.mission.insert_step(wp_id, step)
        logger.debug("Mission has now {} steps".format(self.mission.num_steps))

        # When point is added, layer may need a geometry type change
        if initial_num_steps <= 1:
            self.update_layer_geometry()

        self.mission_changed.emit(wp_id)
        self.update_start_end_markers()
        self.modified = True

    def save_mission(self):
        """ Saves a mission"""
        if self.mission.num_steps == 0:
            reply = QMessageBox.question(
                None, "Save Mission",
                "You are about to save an empty mission. Do you want to proceed?",
                QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
            if reply == QMessageBox.No:
                return False
        elif float(
                self.mission.get_step(self.mission.get_length() - 1).
                get_maneuver().get_position().get_z()) != 0.0:
            # vehicle last waypoint is not at zero depth.
            reply = QMessageBox.question(
                None, "Save Mission",
                "You are about to save and the last waypoint is not at zero depth. "
                "Do you want to proceed?", QMessageBox.Yes | QMessageBox.No,
                QMessageBox.Yes)
            if reply == QMessageBox.No:
                return False

        logger.info("Saving mission to {}".format(self.mission_filename))
        self.mission.write_mission(self.mission_filename)
        self.mission_layer.setCustomProperty("mission_xml",
                                             self.mission_filename)
        self.saved = True
        self.modified = False
        return True

    def saveas_mission(self):
        """ Save a mission with new name"""
        if self.mission.num_steps == 0:
            reply = QMessageBox.question(
                None, "Save Mission",
                "You are about to save an empty mission. Do you want to proceed?",
                QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
            if reply == QMessageBox.No:
                return False, None
        elif float(
                self.mission.get_step(self.mission.get_length() - 1).
                get_maneuver().get_position().get_z()) != 0.0:
            # vehicle last waypoint is not at zero depth.
            reply = QMessageBox.question(
                None, "Save Mission",
                "You are about to save and the last waypoint is not at zero depth. "
                "Do you want to proceed?", QMessageBox.Yes | QMessageBox.No,
                QMessageBox.Yes)
            if reply == QMessageBox.No:
                return False, None

        mission_filename, selected_filter = QFileDialog.getSaveFileName(
            None, 'Save mission', self.mission_filename, 'XML (*.xml)')

        if mission_filename != '':
            if selected_filter == "XML (*.xml)":
                file_info = QFileInfo(mission_filename)

                self.mission.write_mission(mission_filename)

                logger.debug(file_info.fileName())
                self.set_mission_name(file_info.fileName())
                self.set_mission_filename(mission_filename)
                self.mission_layer.setName(file_info.baseName())
                self.mission_layer.setCustomProperty("mission_xml",
                                                     self.mission_filename)

                self.saved = True
                self.modified = False

            return True, mission_filename

        else:
            return False, None

    def render_mission(self):
        """
        Render the mission, it will render the mission layer with Point geometry if it has only 1 point
        oterwise it will be with LineString geometry.
        """
        waypoints = self.find_waypoints_in_mission()
        if self.mission_layer is None:
            if len(waypoints) == 1:
                self.mission_layer = QgsVectorLayer("Point?crs=epsg:4326",
                                                    self.mission_name,
                                                    "memory")
                self.mission_layer.setCustomProperty("mission_xml",
                                                     self.mission_filename)
                feature = QgsFeature()
                feature.setGeometry(QgsGeometry(waypoints[0]))
                self.mission_layer.dataProvider().addFeatures([feature])
            else:
                self.mission_layer = QgsVectorLayer("LineString?crs=epsg:4326",
                                                    self.mission_name,
                                                    "memory")
                self.mission_layer.setCustomProperty("mission_xml",
                                                     self.mission_filename)
                feature = QgsFeature()
                feature.setGeometry(QgsGeometry.fromPolyline(waypoints))
                self.mission_layer.dataProvider().addFeatures([feature])

        else:
            # mission layer exists, now check if it has already a feature or not
            feats = self.mission_layer.getFeatures()
            logger.debug("layer feature count {}".format(
                self.mission_layer.featureCount()))
            if self.mission_layer.featureCount() == 0:
                feature = QgsFeature()
                if len(waypoints) == 1:
                    feature.setGeometry(QgsGeometry(waypoints[0]))
                else:
                    feature.setGeometry(QgsGeometry.fromPolyline(waypoints))
                self.mission_layer.dataProvider().addFeatures([feature])
            else:
                logger.debug("layer has feature mission already, updating...")
                for f in feats:
                    self.mission_layer.dataProvider().deleteFeatures([f.id()])
                    feature = QgsFeature()
                    if len(waypoints) == 1:
                        feature.setGeometry(QgsGeometry(waypoints[0]))
                    else:
                        feature.setGeometry(
                            QgsGeometry.fromPolyline(waypoints))
                    self.mission_layer.dataProvider().addFeatures([feature])

        self.set_mission_renderer(self.get_default_track_renderer())

    def find_waypoints_in_mission(self, indexes=None):
        """
        Gets all waypoints from a mission structure
        """
        waypoints = []
        if indexes is None:
            step_indexes = range(0, self.mission.size())
        else:
            step_indexes = indexes

        for stepindex in step_indexes:
            step = self.mission.get_step(stepindex)
            maneuver = step.get_maneuver()

            if maneuver.get_maneuver_type(
            ) == SECTION_MANEUVER:  # for a Section
                position = maneuver.get_final_position()
            else:
                position = maneuver.get_position()  # for Waypoint and Park

            waypoints.append(
                QgsPoint(float(position.longitude), float(position.latitude)))
        return waypoints

    def get_default_track_renderer(self):
        # Renderer for track lines
        registry = QgsSymbolLayerRegistry()
        line_meta = registry.symbolLayerMetadata("SimpleLine")
        marker_meta = registry.symbolLayerMetadata("MarkerLine")

        symbol = QgsSymbol.defaultSymbol(self.mission_layer.geometryType())

        # Line layer
        line_layer = line_meta.createSymbolLayer({
            'width': '0.5',
            'color': '255,0,0',
            'offset': '0.0',
            'penstyle': 'solid',
            'use_custom_dash': '0',
            'joinstyle': 'bevel',
            'capstyle': 'square'
        })

        # Marker layer
        marker_layer = marker_meta.createSymbolLayer({
            'width': '1.5',
            'color': '255,0,0',
            'placement': 'vertex',
            'offset': '0.0'
        })
        sub_symbol = marker_layer.subSymbol()

        # Replace the default layer with our own SimpleMarker
        sub_symbol.deleteSymbolLayer(0)
        cross = registry.symbolLayerMetadata("SimpleMarker").createSymbolLayer(
            {
                'name': 'circle',
                'color': '255,0,0',
                'color_border': '0,0,0',
                'offset': '0,0',
                'size': '2.5',
                'angle': '0'
            })
        sub_symbol.appendSymbolLayer(cross)

        # Replace the default layer with our two custom layers
        symbol.deleteSymbolLayer(0)
        symbol.appendSymbolLayer(line_layer)
        symbol.appendSymbolLayer(marker_layer)

        # Replace the renderer of the current layer
        renderer = QgsSingleSymbolRenderer(symbol)
        return renderer

    def update_start_end_markers(self):
        """ Updates start_end_markers"""
        self.start_end_marker.update_markers(self.find_waypoints_in_mission())

    def hide_start_end_markers(self):
        """ Hide start_end_markers"""
        self.start_end_marker.hide_markers()

    def remove_start_end_markers(self):
        """ Removes start end markers"""
        self.start_end_marker.close_markers()

    def update_layer_geometry(self):
        """
        This function needs to be called every time a point is added or deleted from the layer
        If needed, a new layer is created with the apropiate geometry
        Point if only one point, LineString otherwise
        """
        options = QgsDataProvider.ProviderOptions()
        waypoints = self.find_waypoints_in_mission()
        if (len(waypoints) != 1) and self.mission_layer.geometryType(
        ) == QgsWkbTypes.PointGeometry:
            self.mission_layer.setDataSource("LineString?crs=epsg:4326",
                                             self.mission_name, "memory",
                                             options)
            self.set_mission_renderer(self.get_default_track_renderer())
        elif len(waypoints) == 1 and self.mission_layer.geometryType(
        ) == QgsWkbTypes.LineGeometry:
            self.mission_layer.setDataSource("Point?crs=epsg:4326",
                                             self.mission_name, "memory",
                                             options)
            self.set_mission_renderer(self.get_default_track_renderer())
Exemple #32
0
    def checkAddingNewLabeledLayerInvalidatesLabelCache(self, job_type):
        """ adding a new labeled layer should invalidate any previous label caches"""
        layer = QgsVectorLayer("Point?field=fldtxt:string", "layer1", "memory")

        layer.setCustomProperty("labeling", "pal")
        layer.setCustomProperty("labeling/enabled", True)
        layer.setCustomProperty("labeling/fieldName", "fldtxt")

        settings = QgsMapSettings()
        settings.setExtent(QgsRectangle(5, 25, 25, 45))
        settings.setOutputSize(QSize(600, 400))
        settings.setLayers([layer])

        # with cache - first run should populate cache
        cache = QgsMapRendererCache()
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        self.assertFalse(job.usedCachedLabels())
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertTrue(job.takeLabelingResults())

        self.assertEqual(cache.dependentLayers('_labels_'), [layer])

        # add another labeled layer
        layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2",
                                "memory")
        layer2.setCustomProperty("labeling", "pal")
        layer2.setCustomProperty("labeling/enabled", True)
        layer2.setCustomProperty("labeling/fieldName", "fldtxt")
        settings.setLayers([layer, layer2])

        # second job should not be able to use label cache, since a new layer was added
        job = job_type(settings)
        job.setCache(cache)
        job.start()
        job.waitForFinished()
        # shouldn't use cache
        self.assertFalse(job.usedCachedLabels())
        # but results should have been cached
        self.assertTrue(cache.hasCacheImage('_labels_'))
        self.assertEqual(set(cache.dependentLayers('_labels_')),
                         {layer, layer2})
        self.assertTrue(job.takeLabelingResults())
Exemple #33
0
def points_along_line(layerout,
                      startpoint,
                      endpoint,
                      distance,
                      label,
                      layer,
                      selected_only=True,
                      force=False,
                      fo_fila=False,
                      divide=0,
                      decimal=2):
    """Adding Points along the line
    """

    crs = layer.crs().authid()

    # TODO check for virtual or shapelayer and set virt_layer according to it
    shape = False
    if shape:
        # define fields for feature attributes. A list of QgsField objects is needed
        fields = [
            QgsField("first", QVariant.Int),
            QgsField("second", QVariant.String)
        ]
        # create an instance of vector file writer, which will create the vector file.
        # Arguments:
        # 1. path to new file (will fail if exists already)
        # 2. encoding of the attributes
        # 3. field map
        # 4. geometry type - from WKBTYPE enum
        # 5. layer's spatial reference (instance of
        #    QgsCoordinateReferenceSystem) - optional
        # 6. driver name for the output file
        writer = QgsVectorFileWriter("my_shapes.shp", "CP1250", fields,
                                     Qgis.WKBPoint, crs, "ESRI Shapefile")
        if writer.hasError() != QgsVectorFileWriter.NoError:
            # fix_print_with_import
            print("Error when creating shapefile: ", writer.hasError())
        # add a feature
        fet = QgsFeature()
        fet.setGeometry(QgsGeometry.fromPoint(QgsPoint(10, 10)))
        fet.setAttributes([1, "text"])
        writer.addFeature(fet)
        # delete the writer to flush features to disk (optional)
        del writer

        layer_type = "Shapefile"  # TODO Add Shapefile functionality here
    else:
        layer_type = "memory"

    virt_layer = QgsVectorLayer("Point?crs=%s" % crs, layerout, layer_type)
    provider = virt_layer.dataProvider()
    virt_layer.startEditing()  # actually writes attributes

    units = layer.crs().mapUnits()

    unitname = QgsUnitTypes.toString(units)
    provider.addAttributes([
        QgsField("fid", QVariant.Int),
        QgsField("cng" + unitname, QVariant.Double)
    ])

    def get_features():
        """Getting the features
        """
        if selected_only:
            return layer.selectedFeatures()
        else:
            return layer.getFeatures()

    # Loop through all (selected) features
    for feature in get_features():
        geom = feature.geometry()
        # Add feature ID of selected feature
        fid = feature.id()
        if not geom:
            QgsMessageLog.logMessage("No geometry", "QChainage")
            continue

        features = create_points_at(startpoint, endpoint, distance, geom, fid,
                                    force, fo_fila, divide)
        provider.addFeatures(features)
        virt_layer.updateExtents()

    proj = QgsProject.instance()
    proj.addMapLayers([virt_layer])
    virt_layer.commitChanges()
    virt_layer.reload()

    # generic labeling properties
    if label:
        virt_layer.setCustomProperty("labeling", "pal")
        virt_layer.setCustomProperty("labeling/enabled", "true")
        virt_layer.setCustomProperty("labeling/fieldName", "cng")
        virt_layer.setCustomProperty("labeling/fontSize", "10")
        virt_layer.setCustomProperty("labeling/multiLineLabels", "true")
        virt_layer.setCustomProperty("labeling/formatNumbers", "true")
        virt_layer.setCustomProperty("labeling/decimals", decimal)
        virt_layer.setCustomProperty("labeling/Size", "5")
    # symbol = QgsMarkerSymbol.createSimple({"name": "capital"})
    # virt_layer.setRenderer(QgsSingleSymbolRenderer(symbol))
    virt_layer.triggerRepaint()
    return
Exemple #34
0
def points_along_line(layerout,
                      startpoint,
                      endpoint,
                      distance,
                      label,
                      layer,
                      selected_only=True,
                      force=False,
                      fo_fila=False,
                      divide=0,
                      decimal=2):
    """Adding Points along the line
    """

    crs = layer.crs().authid()
    # TODO check for virtual or shapelayer and set virt_layer according to it
    shape = False
    if shape:
        # define fields for feature attributes. A list of QgsField objects is needed
        fields = [QgsField("first", QVariant.Int),
                  QgsField("second", QVariant.String)]
        # create an instance of vector file writer, which will create the vector file.
        # Arguments:
        # 1. path to new file (will fail if exists already)
        # 2. encoding of the attributes
        # 3. field map
        # 4. geometry type - from WKBTYPE enum
        # 5. layer's spatial reference (instance of
        #    QgsCoordinateReferenceSystem) - optional
        # 6. driver name for the output file
        writer = QgsVectorFileWriter("my_shapes.shp",
                                     "CP1250",
                                     fields,
                                     QGis.WKBPoint,
                                     crs,
                                     "ESRI Shapefile")
        if writer.hasError() != QgsVectorFileWriter.NoError:
            print "Error when creating shapefile: ", writer.hasError()
        # add a feature
        fet = QgsFeature()
        fet.setGeometry(QgsGeometry.fromPoint(QgsPoint(10, 10)))
        fet.setAttributes([1, "text"])
        writer.addFeature(fet)
        # delete the writer to flush features to disk (optional)
        del writer

        layer_type = "Shapefile"  # TODO Add Shapefile functionality here
    else:
        layer_type = "memory"

    virt_layer = QgsVectorLayer("Point?crs=%s" % crs,
                                layerout,
                                layer_type)
    provider = virt_layer.dataProvider()
    virt_layer.startEditing()   # actually writes attributes
    units = layer.crs().mapUnits()
    unit_dic = {
        QGis.Degrees: 'Degrees',
        QGis.Meters: 'Meters',
        QGis.Feet: 'Feet',
        QGis.UnknownUnit: 'Unknown'}
    unit = unit_dic.get(units, 'Unknown')
    provider.addAttributes([QgsField("fid", QVariant.Int),
                            QgsField("cng_("+unit+")", QVariant.Double)])

    def get_features():
        """Getting the features
        """
        if selected_only:
            return layer.selectedFeatures()
        else:
            return layer.getFeatures()

    # Loop through all (selected) features
    for feature in get_features():
        geom = feature.geometry()
        # Add feature ID of selected feature
        fid = feature.id()
        if not geom:
            QgsMessageLog.logMessage("No geometry", "QChainage")
            continue

        features = create_points_at(startpoint,
                                    endpoint,
                                    distance,
                                    geom,
                                    fid,
                                    force,
                                    fo_fila,
                                    divide)
        provider.addFeatures(features)
        virt_layer.updateExtents()

    QgsMapLayerRegistry.instance().addMapLayers([virt_layer])
    virt_layer.commitChanges()
    virt_layer.reload()

    # from here Add labeling
    # generic labeling properties
    if label:
        virt_layer.setCustomProperty("labeling", "pal")
        virt_layer.setCustomProperty("labeling/enabled", "true")
        virt_layer.setCustomProperty("labeling/fieldName", "cng_("+unit+")")
        virt_layer.setCustomProperty("labeling/fontSize", "10")
        virt_layer.setCustomProperty("labeling/multiLineLabels", "true")
        virt_layer.setCustomProperty("labeling/formatNumbers", "true")
        virt_layer.setCustomProperty("labeling/decimals", decimal)

        # virt_layer.setCustomProperty("labeling/Size", "5")
    # symbol = QgsMarkerSymbolV2.createSimple({"name": "capital"})
    # virt_layer.setRendererV2(QgsSingleSymbolRendererV2(symbol))
    virt_layer.triggerRepaint()
    return
Exemple #35
0
    def layer_to_QgsVectorLayer(
            source_layer,  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
            input_file,
            context: Context,
            fallback_crs=QgsCoordinateReferenceSystem(),
            defer_layer_uri_set: bool = False):
        """
        Converts a vector layer
        """
        if source_layer.__class__.__name__ == 'CadFeatureLayer':
            layer = source_layer.layer
        else:
            layer = source_layer

        crs = CrsConverter.convert_crs(
            layer.layer_extent.crs,
            context) if layer.layer_extent else QgsCoordinateReferenceSystem()
        if not crs.isValid():
            crs = fallback_crs

        subset_string = ''
        if layer.selection_set:
            subset_string = 'fid in ({})'.format(','.join(
                [str(s) for s in layer.selection_set]))
        elif layer.definition_query:
            subset_string = ExpressionConverter.convert_esri_sql(
                layer.definition_query)

        base, _ = os.path.split(input_file)

        uri, wkb_type, provider, encoding, file_name = VectorLayerConverter.get_uri(
            source_layer=source_layer,
            obj=layer,
            base=base,
            crs=crs,
            subset=subset_string,
            context=context)

        if wkb_type is None or wkb_type == QgsWkbTypes.Unknown:
            wkb_type = VectorLayerConverter.layer_to_wkb_type(layer)
        context.layer_type_hint = wkb_type

        if Qgis.QGIS_VERSION_INT >= 31600:
            # try to get the layer name so that we can remove it from field references.
            # e.g. if layer name is polys then qgis won't support to arcgis style "polys.field" format
            parts = QgsProviderRegistry.instance().decodeUri(provider, uri)
            context.main_layer_name = parts.get('layerName')
            if not context.main_layer_name and provider == 'ogr':
                context.main_layer_name = Path(parts['path']).stem

            if context.main_layer_name:
                subset_string = subset_string.replace(
                    context.main_layer_name + '.', '')
        else:
            context.main_layer_name = None

        if provider == 'ogr' and (not file_name
                                  or not os.path.exists(file_name)
                                  ) and context.invalid_layer_resolver:
            res = context.invalid_layer_resolver(layer.name, uri, wkb_type)
            uri = res.uri
            provider = res.providerKey

        if Qgis.QGIS_VERSION_INT >= 31000:
            opts = QgsVectorLayer.LayerOptions()
            if wkb_type is not None:
                opts.fallbackWkbType = wkb_type

            if provider == 'ogr' and subset_string:
                uri += '|subset={}'.format(subset_string)

            original_uri = uri
            if defer_layer_uri_set:
                uri = 'xxxxxxxxx' + uri

            vl = QgsVectorLayer(uri, layer.name, provider, opts)
            if defer_layer_uri_set:
                vl.setCustomProperty('original_uri', original_uri)
        else:
            vl = QgsMemoryProviderUtils.createMemoryLayer(
                layer.name, QgsFields(), wkb_type, crs)

        # context.style_folder, _ = os.path.split(output_file)
        if layer.renderer:
            renderer = VectorRendererConverter.convert_renderer(
                layer.renderer, context)
            try:
                if not renderer.usingSymbolLevels():
                    renderer.setUsingSymbolLevels(
                        layer.use_advanced_symbol_levels)
            except AttributeError:
                pass

            if layer.use_page_definition_query:
                filter_expression = '"{}" {} @atlas_pagename'.format(
                    layer.page_name_field, layer.page_name_match_operator)
                root_rule = QgsRuleBasedRenderer.Rule(None)

                # special case -- convert a simple renderer
                if isinstance(renderer, QgsSingleSymbolRenderer):
                    filter_rule = QgsRuleBasedRenderer.Rule(
                        renderer.symbol().clone())
                    filter_rule.setFilterExpression(filter_expression)
                    filter_rule.setLabel(layer.name)
                    filter_rule.setDescription(layer.name)
                    root_rule.appendChild(filter_rule)
                else:
                    source_rule_renderer = QgsRuleBasedRenderer.convertFromRenderer(
                        renderer)
                    filter_rule = QgsRuleBasedRenderer.Rule(None)
                    filter_rule.setFilterExpression(filter_expression)
                    filter_rule.setLabel('Current Atlas Page')
                    filter_rule.setDescription('Current Atlas Page')
                    root_rule.appendChild(filter_rule)
                    for child in source_rule_renderer.rootRule().children():
                        filter_rule.appendChild(child.clone())

                renderer = QgsRuleBasedRenderer(root_rule)

            if renderer:
                vl.setRenderer(renderer)
                vl.triggerRepaint()
        else:
            vl.setRenderer(QgsNullSymbolRenderer())
            vl.triggerRepaint()

        metadata = vl.metadata()
        metadata.setAbstract(layer.description)
        vl.setMetadata(metadata)  #

        # layer.zoom_max = "don't show when zoomed out beyond"
        zoom_max = layer.zoom_max
        # layer.zoom_min = "don't show when zoomed in beyond"
        zoom_min = layer.zoom_min

        enabled_scale_range = bool(zoom_max or zoom_min)
        if zoom_max and zoom_min and zoom_min > zoom_max:
            # inconsistent scale range -- zoom_max should be bigger number than zoom_min
            zoom_min, zoom_max = zoom_max, zoom_min

        # qgis minimum scale = don't show when zoomed out beyond, i.e. ArcGIS zoom_max
        vl.setMinimumScale(
            zoom_max if enabled_scale_range else layer.stored_zoom_max)
        # qgis maximum scale = don't show when zoomed in beyond, i.e. ArcGIS zoom_min
        vl.setMaximumScale(
            zoom_min if enabled_scale_range else layer.stored_zoom_min)
        vl.setScaleBasedVisibility(enabled_scale_range)

        vl.setOpacity(1.0 - (layer.transparency or 0) / 100)

        if layer.display_expression_properties and layer.display_expression_properties.expression and layer.display_expression_properties.expression_parser is not None:
            vl.setDisplayExpression(
                ExpressionConverter.convert(
                    layer.display_expression_properties.expression,
                    layer.display_expression_properties.expression_parser,
                    layer.display_expression_properties.advanced, context))

        if Qgis.QGIS_VERSION_INT < 31000:
            vl.setDataSource(uri, layer.name, provider)

        if encoding:
            vl.dataProvider().setEncoding(encoding)

        if subset_string:
            vl.setSubsetString(subset_string)

        vl.setCrs(crs)

        for e in layer.extensions:
            if e.__class__.__name__ == 'ServerLayerExtension':
                if 'CopyrightText' in e.properties.properties:
                    layer_credits = e.properties.properties['CopyrightText']
                    metadata = vl.metadata()
                    rights = metadata.rights()
                    rights.append(layer_credits)
                    metadata.setRights(rights)
                    vl.setMetadata(metadata)

        LabelConverter.convert_annotation_collection(
            layer.annotation_collection, dest_layer=vl, context=context)
        vl.setLabelsEnabled(layer.labels_enabled)

        DiagramConverter.convert_diagrams(layer.renderer,
                                          dest_layer=vl,
                                          context=context)

        # setup joins
        join_layer = VectorLayerConverter.add_joined_layer(
            source_layer=layer,
            input_file=input_file,
            base_layer=vl,
            context=context)

        context.dataset_name = ''

        vl.setLegend(QgsMapLayerLegend.defaultVectorLegend(vl))

        if layer.hyperlinks:
            VectorLayerConverter.convert_hyperlinks(layer.hyperlinks, vl)

        vl.setDisplayExpression(
            QgsExpression.quotedColumnRef(layer.display_field))

        res = [vl]
        if join_layer:
            res.append(join_layer)

        context.main_layer_name = None
        return res
class LoadOutputAsLayerDialog(QDialog, FORM_CLASS):
    """
    Dialog to load an oq-engine output as layer
    """
    init_done = pyqtSignal(QDialog)
    loading_completed = pyqtSignal(QDialog)
    loading_exception = pyqtSignal(QDialog, Exception)

    def __init__(self,
                 drive_engine_dlg,
                 iface,
                 viewer_dock,
                 session,
                 hostname,
                 calc_id,
                 output_type=None,
                 path=None,
                 mode=None,
                 zonal_layer_path=None,
                 engine_version=None,
                 calculation_mode=None):
        # sanity check
        if output_type not in OQ_TO_LAYER_TYPES:
            raise NotImplementedError(output_type)
        self.drive_engine_dlg = drive_engine_dlg
        self.iface = iface
        self.viewer_dock = viewer_dock
        self.path = path
        self.session = session
        self.hostname = hostname
        self.calc_id = calc_id
        self.output_type = output_type
        self.mode = mode  # if 'testing' it will avoid some user interaction
        self.zonal_layer_path = zonal_layer_path
        self.engine_version = engine_version
        self.calculation_mode = calculation_mode
        QDialog.__init__(self)
        # Set up the user interface from Designer.
        self.setupUi(self)
        # Disable ok_button until all user options are set
        self.ok_button = self.buttonBox.button(QDialogButtonBox.Ok)
        self.ok_button.setDisabled(True)
        self.oqparam = self.drive_engine_dlg.get_oqparam()

    def on_extract_error(self, exception):
        if isinstance(exception, TaskCanceled):
            msg = 'Data extraction canceled'
            log_msg(msg, level='W', message_bar=self.iface.messageBar())
        else:
            log_msg('Unable to complete data extraction',
                    level='C',
                    message_bar=self.iface.messageBar(),
                    exception=exception)
        self.reject()

    def finalize_init(self, extracted_npz):
        self.npz_file = extracted_npz
        self.populate_out_dep_widgets()
        self.adjustSize()
        self.set_ok_button()
        self.show()
        self.init_done.emit(self)

    def create_num_sites_indicator(self):
        self.num_sites_msg = 'Number of sites: %s'
        self.num_sites_lbl = QLabel(self.num_sites_msg % '')
        self.vlayout.addWidget(self.num_sites_lbl)

    def create_file_size_indicator(self):
        self.file_size_msg = 'File size: %s'
        self.file_size_lbl = QLabel(self.file_size_msg % '')
        self.vlayout.addWidget(self.file_size_lbl)

    def create_single_layer_ckb(self):
        self.load_single_layer_ckb = QCheckBox(
            'Load one layer containing all hazard maps')
        self.vlayout.addWidget(self.load_single_layer_ckb)

    def create_load_one_layer_per_stat_ckb(self):
        self.load_one_layer_per_stat_ckb = QCheckBox(
            'Load one layer per realization or statistic')
        self.vlayout.addWidget(self.load_one_layer_per_stat_ckb)

    def create_min_mag_dsb(self, min_mag=4.0):
        self.min_mag_lbl = QLabel()
        self.min_mag_dsb = QDoubleSpinBox(self)
        self.min_mag_dsb.setRange(0, 10)
        self.min_mag_dsb.setDecimals(1)
        self.min_mag_dsb.setSingleStep(0.1)
        self.min_mag_dsb.setValue(min_mag)
        self.vlayout.addWidget(self.min_mag_lbl)
        self.vlayout.addWidget(self.min_mag_dsb)
        # NOTE: if we don't modify the text of the label after adding the
        # widget to the layout, the adjustSize does not work properly, for some
        # unknown reason
        self.min_mag_lbl.setText('Minimum magnitude')

    def create_rlz_or_stat_selector(self, all_ckb=False, label='Realization'):
        self.rlz_or_stat_lbl = QLabel(label)
        self.rlz_or_stat_cbx = QComboBox()
        self.rlz_or_stat_cbx.setEnabled(False)
        self.rlz_or_stat_cbx.currentIndexChanged['QString'].connect(
            self.on_rlz_or_stat_changed)
        if all_ckb:
            self.load_all_rlzs_or_stats_chk = QCheckBox(
                'Load all realizations')
            self.load_all_rlzs_or_stats_chk.stateChanged[int].connect(
                self.on_load_all_rlzs_or_stats_chk_stateChanged)
            self.vlayout.addWidget(self.load_all_rlzs_or_stats_chk)
        self.vlayout.addWidget(self.rlz_or_stat_lbl)
        self.vlayout.addWidget(self.rlz_or_stat_cbx)

    def on_load_all_rlzs_or_stats_chk_stateChanged(self, state):
        self.rlz_or_stat_cbx.setEnabled(state == Qt.Unchecked)

    def create_selector(self,
                        name,
                        label_text,
                        filter_ckb=False,
                        add_to_layout=None,
                        on_text_changed=None):
        if add_to_layout is not None:
            layout = add_to_layout
        else:
            layout = self.vlayout
        setattr(self, "%s_lbl" % name, QLabel(label_text))
        setattr(self, "%s_cbx" % name, QComboBox())
        lbl = getattr(self, "%s_lbl" % name)
        cbx = getattr(self, "%s_cbx" % name)
        cbx.setDisabled(filter_ckb)
        if on_text_changed is not None:
            cbx.currentTextChanged['QString'].connect(on_text_changed)
        if filter_ckb:
            setattr(self, "filter_by_%s_ckb" % name,
                    QCheckBox('Filter by %s' % name))
            filter_ckb = getattr(self, "filter_by_%s_ckb" % name)

            def on_load_all_ckb_changed():
                cbx.setEnabled(filter_ckb.isChecked())

            filter_ckb.stateChanged[int].connect(on_load_all_ckb_changed)
            filter_ckb.setChecked(False)
            layout.addWidget(filter_ckb)
        layout.addWidget(lbl)
        layout.addWidget(cbx)

    def create_imt_selector(self, all_ckb=False):
        self.imt_lbl = QLabel('Intensity Measure Type')
        self.imt_cbx = QComboBox()
        self.imt_cbx.setEnabled(False)
        self.imt_cbx.currentIndexChanged['QString'].connect(
            self.on_imt_changed)
        if all_ckb:
            self.load_all_imts_chk = QCheckBox('Load all IMTs')
            self.load_all_imts_chk.stateChanged[int].connect(
                self.on_load_all_imts_chk_stateChanged)
            self.vlayout.addWidget(self.load_all_imts_chk)
        self.vlayout.addWidget(self.imt_lbl)
        self.vlayout.addWidget(self.imt_cbx)

    def on_load_all_imts_chk_stateChanged(self, state):
        self.imt_cbx.setEnabled(state == Qt.Unchecked)

    def create_poe_selector(self, all_ckb=False):
        self.poe_lbl = QLabel('Probability of Exceedance')
        self.poe_cbx = QComboBox()
        self.poe_cbx.setEnabled(False)
        self.poe_cbx.currentIndexChanged['QString'].connect(
            self.on_poe_changed)
        if all_ckb:
            self.load_all_poes_chk = QCheckBox('Load all PoEs')
            self.load_all_poes_chk.stateChanged[int].connect(
                self.on_load_all_poes_chk_stateChanged)
            self.vlayout.addWidget(self.load_all_poes_chk)
        self.vlayout.addWidget(self.poe_lbl)
        self.vlayout.addWidget(self.poe_cbx)

    def on_load_all_poes_chk_stateChanged(self, state):
        self.poe_cbx.setEnabled(state == Qt.Unchecked)

    def create_loss_type_selector(self):
        self.loss_type_lbl = QLabel('Loss Type')
        self.loss_type_cbx = QComboBox()
        self.loss_type_cbx.setEnabled(False)
        self.loss_type_cbx.currentIndexChanged['QString'].connect(
            self.on_loss_type_changed)
        self.vlayout.addWidget(self.loss_type_lbl)
        self.vlayout.addWidget(self.loss_type_cbx)

    def create_eid_selector(self):
        self.eid_lbl = QLabel('Event ID')
        self.eid_sbx = QSpinBox()
        self.eid_sbx.setEnabled(False)
        self.vlayout.addWidget(self.eid_lbl)
        self.vlayout.addWidget(self.eid_sbx)

    def create_dmg_state_selector(self):
        self.dmg_state_lbl = QLabel('Damage state')
        self.dmg_state_cbx = QComboBox()
        self.dmg_state_cbx.setEnabled(False)
        self.dmg_state_cbx.currentIndexChanged['QString'].connect(
            self.on_dmg_state_changed)
        self.vlayout.addWidget(self.dmg_state_lbl)
        self.vlayout.addWidget(self.dmg_state_cbx)

    def create_taxonomy_selector(self):
        self.taxonomy_lbl = QLabel('Taxonomy')
        self.taxonomy_cbx = QComboBox()
        self.taxonomy_cbx.setEnabled(False)
        self.vlayout.addWidget(self.taxonomy_lbl)
        self.vlayout.addWidget(self.taxonomy_cbx)

    def create_style_by_selector(self):
        self.style_by_lbl = QLabel('Style by')
        self.style_by_cbx = QComboBox()
        self.vlayout.addWidget(self.style_by_lbl)
        self.vlayout.addWidget(self.style_by_cbx)

    def create_load_selected_only_ckb(self):
        self.load_selected_only_ckb = QCheckBox("Load only the selected items")
        self.load_selected_only_ckb.setChecked(True)
        self.vlayout.addWidget(self.load_selected_only_ckb)

    def create_show_return_period_ckb(self):
        self.show_return_period_chk = QCheckBox(
            "Show the return period in layer names")
        self.show_return_period_chk.setChecked(False)
        self.vlayout.addWidget(self.show_return_period_chk)

    def create_aggregate_by_site_ckb(self):
        self.aggregate_by_site_ckb = QCheckBox("Aggregate by site")
        self.aggregate_by_site_ckb.setChecked(True)
        self.vlayout.addWidget(self.aggregate_by_site_ckb)

    def create_zonal_layer_selector(self, discard_nonmatching=True):
        self.added_zonal_layer = None
        self.zonal_layer_gbx = QGroupBox()
        self.zonal_layer_gbx.setTitle('Aggregate by zone')
        self.zonal_layer_gbx.setCheckable(True)
        self.zonal_layer_gbx.setChecked(False)
        self.zonal_layer_gbx_v_layout = QVBoxLayout()
        self.zonal_layer_gbx.setLayout(self.zonal_layer_gbx_v_layout)
        self.zonal_layer_cbx = QComboBox()
        self.zonal_layer_cbx.addItem('')
        self.zonal_layer_lbl = QLabel('Zonal layer')
        self.zonal_layer_tbn = QToolButton()
        self.zonal_layer_tbn.setText('...')
        self.discard_nonmatching_chk = QCheckBox(
            'Discard zones with no points')
        self.discard_nonmatching_chk.setChecked(discard_nonmatching)
        self.zonal_layer_h_layout = QHBoxLayout()
        self.zonal_layer_h_layout.addWidget(self.zonal_layer_cbx)
        self.zonal_layer_h_layout.addWidget(self.zonal_layer_tbn)
        self.zonal_layer_gbx_v_layout.addWidget(self.zonal_layer_lbl)
        self.zonal_layer_gbx_v_layout.addLayout(self.zonal_layer_h_layout)
        self.zonal_layer_gbx_v_layout.addWidget(self.discard_nonmatching_chk)
        self.vlayout.addWidget(self.zonal_layer_gbx)
        self.zonal_layer_tbn.clicked.connect(self.open_load_zonal_layer_dialog)
        self.zonal_layer_cbx.currentIndexChanged[int].connect(
            self.on_zonal_layer_cbx_currentIndexChanged)
        self.zonal_layer_gbx.toggled[bool].connect(
            self.on_zonal_layer_gbx_toggled)
        self.iface.layerTreeView().currentLayerChanged.connect(
            self.on_currentLayerChanged)

    def on_currentLayerChanged(self):
        self.pre_populate_zonal_layer_cbx()

    def pre_populate_zonal_layer_cbx(self):
        # populate cbx only with vector layers containing polygons
        self.zonal_layer_cbx.clear()
        for key, layer in QgsProject.instance().mapLayers().items():
            if layer.type() != QgsMapLayer.VectorLayer:
                continue
            if layer.geometryType() == QgsWkbTypes.PolygonGeometry:
                self.zonal_layer_cbx.addItem(layer.name())
                self.zonal_layer_cbx.setItemData(
                    self.zonal_layer_cbx.count() - 1, layer.id())
        if self.added_zonal_layer is not None:
            self.zonal_layer_cbx.setCurrentIndex(
                self.zonal_layer_cbx.findData(self.added_zonal_layer.id()))
        self.zonal_layer_gbx.setChecked(
            self.zonal_layer_cbx.currentIndex() != -1)

    def on_zonal_layer_cbx_currentIndexChanged(self, new_index):
        self.zonal_layer = None
        if not self.zonal_layer_cbx.currentText():
            if self.zonal_layer_gbx.isChecked():
                self.ok_button.setEnabled(False)
            return
        zonal_layer_id = self.zonal_layer_cbx.itemData(new_index)
        self.zonal_layer = QgsProject.instance().mapLayer(zonal_layer_id)
        self.set_ok_button()

    def on_zonal_layer_gbx_toggled(self, is_checked):
        if is_checked and not self.zonal_layer_cbx.currentText():
            self.ok_button.setEnabled(False)
        else:
            self.set_ok_button()

    def on_output_type_changed(self):
        if self.output_type in OQ_TO_LAYER_TYPES:
            self.create_load_selected_only_ckb()
        self.set_ok_button()

    def on_rlz_or_stat_changed(self):
        self.dataset = self.npz_file[self.rlz_or_stat_cbx.currentText()]
        self.set_ok_button()

    def on_loss_type_changed(self):
        self.set_ok_button()

    def on_imt_changed(self):
        self.set_ok_button()

    def on_poe_changed(self):
        self.set_ok_button()

    def on_eid_changed(self):
        self.set_ok_button()

    def on_dmg_state_changed(self):
        self.set_ok_button()

    def populate_out_dep_widgets(self):
        self.populate_rlz_or_stat_cbx()
        self.show_num_sites()

    def get_taxonomies(self):
        raise NotImplementedError()

    def populate_rlz_or_stat_cbx(self):
        self.rlzs_or_stats = [
            key for key in sorted(self.npz_file)
            if key not in ('imtls', 'array')
        ]
        self.rlz_or_stat_cbx.clear()
        self.rlz_or_stat_cbx.setEnabled(True)
        self.rlz_or_stat_cbx.addItems(self.rlzs_or_stats)

    def populate_loss_type_cbx(self, loss_types):
        self.loss_type_cbx.clear()
        self.loss_type_cbx.setEnabled(True)
        self.loss_type_cbx.addItems(loss_types)

    def show_num_sites(self):
        # NOTE: we are assuming all realizations have the same number of sites,
        #       which currently is always true.
        #       If different realizations have a different number of sites, we
        #       need to move this block of code inside on_rlz_or_stat_changed()
        rlz_or_stat_data = self.npz_file[self.rlz_or_stat_cbx.currentText()]
        self.num_sites_lbl.setText(self.num_sites_msg % rlz_or_stat_data.shape)

    def set_ok_button(self):
        raise NotImplementedError()

    def build_layer_name(self, *args, **kwargs):
        raise NotImplementedError()

    def get_field_types(self, **kwargs):
        raise NotImplementedError()

    def read_npz_into_layer(self, field_types, **kwargs):
        raise NotImplementedError()

    def load_from_npz(self):
        raise NotImplementedError()

    def add_field_to_layer(self, field_name, field_type):
        # NOTE: add_attribute use the native qgis editing manager
        added_field_name = add_attribute(field_name, field_type, self.layer)
        return added_field_name

    def get_investigation_time(self):
        if self.output_type in ('hcurves', 'uhs', 'hmaps', 'ruptures'):
            try:
                investigation_time = self.npz_file['investigation_time']
            except KeyError as exc:
                msg = ('investigation_time not found. It is mandatory for %s.'
                       ' Please check if the ouptut was produced by an'
                       ' obsolete version of the OpenQuake Engine'
                       ' Server.') % self.output_type
                log_msg(msg,
                        level='C',
                        message_bar=self.iface.messageBar(),
                        exception=exc)
            else:
                # We must cast to 'str' to keep numerical padding
                # after saving the project
                return str(investigation_time)
        else:
            # some outputs do not need the investigation time
            return None

    def build_layer(self,
                    rlz_or_stat=None,
                    taxonomy=None,
                    poe=None,
                    loss_type=None,
                    dmg_state=None,
                    gsim=None,
                    imt=None,
                    boundaries=None,
                    geometry_type='point',
                    wkt_geom_type=None,
                    row_wkt_geom_types=None,
                    add_to_group=None,
                    add_to_map=True):
        layer_name = self.build_layer_name(rlz_or_stat=rlz_or_stat,
                                           taxonomy=taxonomy,
                                           poe=poe,
                                           loss_type=loss_type,
                                           dmg_state=dmg_state,
                                           gsim=gsim,
                                           imt=imt,
                                           geometry_type=geometry_type)
        field_types = self.get_field_types(rlz_or_stat=rlz_or_stat,
                                           taxonomy=taxonomy,
                                           poe=poe,
                                           loss_type=loss_type,
                                           dmg_state=dmg_state,
                                           imt=imt)

        # create layer
        self.layer = QgsVectorLayer("%s?crs=epsg:4326" % geometry_type,
                                    layer_name, "memory")
        modified_field_types = copy.copy(field_types)
        for field_name, field_type in field_types.items():
            if field_name in ['lon', 'lat', 'boundary']:
                continue
            added_field_name = self.add_field_to_layer(field_name, field_type)
            if field_name != added_field_name:
                if field_name == self.default_field_name:
                    self.default_field_name = added_field_name
                # replace field_name with the actual added_field_name
                del modified_field_types[field_name]
                modified_field_types[added_field_name] = field_type
        field_types = copy.copy(modified_field_types)

        self.layer = self.read_npz_into_layer(
            field_types,
            rlz_or_stat=rlz_or_stat,
            taxonomy=taxonomy,
            poe=poe,
            loss_type=loss_type,
            dmg_state=dmg_state,
            imt=imt,
            boundaries=boundaries,
            geometry_type=geometry_type,
            wkt_geom_type=wkt_geom_type,
            row_wkt_geom_types=row_wkt_geom_types)
        if (self.output_type == 'damages-rlzs'
                and not self.aggregate_by_site_ckb.isChecked()):
            self.layer.setCustomProperty('output_type', 'recovery_curves')
        else:
            self.layer.setCustomProperty('output_type', self.output_type)
        investigation_time = self.get_investigation_time()
        if investigation_time is not None:
            self.layer.setCustomProperty('investigation_time',
                                         investigation_time)
        if self.engine_version is not None:
            self.layer.setCustomProperty('engine_version', self.engine_version)
        irmt_version = get_irmt_version()
        self.layer.setCustomProperty('irmt_version', irmt_version)
        self.layer.setCustomProperty('calc_id', self.calc_id)
        if poe is not None:
            self.layer.setCustomProperty('poe', poe)
        user_params = {
            'rlz_or_stat': rlz_or_stat,
            'taxonomy': taxonomy,
            'poe': poe,
            'loss_type': loss_type,
            'dmg_state': dmg_state,
            'gsim': gsim,
            'imt': imt
        }
        write_metadata_to_layer(self.drive_engine_dlg, self.output_type,
                                self.layer, user_params)
        try:
            if (self.zonal_layer_cbx.currentText()
                    and self.zonal_layer_gbx.isChecked()):
                return
        except AttributeError:
            # the aggregation stuff might not exist for some loaders
            pass
        if add_to_map:
            if add_to_group:
                tree_node = add_to_group
            else:
                tree_node = QgsProject.instance().layerTreeRoot()
            if self.mode != 'testing':
                # NOTE: the following commented line would cause (unexpectedly)
                #       "QGIS died on signal 11" and double creation of some
                #       layers during integration tests
                QgsProject.instance().addMapLayer(self.layer, False)
            tree_node.insertLayer(0, self.layer)
            self.iface.setActiveLayer(self.layer)
            if add_to_group:
                # NOTE: zooming to group from caller function, to avoid
                #       repeating it once per layer
                pass
            else:
                self.iface.zoomToActiveLayer()
            log_msg('Layer %s was created successfully' % layer_name,
                    level='S',
                    message_bar=self.iface.messageBar())
        return self.layer

    @staticmethod
    def style_maps(layer,
                   style_by,
                   iface,
                   output_type='damages-rlzs',
                   perils=None,
                   add_null_class=False,
                   render_higher_on_top=False,
                   repaint=True,
                   use_sgc_style=False):
        symbol = QgsSymbol.defaultSymbol(layer.geometryType())
        # see properties at:
        # https://qgis.org/api/qgsmarkersymbollayerv2_8cpp_source.html#l01073
        symbol.setOpacity(1)
        if isinstance(symbol, QgsMarkerSymbol):
            # do it only for the layer with points
            symbol.symbolLayer(0).setStrokeStyle(Qt.PenStyle(Qt.NoPen))

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

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

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

                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                # jenks = natural breaks
                if Qgis.QGIS_VERSION_INT < 31000:
                    style_mode = QgsGraduatedSymbolRenderer.Pretty
                else:
                    style_mode = 'PrettyBreaks'
                try:
                    ramp_type_idx = default_color_ramp_names.index(
                        'SGC_Green2Red_Hmap_Color_Ramp')
                except ValueError:
                    raise ValueError(
                        'Color ramp SGC_Green2Red_Hmap_Color_Ramp was '
                        'not found. Please import it from '
                        'Settings -> Style Manager, loading '
                        'svir/resources/sgc_green2red_hmap_color_ramp.xml')
                inverted = False
                registry = QgsApplication.symbolLayerRegistry()
                symbol_props = {
                    'name': 'square',
                    'color': '0,0,0',
                    'color_border': '0,0,0',
                    'offset': '0,0',
                    'size': '1.5',  # FIXME
                    'angle': '0',
                }
                square = registry.symbolLayerMetadata(
                    "SimpleMarker").createSymbolLayer(symbol_props)
                symbol = QgsSymbol.defaultSymbol(layer.geometryType()).clone()
                symbol.deleteSymbolLayer(0)
                symbol.appendSymbolLayer(square)
                symbol.symbolLayer(0).setStrokeStyle(Qt.PenStyle(Qt.NoPen))
            elif output_type in ['asset_risk', 'input']:
                # options are EqualInterval, Quantile, Jenks, StdDev, Pretty
                # jenks = natural breaks
                if Qgis.QGIS_VERSION_INT < 31000:
                    style_mode = QgsGraduatedSymbolRenderer.EqualInterval
                else:
                    style_mode = 'EqualInterval'
                # exposure_strings = ['number', 'occupants', 'value']
                # setting exposure colors by default
                colors = {
                    'single': RAMP_EXTREME_COLORS['Blues']['top'],
                    'ramp_name': 'Blues'
                }
                inverted = False
                if output_type == 'asset_risk':
                    damage_strings = perils
                    for damage_string in damage_strings:
                        if damage_string in style_by:
                            colors = {
                                'single':
                                RAMP_EXTREME_COLORS['Spectral']['top'],
                                'ramp_name': 'Spectral'
                            }
                            inverted = True
                            break
                else:  # 'input'
                    colors = {
                        'single': RAMP_EXTREME_COLORS['Greens']['top'],
                        'ramp_name': 'Greens'
                    }
                    symbol.symbolLayer(0).setShape(
                        QgsSimpleMarkerSymbolLayerBase.Square)
                single_color = colors['single']
                ramp_name = colors['ramp_name']
                ramp_type_idx = default_color_ramp_names.index(ramp_name)
                symbol.setColor(QColor(single_color))
            else:
                raise NotImplementedError(
                    'Undefined color ramp for output type %s' % output_type)
            ramp = default_qgs_style.colorRamp(
                default_color_ramp_names[ramp_type_idx])
            if inverted:
                ramp.invert()
        # get unique values
        fni = layer.fields().indexOf(style_by)
        unique_values = layer.dataProvider().uniqueValues(fni)
        num_unique_values = len(unique_values - {NULL})
        if num_unique_values > 2:
            if Qgis.QGIS_VERSION_INT < 31000:
                renderer = QgsGraduatedSymbolRenderer.createRenderer(
                    layer, style_by, min(num_unique_values, style['classes']),
                    style_mode, symbol.clone(), ramp)
            else:
                renderer = QgsGraduatedSymbolRenderer(style_by, [])
                # NOTE: the following returns an instance of one of the
                #       subclasses of QgsClassificationMethod
                classification_method = \
                    QgsApplication.classificationMethodRegistry().method(
                        style_mode)
                renderer.setClassificationMethod(classification_method)
                renderer.updateColorRamp(ramp)
                renderer.updateSymbols(symbol.clone())
                renderer.updateClasses(
                    layer, min(num_unique_values, style['classes']))
            if not use_sgc_style:
                if Qgis.QGIS_VERSION_INT < 31000:
                    label_format = renderer.labelFormat()
                    # NOTE: the following line might be useful
                    # label_format.setTrimTrailingZeroes(True)
                    label_format.setPrecision(2)
                    renderer.setLabelFormat(label_format, updateRanges=True)
                else:
                    renderer.classificationMethod().setLabelPrecision(2)
                    renderer.calculateLabelPrecision()
        elif num_unique_values == 2:
            categories = []
            for unique_value in unique_values:
                symbol = symbol.clone()
                try:
                    symbol.setColor(
                        QColor(RAMP_EXTREME_COLORS[ramp_name]
                               ['bottom' if unique_value ==
                                min(unique_values) else 'top']))
                except Exception:
                    symbol.setColor(
                        QColor(style['color_from'] if unique_value ==
                               min(unique_values) else style['color_to']))
                category = QgsRendererCategory(unique_value, symbol,
                                               str(unique_value))
                # entry for the list of category items
                categories.append(category)
            renderer = QgsCategorizedSymbolRenderer(style_by, categories)
        else:
            renderer = QgsSingleSymbolRenderer(symbol.clone())
        if add_null_class and NULL in unique_values:
            # add a class for NULL values
            rule_renderer = QgsRuleBasedRenderer(symbol.clone())
            root_rule = rule_renderer.rootRule()
            not_null_rule = root_rule.children()[0].clone()
            # strip parentheses from stringified color HSL
            not_null_rule.setFilterExpression(
                '%s IS NOT NULL' % QgsExpression.quotedColumnRef(style_by))
            not_null_rule.setLabel('%s:' % style_by)
            root_rule.appendChild(not_null_rule)
            null_rule = root_rule.children()[0].clone()
            null_rule.setSymbol(
                QgsFillSymbol.createSimple({
                    'color': '160,160,160',
                    'style': 'diagonal_x'
                }))
            null_rule.setFilterExpression(
                '%s IS NULL' % QgsExpression.quotedColumnRef(style_by))
            null_rule.setLabel(tr('No points'))
            root_rule.appendChild(null_rule)
            if isinstance(renderer, QgsGraduatedSymbolRenderer):
                # create value ranges
                rule_renderer.refineRuleRanges(not_null_rule, renderer)
                # remove default rule
            elif isinstance(renderer, QgsCategorizedSymbolRenderer):
                rule_renderer.refineRuleCategoris(not_null_rule, renderer)
            for rule in rule_renderer.rootRule().children()[1].children():
                label = rule.label()
                # by default, labels are like:
                # ('"collapse-structural-ASH_DRY_sum" >= 0.0000 AND
                # "collapse-structural-ASH_DRY_sum" <= 2.3949')
                first, second = label.split(" AND ")
                bottom = first.rsplit(" ", 1)[1]
                top = second.rsplit(" ", 1)[1]
                simplified = "%s - %s" % (bottom, top)
                rule.setLabel(simplified)
            root_rule.removeChildAt(0)
            renderer = rule_renderer
        if render_higher_on_top:
            renderer.setUsingSymbolLevels(True)
            symbol_items = [item for item in renderer.legendSymbolItems()]
            for i in range(len(symbol_items)):
                sym = symbol_items[i].symbol().clone()
                key = symbol_items[i].ruleKey()
                for lay in range(sym.symbolLayerCount()):
                    sym.symbolLayer(lay).setRenderingPass(i)
                renderer.setLegendSymbolItem(key, sym)
        layer.setRenderer(renderer)
        if not use_sgc_style:
            layer.setOpacity(0.7)
        if repaint:
            layer.triggerRepaint()
            iface.setActiveLayer(layer)
            iface.zoomToActiveLayer()
            # NOTE QGIS3: probably not needed
            # iface.layerTreeView().refreshLayerSymbology(layer.id())
            iface.mapCanvas().refresh()

    def style_categorized(self, layer=None, style_by=None):
        if layer is None:
            layer = self.layer
        if style_by is None:
            style_by = self.default_field_name
        # get unique values
        fni = layer.fields().indexOf(style_by)
        unique_values = layer.dataProvider().uniqueValues(fni)
        # define categories
        categories = []
        for unique_value in unique_values:
            # initialize the default symbol for this geometry type
            symbol = QgsSymbol.defaultSymbol(layer.geometryType())
            # configure a symbol layer
            layer_style = {}
            layer_style['color'] = '%d, %d, %d' % (randrange(
                0, 256), randrange(0, 256), randrange(0, 256))
            layer_style['outline'] = '#000000'
            symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)
            # replace default symbol layer with the configured one
            if symbol_layer is not None:
                symbol.changeSymbolLayer(0, symbol_layer)
            # create renderer object
            category = QgsRendererCategory(unique_value, symbol,
                                           str(unique_value))
            # entry for the list of category items
            categories.append(category)
        # create renderer object
        renderer = QgsCategorizedSymbolRenderer(style_by, categories)
        # assign the created renderer to the layer
        if renderer is not None:
            layer.setRenderer(renderer)
        layer.triggerRepaint()

        # NOTE QGIS3: probably not needed
        # self.iface.layerTreeView().refreshLayerSymbology(layer.id())

        self.iface.mapCanvas().refresh()

    def style_curves(self):
        registry = QgsApplication.symbolLayerRegistry()
        symbol_props = {
            'name': 'cross2',
            'color': '0,0,0',
            'color_border': '0,0,0',
            'offset': '0,0',
            'size': '1.5',
            'angle': '0',
        }
        opacity = 0.7
        cross = registry.symbolLayerMetadata("SimpleMarker").createSymbolLayer(
            symbol_props)
        # NOTE: Cross symbols rendered for OQ-Engine disaggregation outputs are
        # opaque, wider and thicker than those used for other outputs (e.g.
        # hcurves)
        if self.output_type == 'disagg':
            cross.setSize(3)
            cross.setStrokeWidth(0.5)
            opacity = 1
        symbol = QgsSymbol.defaultSymbol(self.layer.geometryType()).clone()
        symbol.deleteSymbolLayer(0)
        symbol.appendSymbolLayer(cross)
        renderer = QgsSingleSymbolRenderer(symbol)
        effect = QgsOuterGlowEffect()
        effect.setSpread(0.5)
        effect.setOpacity(opacity)
        effect.setColor(QColor(255, 255, 255))
        effect.setBlurLevel(1)

        renderer.paintEffect().appendEffect(effect)
        renderer.paintEffect().setEnabled(True)

        self.layer.setRenderer(renderer)
        self.layer.setOpacity(opacity)
        self.layer.triggerRepaint()

        # NOTE QGIS3: probably not needed
        # self.iface.layerTreeView().refreshLayerSymbology(self.layer.id())

        self.iface.mapCanvas().refresh()

    def open_load_zonal_layer_dialog(self):
        """
        Open a file dialog to select the zonal layer to be loaded
        :returns: the zonal layer
        """
        text = self.tr('Select zonal layer to import')
        filters = self.tr('All files (*.*);;'
                          'GeoPackages (*.gpkg);;'
                          'Vector shapefiles (*.shp);;'
                          'SQLite (*.sqlite);;')
        default_dir = QSettings().value('irmt/select_layer_dir',
                                        QDir.homePath())
        file_name, _ = QFileDialog.getOpenFileName(self, text, default_dir,
                                                   filters)
        if not file_name:
            return None
        selected_dir = QFileInfo(file_name).dir().path()
        QSettings().setValue('irmt/select_layer_dir', selected_dir)
        zonal_layer = self.load_zonal_layer(file_name)
        return zonal_layer

    def load_zonal_layer(self, zonal_layer_path):
        self.added_zonal_layer = zonal_layer = None
        zonal_layer_basename, zonal_layer_ext = os.path.splitext(
            os.path.basename(zonal_layer_path))
        if zonal_layer_ext == '.gpkg':
            dlg = QgsSublayersDialog(QgsSublayersDialog.Ogr,
                                     'Select zonal layer')
            conn = ogr.Open(zonal_layer_path)
            layer_defs = []
            for idx, c in enumerate(conn):
                ld = QgsSublayersDialog.LayerDefinition()
                ld.layerId = idx
                ld.layerName = c.GetDescription()
                ld.count = c.GetFeatureCount()
                ld.type = ogr.GeometryTypeToName(c.GetGeomType())
                layer_defs.append(ld)
            dlg.populateLayerTable(layer_defs)
            dlg.exec_()
            if not dlg.selection():
                return None
            for sel in dlg.selection():
                # NOTE: the last one will be chosen as zonal layer
                zonal_layer = QgsVectorLayer(
                    zonal_layer_path + "|layername=" + sel.layerName,
                    sel.layerName, 'ogr')
                if zonal_layer.isValid():
                    root = QgsProject.instance().layerTreeRoot()
                    QgsProject.instance().addMapLayer(zonal_layer, False)
                    root.insertLayer(0, zonal_layer)
                else:
                    msg = 'Invalid layer'
                    log_msg(msg,
                            level='C',
                            message_bar=self.iface.messageBar())
                    return None
        else:
            zonal_layer = QgsVectorLayer(zonal_layer_path,
                                         zonal_layer_basename, 'ogr')
        if not zonal_layer.geometryType() == QgsWkbTypes.PolygonGeometry:
            msg = 'Zonal layer must contain zone polygons'
            log_msg(msg, level='C', message_bar=self.iface.messageBar())
            return None
        if zonal_layer_ext != '.gpkg':
            # Add zonal layer to registry
            if zonal_layer.isValid():
                root = QgsProject.instance().layerTreeRoot()
                QgsProject.instance().addMapLayer(zonal_layer, False)
                root.insertLayer(0, zonal_layer)
            else:
                msg = 'Invalid zonal layer'
                log_msg(msg, level='C', message_bar=self.iface.messageBar())
                return None
        self.added_zonal_layer = zonal_layer
        self.pre_populate_zonal_layer_cbx()
        return zonal_layer

    def populate_zonal_layer_cbx(self, zonal_layer):
        cbx = self.zonal_layer_cbx
        cbx.addItem(zonal_layer.name())
        last_index = cbx.count() - 1
        cbx.setItemData(last_index, zonal_layer.id())
        cbx.setCurrentIndex(last_index)

    def show_file_size(self):
        file_size = get_file_size(self.path)
        self.file_size_lbl.setText(self.file_size_msg % file_size)

    def accept(self):
        log_msg('Loading output started. Watch progress in QGIS task bar',
                level='I',
                message_bar=self.iface.messageBar())
        try:
            self.iface.layerTreeView().currentLayerChanged.disconnect(
                self.on_currentLayerChanged)
        except Exception:
            # it's connected only for some loaders
            pass
        self.hide()
        if self.output_type in OQ_EXTRACT_TO_LAYER_TYPES:
            self.load_from_npz()
            if self.output_type in ('avg_losses-rlzs', 'damages-rlzs',
                                    'avg_losses-stats'):
                # check if also aggregating by zone or not
                if (not self.zonal_layer_cbx.currentText()
                        or not self.zonal_layer_gbx.isChecked()):
                    super().accept()
                    return
                self.aggregate_by_zone()
            else:
                super().accept()
        elif self.output_type in OQ_CSV_TO_LAYER_TYPES:
            self.load_from_csv()
            super().accept()

    def aggregate_by_zone(self):
        loss_layer = self.layer
        zonal_layer_id = self.zonal_layer_cbx.itemData(
            self.zonal_layer_cbx.currentIndex())
        zonal_layer = QgsProject.instance().mapLayer(zonal_layer_id)
        QgsProject.instance().layerTreeRoot().findLayer(
            zonal_layer.id()).setItemVisibilityChecked(False)
        # if the two layers have different projections, display a
        # warning, but try proceeding anyway
        have_same_projection, check_projection_msg = ProcessLayer(
            loss_layer).has_same_projection_as(zonal_layer)
        if not have_same_projection:
            log_msg(check_projection_msg,
                    level='W',
                    message_bar=self.iface.messageBar())
        try:
            [self.loss_attr_name
             ] = [field.name() for field in loss_layer.fields()]
        except ValueError:
            self.loss_attr_name = self.default_field_name
        zonal_layer_plus_sum_name = "%s: %s_sum" % (zonal_layer.name(),
                                                    self.loss_attr_name)
        discard_nonmatching = self.discard_nonmatching_chk.isChecked()
        try:
            calculate_zonal_stats(self.on_calculate_zonal_stats_completed,
                                  zonal_layer,
                                  loss_layer, [self.loss_attr_name],
                                  zonal_layer_plus_sum_name,
                                  discard_nonmatching=discard_nonmatching,
                                  predicates=('intersects', ),
                                  summaries=('sum', ))
        except Exception as exc:
            log_msg(str(exc),
                    level='C',
                    message_bar=self.iface.messageBar(),
                    exception=exc)

    def on_calculate_zonal_stats_completed(self, zonal_layer_plus_sum):
        if zonal_layer_plus_sum is None:
            msg = 'The calculation of zonal statistics was not completed'
            log_msg(msg, level='C', message_bar=self.iface.messageBar())
            return None
        # Add zonal layer to registry
        if zonal_layer_plus_sum.isValid():
            root = QgsProject.instance().layerTreeRoot()
            QgsProject.instance().addMapLayer(zonal_layer_plus_sum, False)
            root.insertLayer(0, zonal_layer_plus_sum)
        else:
            msg = 'The layer aggregating data by zone is invalid.'
            log_msg(msg, level='C', message_bar=self.iface.messageBar())
            return None
        # NOTE: in scenario damage, keys are like
        #       u'structural_no_damage_mean', and not just
        #       u'structural', therefore we can't just use the selected
        #       loss type, but we must use the actual only key in the
        #       dict
        added_loss_attr = "%s_sum" % self.loss_attr_name
        style_by = added_loss_attr
        try:
            perils = self.perils
        except AttributeError:
            perils = None
        self.style_maps(zonal_layer_plus_sum,
                        style_by,
                        self.iface,
                        self.output_type,
                        perils=perils,
                        add_null_class=True)
        super().accept()

    def reject(self):
        try:
            self.iface.layerTreeView().currentLayerChanged.disconnect(
                self.on_currentLayerChanged)
        except Exception:
            # it's connected only for some loaders
            pass
        super().reject()
class NEDOriginDrawer:
    def __init__(self, proj, vehicle_info):
        self.proj = proj
        self.vehicle_info = vehicle_info
        self.ned_origin_layer = QgsVectorLayer("Point?crs=epsg:4326",
                                               "NED Origin", "memory")
        self.feat = QgsFeature()
        self.ned_origin_layer.dataProvider().addFeature(self.feat)
        self.ned_origin_layer.setRenderer(self.ned_origin_renderer())
        self.ned_origin_layer.setCustomProperty("ned_origin", "NED Origin")

        self.ned_lat = None
        self.ned_lon = None

    def ned_origin_renderer(self):
        symbol = QgsSymbol.defaultSymbol(self.ned_origin_layer.geometryType())
        svg_style = {
            'fill': '# 0000ff',
            'name': ':/resources/Star2.svg',
            'outline': '#000000',
            'outline - width': '6.8',
            'size': '6'
        }
        # create svg symbol layer
        sym_lyr1 = QgsSvgMarkerSymbolLayer.create(svg_style)
        # Replace the default layer with our custom layer
        symbol.deleteSymbolLayer(0)
        symbol.appendSymbolLayer(sym_lyr1)
        # Replace the renderer of the current layer
        renderer = QgsSingleSymbolRenderer(symbol)
        return renderer

    def update_ned_point(self):
        try:
            self.ned_lat = float(
                cola2_interface.get_ros_param(
                    self.vehicle_info.get_vehicle_ip(), 9091,
                    self.vehicle_info.get_vehicle_namespace() +
                    '/navigator/ned_latitude')['value'])
            self.ned_lon = float(
                cola2_interface.get_ros_param(
                    self.vehicle_info.get_vehicle_ip(), 9091,
                    self.vehicle_info.get_vehicle_namespace() +
                    '/navigator/ned_longitude')['value'])

            # layer is not yet in the project, add it
            if len(self.proj.mapLayersByName('NED Origin')) == 0:
                self.feat.setGeometry(
                    QgsGeometry.fromPointXY(
                        QgsPointXY(self.ned_lon, self.ned_lat)))
                self.ned_origin_layer.dataProvider().addFeatures([self.feat])
                self.proj.addMapLayer(self.ned_origin_layer, True)
            else:
                self.ned_origin_layer.startEditing()
                self.ned_origin_layer.moveVertex(self.ned_lon, self.ned_lat,
                                                 self.feat.id() + 1, 0)
                self.ned_origin_layer.commitChanges()

        except:
            QMessageBox.warning(None, "NED Origin update",
                                "Could not read NED origin topic.",
                                QMessageBox.Close)