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
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)
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)
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
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
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 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 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()
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)
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())
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
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)
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)
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)
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 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())
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
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
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)