def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # map of layer object => LayerInfo self.layers = {} self.vpipe = ViewerPipe() self.signalsConnected = False
class Canvas3D: def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # map of layer object => LayerInfo self.layers = {} self.vpipe = ViewerPipe() self.signalsConnected = False def initGui(self): # Create action that will start plugin configuration self.action = QAction( QIcon(":/plugins/canvas3d/icon.png"), u"3D canvas", self.iface.mainWindow()) # connect the action to the run method self.action.triggered.connect(self.run) # Add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu( WIN_TITLE, self.action) def unload(self): # Remove the plugin menu item and icon self.iface.removePluginMenu( WIN_TITLE, self.action) self.iface.removeToolBarIcon(self.action) def sendToViewer( self, cmd, args ): if self.vpipe: r = self.vpipe.evaluate( cmd, args ) if r[0] == 'broken_pipe': # the viewer is not here anymore, ignoring return if r[0] != 'ok': QMessageBox.warning( None, "Communication error", r[1]['msg'] ) # called when layer's properties has been changed through UI def onPropertiesChanged( self, layer ): style = {} if layer.type() == 0: # vector renderer = layer.rendererV2() # first symbol sym = renderer.symbols()[0] # first symbol layer symL = sym.symbolLayer( 0 ) if symL.type() == 0: # point style['fill_color_diffuse'] = symL.color().name() elif symL.type() == 2: # polygon style['fill_color_diffuse'] = symL.color().name() elif layer.type() == 1: # raster # get 'colorize' option f = layer.hueSaturationFilter() if f.colorizeOn(): style['fill_color_diffuse'] = f.colorizeColor().name() # send symbology if len(style) > 0: style['id'] = layer.id() self.sendToViewer( 'setSymbology', style ) def addLayer( self, layer ): providerName = layer.dataProvider().name() # get position in layer set (z-index) visibleLayers = self.iface.mapCanvas().mapRenderer().layerSet() z = 0 for l in visibleLayers: if l == layer.id(): break if layer.type() == 0: # vector z = z + Z_VECTOR_FIGHT_GAP elif layer.type() == 1: # raster z = z + Z_DEM_FIGHT_GAP elevationFile = None if DEM_VIEWER_DRAPING: # get raster layer, if any registry = QgsMapLayerRegistry.instance() for name, l in registry.mapLayers().iteritems(): if l.type() == 1: provider = l.dataProvider() if provider.name() == 'gdal' and provider.bandCount() == 1 and provider.dataType( 1 ) != QGis.Byte: elevationFile = l.source() break ret = None # get layer' style if layer.type() == 0: # vector if providerName == 'postgres': # test 2D/3D geometries: is3D = False provider = layer.dataProvider() sys.stderr.write("get features...\n") it = provider.getFeatures() sys.stderr.write("end of get features...\n") feature = it.next() if feature: t = feature.geometry().wkbType() if feature.geometry().wkbType() < 0 : # is3D = True it.rewind() it.close() # parse connection string connection = {} geocolumn = 'geom' args = {} query = '' table = '' # connection info followed by table and queries # connection info : k='v' (optional ' for integers) # queries : table="...." (geocolumn_name) sql=... until end of line (connection_str, queries_str) = layer.source().split("table=") s = connection_str.split(' ') for si in s: ss = si.split('=') if len(ss) > 1: connection[ ss[0] ] = ss[1].strip("'\"") (table, q) = queries_str.split('" ') # if the table is a query (from DB manager): if table[0:2] == '"(': table.strip('"') else: table=table+'"' (geocolumn,query) = q.split('sql=') geocolumn = geocolumn.strip('() ') # use geocolumn only for polygons if layer.geometryType() == 2: args['geocolumn'] = geocolumn args['id'] = layer.id() args['conn_info'] = ' '.join( ["%s='%s'" % (k,v) for k,v in connection.iteritems() if k in ['dbname','user','port','host']] ) extent = layer.extent() center = self.fullExtent.center() args['extent'] = "%f %f,%f %f" % ( extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() ) args['origin'] = "%f %f %f" % (center.x(), center.y(), z) if table[0:2] == '"(': # table == query (DB manager) query = table.strip('"()') else: query = "SELECT * FROM %s /**WHERE TILE && %s*/" % (table, geocolumn) if layer.hasScaleBasedVisibility(): lmin = layer.minimumScale() lmax = layer.maximumScale() else: # by default, we enable on demand loading lmin = 1 lmax = 10000000 # conversion from 1:N scale to distance from ground # cnv = self.iface.mapCanvas() # FIXME: we use the default FOV here fov = 29.1 * math.pi / 180.0 altMax = 0.5 * (cnv.extent().height() / cnv.scale() * lmax ) / math.tan(fov/2.0) altMin = 0.5 * (cnv.extent().height() / cnv.scale() * lmin ) / math.tan(fov/2.0) args['lod'] = "%f %f" % (altMax, altMin) args['query_0'] = query args['tile_size'] = TILE_SIZE if elevationFile and not is3D and layer.geometryType() == 2: args['elevation'] = elevationFile self.sendToViewer( 'loadVectorPostgis', args ) self.layers[ layer ] = LayerInfo( layer.id(), False ) # # raster layers # elif layer.type() == 1: provider = layer.dataProvider() if provider.name() == 'gdal': # warning: band # start at 1 extent = self.fullExtent extent = "%f %f,%f %f" % ( extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() ) center = self.fullExtent.center() origin = "%f %f %f" % ( center.x(), center.y(), z) fileSrc = layer.source() # look for a .ive file and use it if present fileName, fileExt = os.path.splitext( fileSrc ) iveFile = fileName + ".ive" if os.path.exists( iveFile ): fileSrc = iveFile if provider.bandCount() == 1 and provider.dataType( 1 ) != QGis.Byte: if layer.hasScaleBasedVisibility(): lmin = layer.minimumScale() lmax = layer.maximumScale() else: # by default, we enable on demand loading lmin = 1 lmax = 10000000 cnv = self.iface.mapCanvas() # FIXME: we use the default FOV here fov = 29.1 * math.pi / 180.0 altMax = 0.5 * (cnv.extent().height() / cnv.scale() * lmax ) / math.tan(fov/2.0) altMin = 0.5 * (cnv.extent().height() / cnv.scale() * lmin ) / math.tan(fov/2.0) altMax = 1000 # Nicolasribot: # test without lod, no tile self.sendToViewer( 'loadElevation', { 'id': layer.id(), 'file': fileSrc, 'extent' : extent, 'origin' : origin, 'mesh_size' : 10 # ??? # 'mesh_size_0' : 10, # ??? # 'lod' : "%f %f" % (altMax, altMin), # 'tile_size' : TILE_SIZE } ) else: pass # not supported yet self.sendToViewer( 'loadImageGDAL', { 'id': layer.id(), 'url':layer.source()} ) self.layers[ layer ] = LayerInfo( layer.id(), False ) # update symbology self.onPropertiesChanged( layer ) # connect to rendererChanged QObject.connect( layer, SIGNAL( "rendererChanged()" ), lambda l=layer: self.onPropertiesChanged(l) ) def removeLayer( self, layer ): if self.layers.has_key( layer ): layerId = self.layers[ layer ].id self.sendToViewer( 'unloadLayer', { 'id': layerId } ) # disconnect QObject.disconnect( layer, SIGNAL( "rendererChanged()" ), lambda l=layer: self.onPropertiesChanged(l) ) del self.layers[ layer ] def setExtent( self, epsg, xmin, ymin, xmax, ymax ): center = self.fullExtent.center() self.sendToViewer( 'addPlane', { 'id' : 'p0', 'extent' : "%f %f,%f %f" % (xmin, ymin, xmax, ymax), 'origin' : "%f %f 1" % (center.x(), center.y()) } ) self.sendToViewer( 'setSymbology', { 'id' : 'p0', 'fill_color_diffuse': '#ffffffff' } ) def setLayerVisibility( self, layer, visibility ): if not self.layers.has_key( layer ): return layerId = self.layers[ layer ].id if visibility: self.sendToViewer( 'showLayer', {'id' : layerId } ) else: self.sendToViewer( 'hideLayer', {'id' : layerId } ) self.layers[ layer ].visible = visibility # qgis signal : layer added def onLayerAdded( self, layer ): self.addLayer( layer ) self.setLayerVisibility( layer, True ) # qgis signal : layer removed def onLayerRemoved( self, layerId ): registry = QgsMapLayerRegistry.instance() layer = registry.mapLayer( layerId ) if layer: self.removeLayer( layer ) # qgis signal : layers changed def onLayersChanged( self ): renderer = self.iface.mapCanvas().mapRenderer() # returns visible layers layers = renderer.layerSet() for layer, p in self.layers.iteritems(): if layer.id() in layers and not p.visible: self.setLayerVisibility( layer, True ) if layer.id() not in layers and p.visible: self.setLayerVisibility( layer, False ) # qgis signal : extents changed def updateCamera( self ): vExtent = self.iface.mapCanvas().extent() center = self.fullExtent.center() self.sendToViewer( 'lookAt', { 'origin' : "%f %f 0" % (center.x(), center.y()), 'extent' : "%f %f,%f %f" % (vExtent.xMinimum(), vExtent.yMinimum(), vExtent.xMaximum(), vExtent.yMaximum() ) } ) # run method that performs all the real work def run(self): registry = QgsMapLayerRegistry.instance() if not self.signalsConnected: # when a layer is added QObject.connect( registry, SIGNAL( "layerWasAdded( QgsMapLayer * )" ), self.onLayerAdded ) # when a layer is removed QObject.connect( registry, SIGNAL( "layerWillBeRemoved( QString )" ), self.onLayerRemoved ) # when a layer' state changes (in particular: its visibility) QObject.connect( self.iface.mapCanvas(), SIGNAL( "layersChanged()" ), self.onLayersChanged ) # when the extent has changed QObject.connect( self.iface.mapCanvas(), SIGNAL( "extentsChanged()" ), self.updateCamera ) self.signalsConnected = True # get the current global extent renderer = self.iface.mapCanvas().mapRenderer() # store it (will be used as origin) self.fullExtent = renderer.fullExtent() extent = self.fullExtent epsg = renderer.destinationCrs().authid() if extent.isEmpty(): QMessageBox.information( None, WIN_TITLE, "No layer loaded, no extent defined, aborting" ) return try: self.vpipe.start( SIMPLEVIEWER_BIN ) except OSError as e: QMessageBox.warning( None, WIN_TITLE, "Problem starting %s: %s" % (SIMPLEVIEWER_BIN, e.strerror ) ) return self.setExtent( epsg, extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() ) # set camera self.updateCamera() # load every visible layer layers = registry.mapLayers() # returns visible layers visibleLayers = self.iface.mapCanvas().mapRenderer().layerSet() for lid, l in layers.iteritems(): self.addLayer( l ) if l.id() in visibleLayers: self.setLayerVisibility( l, True ) else: self.setLayerVisibility( l, False )