def _buildSymbolWidget(self, spColumnName): """ Build symbol widget based on geometry type. """ geomType, srid = geometryType(self._dsName, spColumnName) if geomType == "": return None vlGeomConfig = "{0}?crs=epsg:{1!s}".format(geomType, srid) vl = QgsVectorLayer(vlGeomConfig, "stdm_proxy", "memory") if geomType == "POINT": symbolEditor = QgsSimpleMarkerSymbolLayerV2Widget(vl) elif geomType == "LINESTRING": symbolEditor = QgsSimpleLineSymbolLayerV2Widget.create(vl) elif geomType == "POLYGON": symbolEditor = SimpleFillSymbolLayerProxyWidget(vl) else: return None return (symbolEditor, geomType, srid)
def _buildSymbolWidget(self,spColumnName): """ Build symbol widget based on geometry type. """ geomType,srid = geometryType(self._dsName,spColumnName) if geomType == "": return None vlGeomConfig = "{0}?crs=epsg:{1!s}".format(geomType,srid) vl = QgsVectorLayer(vlGeomConfig, "stdm_proxy", "memory") if geomType == "POINT": symbolEditor = QgsSimpleMarkerSymbolLayerV2Widget(vl) elif geomType == "LINESTRING": symbolEditor = QgsSimpleLineSymbolLayerV2Widget.create(vl) elif geomType == "POLYGON": symbolEditor = SimpleFillSymbolLayerProxyWidget(vl) else: return None return (symbolEditor,geomType,srid)
def _build_symbol_widget(self, sp_column_name): """ Build symbol widget based on geometry type. """ geom_type,srid = geometryType(self._ds_name, sp_column_name) if not geom_type: return None vlGeomConfig = "{0}?crs=epsg:{1!s}".format(geom_type, srid) vl = QgsVectorLayer(vlGeomConfig, "stdm_proxy", "memory") if geom_type == "POINT" or geom_type == "MULTIPOINT": symbol_editor = _SimpleMarkerSymbolLayerProxyWidget(vl) elif geom_type == "LINESTRING" or geom_type == "MULTILINESTRING": symbol_editor = _SimpleLineSymbolLayerProxyWidget(vl) elif geom_type == "POLYGON" or geom_type == "MULTIPOLYGON": symbol_editor = _SimpleFillSymbolLayerProxyWidget(vl) else: return None, "", -1 return (symbol_editor,geom_type,srid)
def featToDb(self, targettable , columnmatch, append, parentdialog, geomColumn=None, geomCode=-1, translator_manager=ValueTranslatorManager()): """ Performs the data import from the source layer to the STDM database. :param targettable: Destination table name :param columnmatch: Dictionary containing source columns as keys and target columns as the values. :param append: True to append, false to overwrite by deleting previous records :param parentdialog: A reference to the calling dialog. :param translator_manager: Instance of 'stdm.data.importexport.ValueTranslatorManager' containing value translators defined for the destination table columns. :type translator_manager: ValueTranslatorManager """ #Delete existing rows in the target table if user has chosen to overwrite if not append: delete_table_data(targettable) """ #Debug logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', filename='stdm.log', filemode='w') """ #Container for mapping column names to their corresponding values column_value_mapping = {} lyr = self.getLayer() lyr.ResetReading() feat_defn = lyr.GetLayerDefn() numFeat = lyr.GetFeatureCount() #Configure progress dialog init_val = 0 progress = QProgressDialog("", "&Cancel", init_val, numFeat, parentdialog) progress.setWindowModality(Qt.WindowModal) lblMsgTemp = "Importing {0} of {1} to STDM..." for feat in lyr: column_count = 0 progress.setValue(init_val) progressMsg = lblMsgTemp.format((init_val + 1), numFeat) progress.setLabelText(progressMsg) if progress.wasCanceled(): break for f in range(feat_defn.GetFieldCount()): field_defn = feat_defn.GetFieldDefn(f) field_name = field_defn.GetNameRef() #Append value only if it has been defined by the user if field_name in columnmatch: dest_column = columnmatch[field_name] field_value = feat.GetField(f) ''' Check if there is a value translator defined for the specified destination column. ''' value_translator = translator_manager.translator(dest_column) if not value_translator is None: source_col_names = value_translator.source_column_names() field_value_mappings = self._map_column_values(feat, feat_defn, source_col_names) field_value = value_translator.referencing_column_value(field_value_mappings) if not isinstance(field_value, IgnoreType): column_value_mapping[dest_column] = field_value column_count += 1 #Create mapped table only once if self._mapped_class is None: #Execute only once, when all fields have been iterated if column_count == len(columnmatch): #Add geometry column to the mapping if geomColumn is not None: column_value_mapping[geomColumn] = None #Use geometry column SRID in the target table self._geomType,self._targetGeomColSRID = geometryType(targettable, geomColumn) mapped_class = self._get_mapped_class(targettable) if mapped_class is None: msg = QApplication.translate("OGRReader", "Something happened that caused the database table " \ "not to be mapped to the corresponding model class. Please try again.") raise RuntimeError(msg) self._mapped_class = mapped_class #Only insert geometry if it has been defined by the user if geomColumn is not None: geom = feat.GetGeometryRef() if geom is not None: #Get geometry in WKT/WKB format geomWkb = geom.ExportToWkt() column_value_mapping[geomColumn] = "SRID={0!s};{1}".format(self._targetGeomColSRID,geomWkb) #Check if the geometry types match layerGeomType = geom.GetGeometryName() if layerGeomType.lower() != self._geomType.lower(): raise TypeError("The geometries of the source and destination columns do not match.\n" \ "Source Geometry Type: {0}, Destination Geometry Type: {1}".format(layerGeomType, self._geomType)) return #logging.debug() try: #Insert the record self._insertRow(column_value_mapping) except: progress.close() raise init_val+=1 progress.setValue(numFeat)
def featToDb(self,targettable,columnmatch,append,parentdialog,geomColumn=None,geomCode=-1): ''' Performs the data import from the source layer to the STDM database. :param targettable: Destination table name :param columnmatch: Dictionary containing source columns as keys and target columns as the values :param append: True to append, false to overwrite by deleting previous records :param parentdialog: A reference to the calling dialog ''' #Delete existing rows if user has chosen to overwrite if not append: delete_table_data(targettable) """ #Debug logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', filename='stdm.log', filemode='w') """ #Container for mapping column names to their corresponding values columnValueMapping = {} lyr = self.getLayer() lyr.ResetReading() feat_defn = lyr.GetLayerDefn() numFeat = lyr.GetFeatureCount() #Configure progress dialog initVal = 0 progress = QProgressDialog("","&Cancel",initVal,numFeat,parentdialog) progress.setWindowModality(Qt.WindowModal) lblMsgTemp = "Importing {0} of {1} to STDM..." for feat in lyr: progress.setValue(initVal) progressMsg = lblMsgTemp.format(str(initVal+1),str(numFeat)) progress.setLabelText(progressMsg) if progress.wasCanceled(): break for f in range(feat_defn.GetFieldCount()): field_defn = feat_defn.GetFieldDefn(f) field_name = field_defn.GetNameRef() #Append value only if it has been defined by the user if field_name in columnmatch: fieldValue = feat.GetField(f) columnValueMapping[columnmatch[field_name]] = fieldValue #Create mapped table only once if self._mappedClass == None: #Execute only once, when all fields have been iterated if len(columnValueMapping.keys()) == len(columnmatch): #Add geometry column to the mapping if geomColumn is not None: columnValueMapping[geomColumn] = None #Use geometry column SRID in the target table self._geomType,self._targetGeomColSRID = geometryType(targettable,geomColumn) try: primaryMapper = class_mapper(_ReflectedModel) except (sqlalchemy.orm.exc.UnmappedClassError,sqlalchemy.exc.ArgumentError): #Reflect table and map it to '_ReflectedModel' class only once mapper(_ReflectedModel,self._mapTable(targettable)) self._mappedClass = _ReflectedModel #Only insert geometry if it has been defined by the user if geomColumn is not None: geom = feat.GetGeometryRef() if geom is not None: #Get geometry in WKT/WKB format geomWkb = geom.ExportToWkt() columnValueMapping[geomColumn] = "SRID={0!s};{1}".format(self._targetGeomColSRID,geomWkb) #Check if the geometry types match layerGeomType = geom.GetGeometryName() if layerGeomType.lower() != self._geomType.lower(): raise TypeError("The geometries of the source and destination columns do not match.\n" \ "Source Geometry Type: {0}, Destination Geometry Type: {1}".format(layerGeomType, self._geomType)) return #logging.debug() try: #Insert the record self._insertRow(columnValueMapping) except: progress.close() raise initVal+=1 progress.setValue(numFeat)
def draw_spatial_unit(self, model, clear_existing=True): """ Draw geometry of the given model in the respective local and web views. :param model: Source model whose geometry will be drawn. :type model: object :param clear_existing: Clears any existing features prior to adding the new features. :type clear_existing: bool """ if model is None: msg = QApplication.translate("SpatialPreview", "Data model is empty, the spatial " "unit cannot be rendered.") QMessageBox.critical(self, QApplication.translate("SpatialPreview", "Spatial Unit Preview"), msg) return table_name = model.__class__.__name__.replace(' ', '_').lower() if not pg_table_exists(table_name): msg = QApplication.translate("SpatialPreview", "The spatial unit data source could " "not be retrieved, the feature cannot " "be rendered.") QMessageBox.critical(self, QApplication.translate("SpatialPreview", "Spatial Unit Preview"), msg) return spatial_cols = table_column_names(table_name, True) geom, geom_col = None, "" for sc in spatial_cols: geom = getattr(model, sc) #Use the first non-empty geometry value in the collection if not geom is None: geom_col = sc if geom is None: msg = QApplication.translate("SpatialPreview", "The selected spatial unit does not " "contain a valid geometry.") QMessageBox.critical(self, QApplication.translate("SpatialPreview", "Spatial Unit Preview"), msg) return geom_type, epsg_code = geometryType(table_name, geom_col) if self._overlay_layer is None: self._create_vector_layer(geom_type, epsg_code) #Add layer to map QgsMapLayerRegistry.instance().addMapLayer(self._overlay_layer, False) #Ensure it is always added on top QgsProject.instance().layerTreeRoot().insertLayer(0, self._overlay_layer) if clear_existing: self.delete_local_features() feat, extent = self._add_geom_to_map(geom) #Add spatial unit to web viewer self._web_spatial_loader.add_overlay(model, geom_col) #Increase bounding box by 50%, so that layer slightly zoomed out extent.scale(1.5) #Select feature. Hack for forcing a selection by using inversion self._overlay_layer.invertSelection() self._iface.mapCanvas().setExtent(extent) self._iface.mapCanvas().refresh() self.refresh_canvas_layers() #Need to force event so that layer is shown QCoreApplication.sendEvent(self.local_map, QShowEvent())
def db2Feat(self,parent,table,results,columns,geom=""): #Execute the export process #Create driver drv = ogr.GetDriverByName(self.getDriverName()) if drv is None: raise Exception("{0} driver not available.".format(self.getDriverName())) #Create data source self._ds = drv.CreateDataSource(self._targetFile) if self._ds is None: raise Exception("Creation of output file failed.") #Create layer if geom != "": pgGeomType,srid = geometryType(table,geom) geomType = wkbTypes[pgGeomType] else: geomType=ogr.wkbNone lyr = self._ds.CreateLayer(self.getLayerName(),None,geomType) if lyr is None: raise Exception("Layer creation failed") #Create fields for c in columns: #SQLAlchemy string values are in unicode so decoding is required in order to use in OGR encodedFieldName = c.encode('utf-8') field_defn = self.createField(table,encodedFieldName) if lyr.CreateField(field_defn) != 0: raise Exception("Creating %s field failed"%(c)) #Add Geometry column to list for referencing in the result set if geom != "": columns.append(geom) featGeom=None #Configure progress dialog initVal=0 numFeat = results.rowcount progress = QProgressDialog("","&Cancel",initVal,numFeat,parent) progress.setWindowModality(Qt.WindowModal) lblMsgTemp = "Writing {0} of {1} to file..." #Iterate the result set for r in results: #Progress dialog progress.setValue(initVal) progressMsg = lblMsgTemp.format(str(initVal+1),str(numFeat)) progress.setLabelText(progressMsg) if progress.wasCanceled(): break #Create OGR Feature feat = ogr.Feature(lyr.GetLayerDefn()) for i in range(len(columns)): colName = columns[i] #Check if its the geometry column in the iteration if colName==geom: if r[i] is not None: featGeom=ogr.CreateGeometryFromWkt(r[i]) else: featGeom=ogr.CreateGeometryFromWkt("") feat.SetGeometry(featGeom) else: fieldValue = r[i] if isinstance(fieldValue,unicode): fieldValue = fieldValue.encode('utf-8') feat.SetField(i,fieldValue) if lyr.CreateFeature(feat) != 0: progress.close() raise Exception("Failed to create feature in %s"%(self._targetFile)) if featGeom is not None: featGeom.Destroy() feat.Destroy() initVal+=1 progress.setValue(numFeat)
def run(self, *args, **kwargs): """ :param templatePath: The file path to the user-defined template. :param entityFieldName: The name of the column for the specified entity which must exist in the data source view or table. :param entityFieldValue: The value for filtering the records in the data source view or table. :param outputMode: Whether the output composition should be an image or PDF. :param filePath: The output file where the composition will be written to. Applies to single mode output generation. :param dataFields: List containing the field names whose values will be used to name the files. This is used in multiple mode configuration. :param fileExtension: The output file format. Used in multiple mode configuration. :param data_source: Name of the data source table or view whose row values will be used to name output files if the options has been specified by the user. """ templatePath = args[0] entityFieldName = args[1] entityFieldValue = args[2] outputMode = args[3] filePath = kwargs.get("filePath", None) dataFields = kwargs.get("dataFields", []) fileExtension = kwargs.get("fileExtension", "") data_source = kwargs.get("data_source", "") templateFile = QFile(templatePath) if not templateFile.open(QIODevice.ReadOnly): return False, QApplication.translate("DocumentGenerator", "Cannot read template file.") templateDoc = QDomDocument() if templateDoc.setContent(templateFile): composerDS = ComposerDataSource.create(templateDoc) spatialFieldsConfig = SpatialFieldsConfiguration.create(templateDoc) composerDS.setSpatialFieldsConfig(spatialFieldsConfig) #Check if data source exists and return if it doesn't if not self.data_source_exists(composerDS): msg = QApplication.translate("DocumentGenerator", u"'{0}' data source does not exist in the database." u"\nPlease contact your database " u"administrator.".format(composerDS.name())) return False, msg #TODO: Need to automatically register custom configuration collections #Photo config collection ph_config_collection = PhotoConfigurationCollection.create(templateDoc) #Table configuration collection table_config_collection = TableConfigurationCollection.create(templateDoc) #Create chart configuration collection object chart_config_collection = ChartConfigurationCollection.create(templateDoc) #Load the layers required by the table composer items self._table_mem_layers = load_table_layers(table_config_collection) #Execute query dsTable,records = self._exec_query(composerDS.name(), entityFieldName, entityFieldValue) if records is None or len(records) == 0: return False, QApplication.translate("DocumentGenerator", "No matching records in the database") """ Iterate through records where a single file output will be generated for each matching record. """ for rec in records: composition = QgsComposition(self._map_renderer) composition.loadFromTemplate(templateDoc) #Set value of composer items based on the corresponding db values for composerId in composerDS.dataFieldMappings().reverse: #Use composer item id since the uuid is stripped off composerItem = composition.getComposerItemById(composerId) if not composerItem is None: fieldName = composerDS.dataFieldName(composerId) fieldValue = getattr(rec,fieldName) self._composeritem_value_handler(composerItem, fieldValue) #Extract photo information self._extract_photo_info(composition, ph_config_collection, rec) #Set table item values based on configuration information self._set_table_data(composition, table_config_collection, rec) #Refresh non-custom map composer items self._refresh_composer_maps(composition, spatialFieldsConfig.spatialFieldsMapping().keys()) #Create memory layers for spatial features and add them to the map for mapId,spfmList in spatialFieldsConfig.spatialFieldsMapping().iteritems(): map_item = composition.getComposerItemById(mapId) if not map_item is None: #Clear any previous map memory layer self.clear_temporary_map_layers() for spfm in spfmList: #Use the value of the label field to name the layer lbl_field = spfm.labelField() spatial_field = spfm.spatialField() if not spatial_field: continue if lbl_field: if hasattr(rec, spfm.labelField()): layerName = getattr(rec, spfm.labelField()) else: layerName = self._random_feature_layer_name(spatial_field) else: layerName = self._random_feature_layer_name(spatial_field) #Extract the geometry using geoalchemy spatial capabilities geom_value = getattr(rec, spatial_field) if geom_value is None: continue geom_func = geom_value.ST_AsText() geomWKT = self._dbSession.scalar(geom_func) #Get geometry type geom_type, srid = geometryType(composerDS.name(), spatial_field) #Create reference layer with feature ref_layer = self._build_vector_layer(layerName, geom_type, srid) if ref_layer is None or not ref_layer.isValid(): continue #Add feature bbox = self._add_feature_to_layer(ref_layer, geomWKT) bbox.scale(spfm.zoomLevel()) #Workaround for zooming to single point extent if ref_layer.wkbType() == QGis.WKBPoint: canvas_extent = self._iface.mapCanvas().fullExtent() cnt_pnt = bbox.center() canvas_extent.scale(1.0/32, cnt_pnt) bbox = canvas_extent #Style layer based on the spatial field mapping symbol layer symbol_layer = spfm.symbolLayer() if not symbol_layer is None: ref_layer.rendererV2().symbols()[0].changeSymbolLayer(0,spfm.symbolLayer()) ''' Add layer to map and ensure its always added at the top ''' QgsMapLayerRegistry.instance().addMapLayer(ref_layer, False) QgsProject.instance().layerTreeRoot().insertLayer(0, ref_layer) self._iface.mapCanvas().setExtent(bbox) self._iface.mapCanvas().refresh() #Add layer to map memory layer list self._map_memory_layers.append(ref_layer) ''' Use root layer tree to get the correct ordering of layers in the legend ''' self._refresh_map_item(map_item) #Extract chart information and generate chart self._generate_charts(composition, chart_config_collection, rec) #Build output path and generate composition if not filePath is None and len(dataFields) == 0: self._write_output(composition, outputMode, filePath) elif filePath is None and len(dataFields) > 0: docFileName = self._build_file_name(data_source, entityFieldName, entityFieldValue, dataFields, fileExtension) if not docFileName: return (False, QApplication.translate("DocumentGenerator", "File name could not be generated from the data fields.")) outputDir = self._composer_output_path() if outputDir is None: return (False, QApplication.translate("DocumentGenerator", "System could not read the location of the output directory in the registry.")) qDir = QDir() if not qDir.exists(outputDir): return (False, QApplication.translate("DocumentGenerator", "Output directory does not exist")) absDocPath = u"{0}/{1}".format(outputDir, docFileName) self._write_output(composition, outputMode, absDocPath) #Clear temporary layers self.clear_temporary_layers() return True, "Success" return False, "Document composition could not be generated"