class UmbraLayer(object): count=0 layer_count=0 def __init__(self,umbra,grid,name=None): """ Does not add the layers to the GUI - call register_layers for that. """ # having some trouble getting reliable output from the log... if 0: self.log=log else: class DumbLog(object): def info(self,*a): s=" ".join(a) with open(os.path.join(os.path.dirname(__file__),'log'),'a') as fp: fp.write(s+"\n") fp.flush() log=DumbLog() log.debug=log.info log.warning=log.info log.error=log.info self.log=log self.umbra=umbra self.grid=grid self.iface=None # gets set in register_layers UmbraLayer.layer_count+=1 if name is None: name="grid%d"%UmbraLayer.layer_count self.name=name self.layers=[] # SubLayer objects associated with this grid. self.umbra.register_grid(self) self.undo_stack=QUndoStack() def match_to_qlayer(self,ql): # need to either make the names unique, or find a better test. # since we can't really enforce the grouped layout, better to make the names # unique. for layer in self.layers: if layer.qlayer.name() == ql.name(): return True return False def grid_name(self): """ used to label the group """ UmbraLayer.count+=1 return "grid%4d"%UmbraLayer.count @classmethod def open_layer(klass,umbra,grid_format,path): g=klass.load_grid(path=path,grid_format=grid_format) return klass(umbra=umbra,grid=g) @classmethod def load_grid(klass,grid_format=None,path=None): if path is None: # for development, load sample data: suntans_path=os.path.join( os.path.dirname(__file__), "sample_data/sfbay" ) grid=unstructured_grid.SuntansGrid(suntans_path) else: if grid_format=='SUNTANS': grid=unstructured_grid.SuntansGrid(path) elif grid_format=='pickle': grid=unstructured_grid.UnstructuredGrid.from_pickle(path) elif grid_format=='DFM': grid=dfm_grid.DFMGrid(fn=path) else: raise Exception("Need to add other grid types, like %s!"%grid_format) return grid def on_layer_deleted(self,sublayer): self.layers.remove(sublayer) self.log.info("signal received: node layer deleted") sublayer.unextend_grid() # these are the steps which lead to this callback. don't do them # again. # reg=QgsMapLayerRegistry.instance() # reg.removeMapLayers([sublayer.qlayer]) # create the memory layers and populate accordingly def register_layer(self,sublayer): self.layers.append( sublayer ) def callback(sublayer=sublayer): self.on_layer_deleted(sublayer) sublayer.qlayer.layerDeleted.connect(callback) QgsMapLayerRegistry.instance().addMapLayer(sublayer.qlayer) li=self.iface.legendInterface() li.moveLayer(sublayer.qlayer,self.group_index) def layer_by_tag(self,tag): for layer in self.layers: if layer.tag == tag: return layer return None def create_group(self): # Create a group for the layers - li=self.iface.legendInterface() grp_name=self.grid_name() self.group_index=li.addGroup(grp_name) def register_layers(self): crs="?crs=epsg:26910" # was 4326 self.iface=self.umbra.iface self.create_group() self.register_layer( UmbraCellLayer(self.log, self.grid, crs=crs, prefix=self.name, tag='cells') ) self.register_layer( UmbraEdgeLayer(self.log, self.grid, crs=crs, prefix=self.name, tag='edges' ) ) self.register_layer( UmbraNodeLayer(self.log, self.grid, crs=crs, prefix=self.name, tag='nodes') ) # set extent to the extent of our layer # skip while developing # canvas.setExtent(layer.extent()) def remove_all_qlayers(self): layers=[] for sublayer in self.layers: layers.append( sublayer.qlayer.name() ) reg=QgsMapLayerRegistry.instance() self.log.info("Found %d layers to remove"%len(layers)) reg.removeMapLayers(layers) def distance_to_node(self,pnt,i): """ compute distance from the given point to the given node, returned in physical distance units [meters]""" if not self.grid: return 1e6 return mag( self.grid.nodes['x'][i] - np.asarray(pnt) ) def distance_to_cell(self,pnt,c): if not self.grid: return 1e6 return mag( self.grid.cells_center()[c] - np.asarray(pnt) ) def find_closest_node(self,xy): # xy: [x,y] print "Finding closest node to ",xy return self.grid.select_nodes_nearest(xy) def find_closest_cell(self,xy): # xy: [x,y] return self.grid.select_cells_nearest(xy) def extent(self): xmin,xmax,ymin,ymax = self.grid.bounds() print "extent() called on UmbraLayer" return QgsRectangle(xmin,ymin,xmax,ymax) def renumber(self): self.grid.renumber() # thin wrapper to grid editing calls # channel them through here to (a) keep consistent interface # and (b) track undo stacks at the umbra layer level. def undo(self): if self.undo_stack.canUndo(): self.undo_stack.undo() else: self.log.warning("request for undo, but stack cannot") def redo(self): if self.undo_stack.canRedo(): self.undo_stack.redo() else: self.log.warning("request for undo, but stack cannot") def modify_node(self,n,**kw): cmd=GridCommand(self.grid, "Modify node", lambda: self.grid.modify_node(n,**kw)) self.undo_stack.push(cmd) def toggle_cell_at_point(self,xy): def do_toggle(): self.log.info("umbra_layer: toggle cell at %s"%xy) self.grid.toggle_cell_at_point(xy) cmd=GridCommand(self.grid, "Toggle cell", lambda: self.grid.toggle_cell_at_point(xy)) self.undo_stack.push(cmd) def delete_node(self,n): cmd=GridCommand(self.grid, "Delete node", lambda: self.grid.delete_node_cascade(n)) self.undo_stack.push(cmd) def delete_edge(self,e): cmd=GridCommand(self.grid, "Delete edge", lambda: self.grid.delete_edge_cascade(e)) self.undo_stack.push(cmd) def add_edge(self,nodes): if self.grid.nodes_to_edge(*nodes) is not None: self.log.info("Edge already existed, probably") return self.add_edge_last_id=None def redo(): j=self.grid.add_edge(nodes=nodes) self.add_edge_last_id=j cmd=GridCommand(self.grid,"Add edge",redo) self.undo_stack.push(cmd) assert self.add_edge_last_id is not None self.log.info("Adding an edge! j=%d"%self.add_edge_last_id) return self.add_edge_last_id def add_node(self,x): # awkward jumping through hoops to both use the undo stack # and get the id of a node which was just added self.add_node_last_id=None def redo(): n=self.grid.add_node(x=x) self.add_node_last_id=n cmd=GridCommand(self.grid,"Add node",redo) self.undo_stack.push(cmd) assert self.add_node_last_id is not None return self.add_node_last_id def delete_selected(self): cell_layer=self.layer_by_tag('cells') if cell_layer is not None: selected_cells = cell_layer.selection() self.log.info("Found %d selected cells"%len(selected_cells)) def redo(cells=selected_cells): # pass this way b/c of python bindings weirdness for c in cells: self.grid.delete_cell(c) cmd=GridCommand(self.grid,"Delete cells",redo) self.undo_stack.push(cmd)