def reloadPlugins(self): self.modules = [] self.plugins = [] if self.allPlugins: plugin_dir = QDir( os.path.join(os.path.dirname(QFile.decodeName(__file__)), "plugins")) plugins = plugin_dir.entryList(QDir.Dirs | QDir.NoSymLinks | QDir.NoDotAndDotDot) else: p = QSettings().value("/PhyloGeoRec/plugins", "", type=unicode) plugins = p.split(",") if p else [] import importlib for name in plugins: try: modname = "PhyloGeoRec.plugins." + str(name) module = reload( sys.modules[modname] ) if modname in sys.modules else importlib.import_module( modname) self.modules.append(module) self.plugins.append(getattr(module, "plugin_class")) except ImportError: logMessage("Failed to load plugin: " + str(name))
def properties(self): p = {} for w in self.propertyWidgets: v = None if isinstance(w, QComboBox): index = w.currentIndex() if index == -1: v = None else: v = w.itemData(index) elif isinstance(w, QRadioButton): if not w.isChecked(): continue v = w.isChecked() elif isinstance(w, QCheckBox): v = w.isChecked() elif isinstance(w, (QSlider, QSpinBox)): v = w.value() elif isinstance(w, QLineEdit): v = w.text() elif isinstance(w, StyleWidget): v = w.values() else: logMessage("[propertypages.py] Not recognized widget type: " + unicode(type(w))) p[w.objectName()] = v return p
def _readTransparency(self, widgetValues, f=None): vals = widgetValues if vals["comboData"] == TransparencyWidgetFunc.VALUE: try: return int(vals["editText"]) except ValueError: return 0 alpha = None symbol = self.layer.rendererV2().symbolForFeature(f) if symbol is None: logMessage(u'Symbol for feature cannot be found: {0}'.format(self.layer.name())) symbol = self.layer.rendererV2().symbols()[0] else: sl = symbol.symbolLayer(0) if sl: # and sl.hasDataDefinedProperties(): expr = sl.dataDefinedProperty("color") if expr: # data defined transparency cs_rgba = expr.evaluate(f, f.fields()) rgba = cs_rgba.split(",") if len(rgba) == 4: alpha = float(rgba[3]) / 255 if alpha is None: alpha = symbol.alpha() opacity = float(100 - self.layer.layerTransparency()) / 100 opacity *= alpha # opacity = layer_opacity * feature_opacity return int((1.0 - opacity) * 100)
def values(self, f=None): vals = [] for i in range(32): # big number for style count p = "styleWidget" + str(i) if p not in self.properties: break widgetValues = self.properties[p] if len(widgetValues) == 0: break widgetType = widgetValues["type"] comboData = widgetValues.get("comboData") if widgetType in [StyleWidget.COLOR, StyleWidget.OPTIONAL_COLOR]: vals.append(self._readColor(widgetValues, f, widgetType == StyleWidget.OPTIONAL_COLOR)) elif widgetType == StyleWidget.COLOR_TEXTURE: if comboData == ColorTextureWidgetFunc.MAP_CANVAS: vals.append(comboData) elif comboData == ColorTextureWidgetFunc.LAYER: vals.append(widgetValues.get("layerIds", [])) else: vals.append(self._readColor(widgetValues, f)) elif widgetType == StyleWidget.TRANSPARENCY: vals.append(self._readTransparency(widgetValues, f)) elif widgetType == StyleWidget.FILEPATH: if comboData == FilePathWidgetFunc.FILEPATH or f is None: vals.append(widgetValues["editText"]) else: # prefix + attribute fieldName = widgetValues["comboText"].strip('"') value = f.attribute(fieldName) if value == NULL: value = "" logMessage(u"Empty attribute value in the field '{0}'".format(fieldName)) vals.append(os.path.join(widgetValues["editText"], value.strip('"'))) elif widgetType == StyleWidget.CHECKBOX: vals.append(widgetValues["checkBox"]) elif widgetType == StyleWidget.HEIGHT: if widgetValues["comboData"] in [HeightWidgetFunc.RELATIVE, HeightWidgetFunc.ABSOLUTE, HeightWidgetFunc.Z_VALUE] or f is None: vals.append(self.toFloat(widgetValues["editText"])) else: # attribute value + addend fieldName = widgetValues["comboText"].lstrip("+").strip(' "') vals.append(self.toFloat(f.attribute(fieldName)) + self.toFloat(widgetValues["editText"])) else: if comboData == FieldValueWidgetFunc.ABSOLUTE or f is None: vals.append(widgetValues["editText"]) else: # attribute value * multiplier fieldName = widgetValues["comboText"].strip('"') val = self.toFloat(f.attribute(fieldName)) * self.toFloat(widgetValues["editText"]) vals.append(str(val)) return vals
def saveExportSettings(self, filename): import codecs import json try: with codecs.open(filename, "w", "UTF-8") as f: json.dump(self.exportSettings, f, ensure_ascii=False, indent=2, sort_keys=True) return True except Exception as e: logMessage("Failed to save export settings: " + str(e)) return False
def demProviderByLayerId(self, id): if not id: return FlatDEMProvider() if id.startswith("plugin:"): provider = self.pluginManager.findDEMProvider(id[7:]) if provider: return provider(str(self.crs.toWkt())) logMessage('Plugin "{0}" not found'.format(id)) return FlatDEMProvider() else: layer = QgsMapLayerRegistry.instance().mapLayer(id) return GDALDEMProvider(layer.source(), str(self.crs.toWkt()), source_wkt=str(layer.crs().toWkt())) # use CRS set to the layer in QGIS
def write(self, f): #TODO: separated image files (not in localBrowsingMode) if len(self._list) == 0: return f.write(u'\n// Base64 encoded images\n') for index, image in enumerate(self._list): imageType = image[0] if imageType == self.IMAGE_FILE: image_path = image[1] exists = os.path.exists(image_path) if exists and os.path.isfile(image_path): size = QImageReader(image_path).size() args = (index, size.width(), size.height(), gdal2threejs.base64image(image_path)) else: f.write(u"project.images[%d] = {data:null};\n" % index) if exists: err_msg = u"Not image file path" else: err_msg = u"Image file not found" logMessage(u"{0}: {1}".format(err_msg, image_path)) continue elif imageType == self.MAP_IMAGE: width, height, extent, transp_background = image[1] args = (index, width, height, self.renderedImage(width, height, extent, transp_background)) elif imageType == self.LAYER_IMAGE: layerids, width, height, extent, transp_background = image[1] args = (index, width, height, self.renderedImage(width, height, extent, transp_background, layerids)) else: #imageType == self.CANVAS_IMAGE: transp_background = image[1] size = self.exportSettings.mapSettings.outputSize() args = (index, size.width(), size.height(), self.mapCanvasImage(transp_background)) f.write(u'project.images[%d] = {width:%d,height:%d,data:"%s"};\n' % args)
def _renderedImage2(self, width, height, extent, transp_background=False, layerids=None): """rendering function for GIS < 2.7""" antialias = True if self._renderer is None: self._initRenderer() canvas = self.exportSettings.canvas if canvas is None: logMessage( "With this QGIS version (<= 2.6), map canvas needs to be set to the export settings" ) return if layerids is None: layerids = [mapLayer.id() for mapLayer in canvas.layers()] renderer = self._renderer # QgsMapRenderer renderer.setLayerSet(layerids) image = QImage(width, height, QImage.Format_ARGB32_Premultiplied) if transp_background: image.fill(QColor(Qt.transparent).rgba()) # else: image.fill(canvas.canvasColor().rgba()) # renderer.setOutputSize(image.size(), image.logicalDpiX()) renderer.setExtent(extent.unrotatedRect()) painter = QPainter() painter.begin(image) if antialias: painter.setRenderHint(QPainter.Antialiasing) renderer.render(painter) painter.end() return tools.base64image(image)
def run(self): from vectorobject import ObjectTypeManager from pluginmanager import PluginManager from phylogeorecdialog import PhyloGeoRecDialog if self.objectTypeManager is None: self.objectTypeManager = ObjectTypeManager() if self.pluginManager is None: self.pluginManager = PluginManager() # restore export settings proj_path = QgsProject.instance().fileName() settingsFilePath = proj_path + ".qto3settings" if proj_path else None if not self.exportSettings or settingsFilePath != self.settingsFilePath: if settingsFilePath and os.path.exists(settingsFilePath): self.loadExportSettings(settingsFilePath) logMessage( u"Restored export settings of this project: {0}".format( os.path.basename( proj_path))) #QgsProject.instance().title() dialog = PhyloGeoRecDialog(self.iface, self.objectTypeManager, self.pluginManager, self.exportSettings, self.lastTreeItemData) # show dialog dialog.show() ret = dialog.exec_() self.exportSettings = dialog.settings(True) item = dialog.ui.treeWidget.currentItem() self.lastTreeItemData = item.data(0, Qt.UserRole) if item else None # if export succeeded, save export settings in the directory that project file exists if ret and settingsFilePath: self.saveExportSettings(settingsFilePath) self.settingsFilePath = settingsFilePath
def _readColor(self, widgetValues, f=None, isBorder=False): global colorNames mode = widgetValues["comboData"] if mode == OptionalColorWidgetFunc.NONE: return None if mode == ColorWidgetFunc.RGB: return widgetValues["editText"] if mode == ColorWidgetFunc.RANDOM or f is None: if len(colorNames) == 0: colorNames = QColor.colorNames() colorName = random.choice(colorNames) colorNames.remove(colorName) return QColor(colorName).name().replace("#", "0x") # feature color symbol = self.layer.rendererV2().symbolForFeature(f) if symbol is None: logMessage(u'Symbol for feature cannot be found: {0}'.format(self.layer.name())) symbol = self.layer.rendererV2().symbols()[0] else: sl = symbol.symbolLayer(0) if sl and isBorder: return sl.outlineColor().name().replace("#", "0x") if sl: # and sl.hasDataDefinedProperties(): # needs >= 2.2 expr = sl.dataDefinedProperty("color") if expr: # data defined color cs_rgb = expr.evaluate(f, f.fields()) # "rrr,ggg,bbb" (dec) to "0xRRGGBB" (hex) rgb = map(int, cs_rgb.split(",")[0:3]) return "0x" + "".join(map(chr, rgb)).encode("hex") return symbol.color().name().replace("#", "0x")
def setProperties(self, properties): for n, v in properties.items(): w = getattr(self, n, None) if w is None: continue if isinstance(w, QComboBox): if v is not None: index = w.findData(v) if index != -1: w.setCurrentIndex(index) elif isinstance( w, (QRadioButton, QCheckBox)): # subclass of QAbstractButton w.setChecked(v) elif isinstance(w, (QSlider, QSpinBox)): w.setValue(v) elif isinstance(w, QLineEdit): w.setText(v) elif isinstance(w, StyleWidget): if len(v): w.setValues(v) else: logMessage("[propertypages.py] Cannot restore %s property" % n)
def write(self, f): if len(self._list) == 0: return f.write(u'\n// 3D model data\n') for index, model in enumerate(self._list): model_type, path = model exists = os.path.exists(path) if exists and os.path.isfile(path): with open(path) as model_file: data = model_file.read().replace("\\", "\\\\").replace( "'", "\\'").replace("\t", "\\t").replace( "\r", "\\r").replace("\n", "\\n") f.write(u"project.models[%d] = {type:'%s',data:'%s'};\n" % (index, model_type, data)) else: f.write(u"project.models[%d] = {type:'%s',data:null};\n" % (index, model_type)) if exists: err_msg = u"Not 3D model file path" else: err_msg = u"3D model file not found" logMessage(u"{0}: {1} ({2})".format(err_msg, path, model_type))
def writeVectors(writer, legendInterface=None, progress=None): settings = writer.settings baseExtent = settings.baseExtent progress = progress or dummyProgress renderer = QgsMapRenderer() layers = [] if legendInterface is None: for parentId in [ ObjectTreeItem.ITEM_POINT, ObjectTreeItem.ITEM_LINE, ObjectTreeItem.ITEM_POLYGON ]: for layerId, properties in settings.get(parentId, {}).iteritems(): if properties.get("visible", False): layers.append([layerId, properties]) else: # use vector layer order in legendInterface for layer in legendInterface.layers(): if layer.type() != QgsMapLayer.VectorLayer: continue parentId = ObjectTreeItem.parentIdByLayer(layer) properties = settings.get(parentId, {}).get(layer.id(), {}) if properties.get("visible", False): layers.append([layer.id(), properties]) finishedLayers = 0 for layerId, properties in layers: mapLayer = QgsMapLayerRegistry.instance().mapLayer(layerId) if mapLayer is None: continue prop = VectorPropertyReader(writer.objectTypeManager, mapLayer, properties) obj_mod = writer.objectTypeManager.module(prop.mod_index) if obj_mod is None: logMessage("Module not found") continue # prepare triangle mesh geom_type = mapLayer.geometryType() if geom_type == QGis.Polygon and prop.type_index == 1 and prop.isHeightRelativeToDEM( ): # Overlay progress(None, "Initializing triangle mesh for overlay polygons") writer.triangleMesh() progress( 30 + 30 * finishedLayers / len(layers), u"Writing vector layer ({0} of {1}): {2}".format( finishedLayers + 1, len(layers), mapLayer.name())) # write layer object layer = VectorLayer(writer, mapLayer, prop, obj_mod) writer.writeLayer(layer.layerObject(), layer.fieldNames) # initialize symbol rendering mapLayer.rendererV2().startRender( renderer.rendererContext(), mapLayer.pendingFields() if QGis.QGIS_VERSION_INT >= 20300 else mapLayer) # features to export request = QgsFeatureRequest() clipGeom = None if properties.get("radioButton_IntersectingFeatures", False): request.setFilterRect( layer.transform.transformBoundingBox( baseExtent.boundingBox(), QgsCoordinateTransform.ReverseTransform)) if properties.get("checkBox_Clip"): extent = baseExtent.clone().scale( 0.999999 ) # clip with slightly smaller extent than map canvas extent clipGeom = extent.geometry() for feat in layer.features(request, clipGeom): # write geometry obj_mod.write(writer, layer, feat) # writer.writeFeature(layer, feat, obj_mod) # stack attributes in writer if layer.writeAttrs: writer.addAttributes(feat.attributes()) # write attributes if layer.writeAttrs: writer.writeAttributes() # write materials writer.writeMaterials(layer.materialManager) mapLayer.rendererV2().stopRender(renderer.rendererContext()) finishedLayers += 1
def features(self, request=None, clipGeom=None): settings = self.writer.settings mapTo3d = settings.mapTo3d() baseExtent = settings.baseExtent baseExtentGeom = baseExtent.geometry() rotation = baseExtent.rotation() prop = self.prop useZ = prop.useZ() if useZ: srs_from = osr.SpatialReference() srs_from.ImportFromProj4(str(self.layer.crs().toProj4())) srs_to = osr.SpatialReference() srs_to.ImportFromProj4(str(self.writer.settings.crs.toProj4())) ogr_transform = osr.CreateCoordinateTransformation( srs_from, srs_to) clipGeomWkb = clipGeom.asWkb() if clipGeom else None ogr_clipGeom = ogr.CreateGeometryFromWkb( clipGeomWkb) if clipGeomWkb else None else: # z_func: function to get elevation at given point (x, y) on surface if prop.isHeightRelativeToDEM(): if self.geomType == QGis.Polygon and prop.type_index == 1: # Overlay z_func = lambda x, y: 0 else: # get elevation from DEM z_func = lambda x, y: self.writer.demProvider.readValue( x, y) else: z_func = lambda x, y: 0 request = request or QgsFeatureRequest() for f in self.layer.getFeatures(request): geometry = f.geometry() if geometry is None: logMessage("null geometry skipped") continue # coordinate transformation - layer crs to project crs geom = QgsGeometry(geometry) if geom.transform(self.transform) != 0: logMessage("Failed to transform geometry") continue # check if geometry intersects with the base extent (rotated rect) if rotation and not baseExtentGeom.intersects(geom): continue # create feature feat = Feature(self.writer, self, f) # transform_func: function to transform the map coordinates to 3d coordinates relativeHeight = prop.relativeHeight(f) def transform_func(x, y, z): return mapTo3d.transform(x, y, z + relativeHeight) if useZ: ogr_geom = ogr.CreateGeometryFromWkb(geometry.asWkb()) # transform geometry from layer CRS to project CRS if ogr_geom.Transform(ogr_transform) != 0: logMessage("Failed to transform geometry") continue # clip geometry if ogr_clipGeom and self.geomType == QGis.Line: ogr_geom = ogr_geom.Intersection(ogr_clipGeom) if ogr_geom is None: continue # check if geometry is empty if ogr_geom.IsEmpty(): logMessage("empty geometry skipped") continue feat.geom = self.geomClass.fromOgrGeometry25D( ogr_geom, transform_func) else: # clip geometry if clipGeom and self.geomType in [QGis.Line, QGis.Polygon]: geom = geom.intersection(clipGeom) if geom is None: continue # check if geometry is empty if geom.isGeosEmpty(): logMessage("empty geometry skipped") continue if self.geomType == QGis.Polygon: feat.geom = self.geomClass.fromQgsGeometry( geom, z_func, transform_func, self.hasLabel()) if prop.type_index == 1 and prop.isHeightRelativeToDEM( ): # Overlay and relative to DEM feat.geom.splitPolygon(self.writer.triangleMesh()) else: feat.geom = self.geomClass.fromQgsGeometry( geom, z_func, transform_func) if feat.geom is None: continue yield feat
def log(self, message): logMessage(message)
def toFloat(cls, val): try: return float(val) except Exception as e: logMessage(u'{0} (value: {1})'.format(e.message, unicode(val))) return 0