Example #1
0
    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None
    ):
        """Add a toolbar icon to the toolbar."""
        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action
class Send2GE:
  def __init__(self, iface):
    """Initialize class"""
    # save reference to QGIS interface
    self.iface = iface
    self.plugin_dir = get_file_dir(__file__)

  def initGui(self):
    """Initialize graphic user interface"""
    #create action that will be run by the plugin
    self.action = QAction(
        QIcon("%s/icons/cursor2.png" % self.plugin_dir),
        "Send to Google Earth",
        self.iface.mainWindow()
    )
    self.action.setWhatsThis("Send to Google Earth")
    self.action.setStatusTip("Send coordinates of a mouse click to Google Earth")
    
    # add plugin menu to Vector toolbar
    self.iface.addPluginToMenu("Send2GoogleEarth",self.action)
    
    # add icon to new menu item in Vector toolbar
    self.iface.addToolBarIcon(self.action)

    # connect action to the run method
    self.action.triggered.connect(self.run)

    # prepare map tool
    self.mapTool = Send2GEtool(self.iface)
    #self.iface.mapCanvas().mapToolSet.connect(self.mapToolChanged)
      
  def unload(self):
    """Actions to run when the plugin is unloaded"""
    # remove menu and icon from the menu
    self.iface.removeToolBarIcon(self.action)
    self.iface.removePluginMenu("Send2GoogleEarth",self.action)

    if self.iface.mapCanvas().mapTool() == self.mapTool:
      self.iface.mapCanvas().unsetMapTool(self.mapTool)

    del self.mapTool

  def run(self):
    """Action to run"""
    # create a string and show it

    self.iface.mapCanvas().setMapTool(self.mapTool)
Example #3
0
    def add_action(
            self,
            icon_path,
            text,
            callback,
            enabled_flag=True,
            checkable=False,
            add_to_menu=True,
            add_to_toolbar=True,
            status_tip=None,
            whats_this=None,
            menu=None,
            parent=None):
        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)
        action.setCheckable(checkable)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if menu is not None:
            action.setMenu(menu)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToVectorMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action
Example #4
0
class XYZHubConnector(object):

    """base plugin"""

    def __init__(self, iface):
        """init"""
        import sys
        print(sys.version)
        self.iface = iface
        self.web_menu = "&{name}".format(name=config.PLUGIN_FULL_NAME)
        self.hasGuiInitialized = False
        self.init_modules()
        self.obj = self

    def initGui(self):
        """startup"""

        parent = self.iface.mainWindow()

        ######## action, button

        icon = QIcon("%s/%s" % (config.PLUGIN_DIR,"images/xyz.png"))
        icon_bbox = QIcon("%s/%s" % (config.PLUGIN_DIR,"images/bbox.svg"))
        self.action_connect = QAction(icon, "XYZ Hub Connection", parent)
        self.action_connect.setWhatsThis(
            QCoreApplication.translate(PLUGIN_NAME, "WhatsThis message" ))
        self.action_connect.setStatusTip(
            QCoreApplication.translate(PLUGIN_NAME, "status tip message" ))

        self.action_sync_edit = QAction( QIcon("%s/%s" % (config.PLUGIN_DIR,"images/sync.svg")), "Push changes to XYZ Hub", parent)

        self.action_clear_cache = QAction( QIcon("%s/%s" % (config.PLUGIN_DIR,"images/delete.svg")), "Clear cache", parent)

        # self.action_sync_edit.setVisible(False) # disable magic sync
        self.edit_buffer.config_ui(self.enable_sync_btn)
        
        self.cb_layer_selected(self.iface.activeLayer())

        ######## CONNECT action, button

        self.action_connect.triggered.connect(self.open_connection_dialog)
        self.action_sync_edit.triggered.connect(self.open_sync_edit_dialog)
        self.action_clear_cache.triggered.connect( self.open_clear_cache_dialog)

        ######## Add the toolbar + button
        self.toolbar = self.iface.addToolBar(PLUGIN_NAME)
        self.toolbar.setObjectName(config.PLUGIN_FULL_NAME)

        self.actions_menu = [self.action_connect, self.action_sync_edit, self.action_clear_cache] 

        for a in [self.action_connect, self.action_sync_edit]:
            self.toolbar.addAction(a)

        for a in self.actions_menu:
            self.iface.addPluginToWebMenu(self.web_menu, a)

        # # uncomment to use menu button
        # tool_btn = QToolButton(self.toolbar)
        # tool_btn.setDefaultAction(self.action_connect)
        # tool_btn.setPopupMode(tool_btn.MenuButtonPopup)
        # self.xyz_widget_action = self.toolbar.addWidget(tool_btn) # uncomment to use menu button

        # self.toolbar.addAction(self.action_connect)

        self.action_help = None

        progress = QProgressBar()
        progress.setMinimum(0)
        progress.setMaximum(0)
        progress.reset()
        progress.hide()
        # progress = self.iface.statusBarIface().children()[2] # will be hidden by qgis
        self.iface.statusBarIface().addPermanentWidget(progress)
        self.pb = progress

        # btn_toggle_edit = self.get_btn_toggle_edit()
        # btn_toggle_edit.toggled.connect(lambda *a: print("toggled", a))

        self.hasGuiInitialized = True

    # unused
    def get_btn_toggle_edit(self):
        text_toggle_edit = "toggle editing"
        toolbar = self.iface.digitizeToolBar()
        mapping = dict(
            (w.text().lower() if hasattr(w,"text") else str(i), w)
            for i, w in enumerate(toolbar.children())
        )
        return mapping[text_toggle_edit]

    def new_session(self):
        self.con_man.reset()
        self.edit_buffer.reset()
        self.pending_delete_qnodes.clear()
        
        if self.hasGuiInitialized:
            self.pb.hide()

    def init_modules(self):
        if LOG_TO_FILE:
            QgsApplication.messageLog().messageReceived.connect(cb_log_qgis) #, Qt.QueuedConnection
        connect_global_error_signal(self.log_err_traceback)

        # util.init_module()

        # parent = self.iface.mainWindow()
        parent = QgsProject.instance()

        self.secret = Secret(config.USER_PLUGIN_DIR +"/secret.ini")
        ######## Init xyz modules
        self.map_basemap_meta = basemap.load_default_xml()
        self.auth_manager = AuthManager(config.USER_PLUGIN_DIR +"/auth.ini")
        
        self.network = NetManager(parent)
        
        self.con_man = LoaderManager()
        self.con_man.config(self.network)
        self.edit_buffer = EditBuffer()
        ######## data flow
        # self.conn_info = SpaceConnectionInfo()
        
        ######## token      
        self.token_config = ServerTokenConfig(config.USER_PLUGIN_DIR +"/token.ini", parent)
        self.token_config.set_default_servers(net_utils.API_URL)
        self.token_model = self.token_config.get_token_model()
        self.server_model = self.token_config.get_server_model()

        ######## CALLBACK
        
        self.con_man.ld_pool.signal.progress.connect( self.cb_progress_busy) #, Qt.QueuedConnection
        self.con_man.ld_pool.signal.finished.connect( self.cb_progress_done)
        
        QgsProject.instance().cleared.connect(self.new_session)
        QgsProject.instance().layersWillBeRemoved["QStringList"].connect( self.edit_buffer.remove_layers)
        # QgsProject.instance().layersWillBeRemoved["QStringList"].connect( self.layer_man.remove_layers)

        # QgsProject.instance().layersAdded.connect( self.edit_buffer.config_connection)

        self.iface.currentLayerChanged.connect( self.cb_layer_selected) # UNCOMMENT

        canvas = self.iface.mapCanvas()
        self.lastRect = bbox_utils.extent_to_rect(bbox_utils.get_bounding_box(canvas))
        self.iface.mapCanvas().extentsChanged.connect( self.reload_tile, Qt.QueuedConnection)

        # handle move, delete xyz layer group
        self.pending_delete_qnodes = dict()
        QgsProject.instance().layerTreeRoot().willRemoveChildren.connect(self.cb_qnodes_deleting)
        QgsProject.instance().layerTreeRoot().removedChildren.connect(self.cb_qnodes_deleted)
        QgsProject.instance().layerTreeRoot().visibilityChanged.connect(self.cb_qnode_visibility_changed)
        

        QgsProject.instance().readProject.connect( self.import_project)
        self.import_project()

    def unload_modules(self):
        # self.con_man.disconnect_ux( self.iface)
        QgsProject.instance().cleared.disconnect(self.new_session)
        QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect( self.edit_buffer.remove_layers)
        # QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect( self.layer_man.remove_layers)

        # QgsProject.instance().layersAdded.disconnect( self.edit_buffer.config_connection)
        self.edit_buffer.unload_connection()

        self.con_man.unload()

        self.iface.currentLayerChanged.disconnect( self.cb_layer_selected) # UNCOMMENT

        self.iface.mapCanvas().extentsChanged.disconnect( self.reload_tile)
        
        QgsProject.instance().layerTreeRoot().willRemoveChildren.disconnect(self.cb_qnodes_deleting)
        QgsProject.instance().layerTreeRoot().removedChildren.disconnect(self.cb_qnodes_deleted)
        QgsProject.instance().layerTreeRoot().visibilityChanged.disconnect(self.cb_qnode_visibility_changed)

        QgsProject.instance().readProject.disconnect( self.import_project)
        
        # utils.disconnect_silent(self.iface.currentLayerChanged)

        self.secret.deactivate()
        
        disconnect_global_error_signal()
        if LOG_TO_FILE:
            QgsApplication.messageLog().messageReceived.disconnect(cb_log_qgis)
        # close_file_logger()
        pass
    def unload(self):
        """teardown"""
        self.unload_modules()
        # remove the plugin menu item and icon
        self.iface.removePluginWebMenu(self.web_menu, self.action_help)

        self.toolbar.clear() # remove action from custom toolbar (toolbar still exist)
        self.toolbar.deleteLater()

        for a in self.actions_menu:
            self.iface.removePluginWebMenu(self.web_menu, a)
        # remove progress
        self.iface.statusBarIface().removeWidget(self.pb)

    ############### 
    # Callback
    ###############
    def cb_layer_selected(self, qlayer):
        flag_xyz = True if qlayer is not None and is_xyz_supported_layer(qlayer) else False
        if flag_xyz: 
            self.edit_buffer.config_connection([qlayer])
            self.edit_buffer.enable_ui(qlayer.id())
        else:
            msg = "No XYZHub Layer selected"
            self.enable_sync_btn(False, msg)
        # disable magic sync
        # self.action_sync_edit.setEnabled(flag_xyz)
    def enable_sync_btn(self, flag, msg=""):
        msg = msg or ("No changes detected since last push" if not flag else "Push changes")
        self.action_sync_edit.setToolTip(msg)
        self.action_sync_edit.setEnabled(flag)
        
    ############### 
    # Callback of action (main function)
    ###############
    
    def show_info_msgbar(self, title, msg="", dt=3):
        self.iface.messageBar().pushMessage(
            config.TAG_PLUGIN, ": ".join([title,msg]),
            Qgis.Info, dt
        )

    def show_warning_msgbar(self, title, msg="", dt=3):
        self.iface.messageBar().pushMessage(
            config.TAG_PLUGIN, ": ".join([title,msg]),
            Qgis.Warning, dt
        )

    def show_success_msgbar(self, title, msg="", dt=3):
        self.iface.messageBar().pushMessage(
            config.TAG_PLUGIN, ": ".join([title,msg]),
            Qgis.Success, dt
        )

    def make_cb_success(self, title, msg="", dt=3):
        def _cb_success_msg():
            self.show_success_msgbar(title, msg, dt=dt)
        return _cb_success_msg
        
    def make_cb_success_args(self, title, msg="", dt=3):
        def _cb_success_msg(args):
            a, kw = parse_qt_args(args)
            txt = ". ".join(map(str,a))
            self.show_success_msgbar(title, txt, dt=dt)
        return _cb_success_msg

    def make_cb_info_args(self, title, msg="", dt=3):
        def _cb_info_msg(args):
            a, kw = parse_qt_args(args)
            txt = ". ".join(map(str,a))
            self.show_info_msgbar(title, txt, dt=dt)
        return _cb_info_msg

    def cb_handle_error_msg(self, e):
        err = parse_exception_obj(e)
        if isinstance(err, ChainInterrupt):
            e0, idx = err.args[0:2]
        else:
            e0 = err
        if isinstance(e0, (net_handler.NetworkError, net_handler.NetworkTimeout)):
            ok = self.show_net_err(e0)
            if ok: return
        elif isinstance(e0, EmptyXYZSpaceError):
            ret = exec_warning_dialog("XYZ Hub","Requested query returns no features")
            return
        elif isinstance(e0, ManualInterrupt):
            self.log_err_traceback(e0)
            return
        self.show_err_msgbar(err)

    def show_net_err(self, err):
        reply_tag, status, reason, body, err_str, url = err.args[:6]
        if reply_tag in ["count", "statistics"]: # too many error
            # msg = "Network Error: %s: %s. %s"%(status, reason, err_str)
            return 1
            
        detail = "\n". join(["Request:", url,"","Response:", body])
        msg = (
            "%s: %s\n"%(status,reason) + 
            "There was a problem connecting to the server"
        )
        if status in [401,403]:
            msg += ("\n\n" + "Please input valid token with correct permissions." + "\n" +
            "Token is generated via " +
            "<a href='https://xyz.api.here.com/token-ui/'>https://xyz.api.here.com/token-ui/</a>")
        ret = exec_warning_dialog("Network Error",msg, detail)
        return 1

    def show_err_msgbar(self, err):
        self.iface.messageBar().pushMessage(
            config.TAG_PLUGIN, repr(err),
            Qgis.Warning, 3
        )
        self.log_err_traceback(err)

    def log_err_traceback(self, err):
        msg = format_traceback(err)
        QgsMessageLog.logMessage( msg, config.TAG_PLUGIN, Qgis.Warning)

    def cb_progress_busy(self, n_active):
        if n_active > 1: return
        self.flag_pb_show=True
        self.cb_progress_refresh()

    def cb_progress_done(self):
        self.flag_pb_show=False
        self.cb_progress_refresh()

    def cb_progress_refresh(self):
        if not hasattr(self,"flag_pb_show"): return

        pb = self.pb
        if self.flag_pb_show:
            pb.show()
            # print_qgis("show",pb)
        else:
            pb.hide()        
            # print_qgis("hide")
            
    ############### 
    # Action (main function)
    ###############

    # UNUSED
    def refresh_canvas(self):
        # self.iface.activeLayer().triggerRepaint()
        self.iface.mapCanvas().refresh()
    def previous_canvas_extent(self):
        self.iface.mapCanvas().zoomToPreviousExtent()
    #
    
    def new_main_dialog(self):
        parent = self.iface.mainWindow()
        dialog = MainDialog(parent)

        dialog.config(self.token_model, self.server_model)
        # dialog.config_secret(self.secret)
        auth = self.auth_manager.get_auth()
        dialog.config_basemap(self.map_basemap_meta, auth)

        con = self.con_man.make_con("create")
        con.signal.finished.connect( dialog.btn_use.clicked.emit ) # can be optimized !!
        con.signal.error.connect( self.cb_handle_error_msg )

        con = self.con_man.make_con("list")
        con.signal.results.connect( make_fun_args(dialog.cb_display_spaces) )
        con.signal.error.connect( self.cb_handle_error_msg )
        con.signal.error.connect( lambda e: dialog.cb_enable_token_ui() )
        con.signal.finished.connect( dialog.cb_enable_token_ui )
        con.signal.finished.connect( dialog.ui_valid_token )

        con = self.con_man.make_con("edit")
        con.signal.finished.connect( dialog.btn_use.clicked.emit )
        con.signal.error.connect( self.cb_handle_error_msg )

        con = self.con_man.make_con("delete")
        con.signal.results.connect( dialog.btn_use.clicked.emit )
        con.signal.error.connect( self.cb_handle_error_msg )

        con = self.con_man.make_con("stat")
        con.signal.results.connect( make_fun_args(dialog.cb_display_space_count) )
        con.signal.error.connect( self.cb_handle_error_msg )

        ############ clear cache btn
        dialog.signal_clear_cache.connect( self.open_clear_cache_dialog)
        
        ############ add map tile btn
        dialog.signal_add_basemap.connect( self.add_basemap_layer)
        
        ############ btn: new, edit, delete space   
        dialog.signal_new_space.connect(self.start_new_space)
        dialog.signal_edit_space.connect(self.start_edit_space)
        dialog.signal_del_space.connect(self.start_delete_space)

        ############ Use Token btn        
        dialog.signal_use_token.connect( lambda a: self.con_man.finish_fast())
        dialog.signal_use_token.connect(self.start_use_token)

        ############ get count        
        dialog.signal_space_count.connect(self.start_count_feat, Qt.QueuedConnection) # queued -> non-blocking ui

        ############ connect btn        
        # dialog.signal_space_connect.connect(self.start_load_layer)
        dialog.signal_space_connect.connect(self.start_loading)
        dialog.signal_space_tile.connect(self.start_load_tile)

        ############ upload btn        
        dialog.signal_upload_space.connect(self.start_upload_space)
        
        return dialog
    def start_new_space(self, args):
        con = self.con_man.get_con("create")
        con.start_args(args)

    def start_edit_space(self, args):
        con = self.con_man.get_con("edit")
        con.start_args(args)

    def start_delete_space(self, args):
        con = self.con_man.get_con("delete")
        con.start_args(args)

    def start_use_token(self, args):
        con = self.con_man.get_con("list")
        con.start_args(args)

    def start_count_feat(self, args):
        con = self.con_man.get_con("stat")
        con.start_args(args)

    def start_upload_space(self, args):
        con_upload = UploadLayerController(self.network, n_parallel=2)
        self.con_man.add_on_demand_controller(con_upload)
        # con_upload.signal.finished.connect( self.make_cb_success("Uploading finish") )
        con_upload.signal.results.connect( self.make_cb_success_args("Uploading finish", dt=4))
        con_upload.signal.error.connect( self.cb_handle_error_msg )
        
        con = InitUploadLayerController(self.network)
        self.con_man.add_on_demand_controller(con)

        con.signal.results.connect( con_upload.start_args)
        con.signal.error.connect( self.cb_handle_error_msg )

        con.start_args(args)
    def start_load_layer(self, args):
        # create new con
        # config
        # run
        
        ############ connect btn        
        con_load = LoadLayerController(self.network, n_parallel=1)
        self.con_man.add_on_demand_controller(con_load)
        # con_load.signal.finished.connect( self.make_cb_success("Loading finish") )
        con_load.signal.results.connect( self.make_cb_success_args("Loading finish") )
        # con_load.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
        con_load.signal.error.connect( self.cb_handle_error_msg )

        con_load.start_args(args)

        # con.signal.results.connect( self.layer_man.add_args) # IMPORTANT
    def start_load_tile(self, args):
        # rect = (-180,-90,180,90)
        # level = 0
        a, kw = parse_qt_args(args)
        kw.update(self.make_tile_params())
        # kw["limit"] = 100

        ############ connect btn        
        con_load = TileLayerLoader(self.network, n_parallel=1)
        self.con_man.add_persistent_loader(con_load)
        # con_load.signal.finished.connect( self.make_cb_success("Tiles loaded") )
        con_load.signal.results.connect( self.make_cb_success_args("Tiles loaded", dt=2) )
        # con_load.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
        con_load.signal.error.connect( self.cb_handle_error_msg )

        con_load.start_args( make_qt_args(*a, **kw))

    def make_tile_params(self, rect=None, level=None):
        if not rect:
            canvas = self.iface.mapCanvas()
            rect = bbox_utils.extent_to_rect(
                bbox_utils.get_bounding_box(canvas))
            level = tile_utils.get_zoom_for_current_map_scale(canvas)
        schema = "web"
        kw = dict()
        kw["tile_schema"] = schema
        kw["tile_ids"] = tile_utils.bboxToListColRow(*rect,level, schema=schema)
        return kw

    def start_loading(self, args):
        a, kw = parse_qt_args(args)
        loading_mode = kw.get("loading_mode")
        try:
            con_load = self.make_loader_from_mode(loading_mode)
        except Exception as e:
            self.show_err_msgbar(e)
            return

        if loading_mode == LOADING_MODES.STATIC:
            con_load.start_args(args)
        else:
            kw.update(self.make_tile_params())
            con_load.start_args( make_qt_args(*a, **kw))


    def iter_checked_xyz_subnode(self):
        """ iterate through visible xyz nodes (vector layer and group node)
        """
        root = QgsProject.instance().layerTreeRoot()
        for vl in root.checkedLayers():
            if is_xyz_supported_layer(vl):
                yield vl
        for g in iter_group_node(root):
            if (len(g.findLayers()) == 0 
                and g.isVisible()
                and is_xyz_supported_node(g)):
                yield g

    def iter_all_xyz_node(self):
        """ iterate through xyz group nodes
        """
        for qnode in self._iter_all_xyz_node():
            yield qnode

    def iter_update_all_xyz_node(self):
        """ iterate through xyz group nodes, with meta version check and updated.
        """
        for qnode in self._iter_all_xyz_node(fn_node=updated_xyz_node):
            yield qnode

    def _iter_all_xyz_node(self, fn_node=lambda a: None):
        """ iterate through xyz group nodes, with custom function fn_node applied to every node
        """
        root = QgsProject.instance().layerTreeRoot()
        for g in iter_group_node(root):
            fn_node(g)
            if is_xyz_supported_node(g):
                yield g
    
    def extent_action(self, rect0, rect1):
        diff = [r0 - r1 for r0,r1 in zip(rect0,rect1)]
        x_sign = diff[0] * diff[2]
        y_sign = diff[1] * diff[3]
        if x_sign >= 0 and y_sign >= 0: # same direction
            return "pan"
        elif x_sign < 0 and y_sign < 0:
            return "zoom"
        elif x_sign * y_sign == 0 and x_sign + y_sign < 0:
            return "resize"
        else:
            return "unknown"

    def reload_tile(self):
        canvas = self.iface.mapCanvas()
        rect = bbox_utils.extent_to_rect(bbox_utils.get_bounding_box(canvas))
        ext_action = self.extent_action(rect,self.lastRect)
        print_qgis("Extent action: ", ext_action,rect)
        self.lastRect = rect
        if ext_action not in ["pan", "zoom"]: 
            return
        level = tile_utils.get_zoom_for_current_map_scale(canvas)
        kw = self.make_tile_params(rect, level)
        # kw["limit"] = 100

        lst_con = self._get_lst_reloading_con()
        for con in lst_con:
            print_qgis(con.status)
            print_qgis("loading tile", level, rect)
            con.restart(**kw)

    def _get_lst_reloading_con(self):
        """ Return list of loader to be reload, that has
        + any vlayer in group is visible
        + and no vlayer in group is in edit mode
        """
        editing_xid = set()
        unique_xid = set()
        for qnode in self.iter_checked_xyz_subnode():
            xlayer_id = get_customProperty_str(qnode, QProps.UNIQUE_ID)
            if xlayer_id in editing_xid: continue
            if hasattr(qnode, "isEditable") and qnode.isEditable():
                editing_xid.add(xlayer_id)
                continue
            con = self.con_man.get_interactive_loader(xlayer_id)
            if (con and con.layer and
                self.is_all_layer_edit_buffer_empty(con.layer)
            ):
                unique_xid.add(xlayer_id)
            else:
                continue
        
        # print_qgis(editing_xid, unique_xid)
        # print_qgis(unique_xid.difference(editing_xid))
        # print_qgis(self.con_man._layer_ptr)
        
        return [
            self.con_man.get_interactive_loader(xlayer_id)
            for xlayer_id in unique_xid.difference(editing_xid)
        ]
                

    def is_all_layer_edit_buffer_empty(self, layer: XYZLayer) -> bool:
        return all(
            layer_buffer.is_empty()
            for layer_buffer in (
                self.edit_buffer.get_layer_buffer(vlayer.id())
                for vlayer in layer.iter_layer()
            ) if layer_buffer
        )

    def add_basemap_layer(self, args):
        a, kw = parse_qt_args(args)
        meta, app_id, app_code, api_key = a
        self.auth_manager.save(app_id, app_code, api_key)
        basemap.add_basemap_layer( meta, app_id, app_code, api_key)

    ############### 
    # import project function
    ###############

    def import_project(self):
        self.init_all_layer_loader()

        # # restart static loader once
        # for con in self.con_man.get_all_static_loader():
        #     # truncate all feature 
        #     con.restart()

    def make_loader_from_mode(self, loading_mode, layer=None):
        if loading_mode not in LOADING_MODES:
            raise InvalidLoadingMode(loading_mode)
        option = dict(zip(LOADING_MODES, [
            (LiveTileLayerLoader, self.con_man.add_persistent_loader, self.make_cb_success_args("Tiles loaded", dt=2)),
            (TileLayerLoader, self.con_man.add_persistent_loader, self.make_cb_success_args("Tiles loaded", dt=2)),
            (LoadLayerController, self.con_man.add_static_loader, self.make_cb_success_args("Loading finish", dt=3))
            ])).get(loading_mode)
        if not option:
            return

        loader_class, fn_register, cb_success_args = option
        con_load = loader_class(self.network, n_parallel=1, layer=layer)
        con_load.signal.results.connect( cb_success_args)
        con_load.signal.error.connect( self.cb_handle_error_msg )

        cb_info = self.make_cb_info_args("Loading status", dt=3)
        con_load.signal.info.connect( cb_info)

        ptr = fn_register(con_load)
        return con_load


    def init_layer_loader(self, qnode):
        layer = XYZLayer.load_from_qnode(qnode)
        loading_mode = layer.loader_params.get("loading_mode")
        if loading_mode not in LOADING_MODES:
            # # approach 1: backward compatible, import project
            # # invalid loading mode default to live
            # old = loading_mode
            # loading_mode = LOADING_MODES.LIVE
            # layer.update_loader_params(loading_mode=loading_mode) # save new loading_mode to layer
            # # TODO prompt user for handling invalid loading mode layer
            # self.show_info_msgbar("Import XYZ Layer", 
            #     "Undefined loading mode: %s, " % old +  
            #     "default to %s loading " % (loading_mode) +
            #     "(layer: %s)" % layer.get_name())

            # approach 2: not backward compatible, but no data loss
            self.show_warning_msgbar("Import XYZ Layer", 
                "Undefined loading mode: %s, " % loading_mode + 
                "loading disabled " +
                "(layer: %s)" % layer.get_name())
            return
            
        return self.make_loader_from_mode(loading_mode, layer=layer)

    def init_all_layer_loader(self):
        cnt = 0
        for qnode in self.iter_update_all_xyz_node():
            xlayer_id = get_customProperty_str(qnode, QProps.UNIQUE_ID)
            con = self.con_man.get_loader(xlayer_id)
            if con: continue
            try: 
                con = self.init_layer_loader(qnode)
                if not con: continue
                cnt += 1
            except Exception as e:
                self.show_err_msgbar(e)
                

        # print_qgis(self.con_man._layer_ptr)
        self.show_success_msgbar("Import XYZ Layer", "%s XYZ Layer imported"%cnt, dt=2)


    def cb_qnode_visibility_changed(self, qnode):
        if qnode.isVisible(): return
        xlayer_id = get_customProperty_str(qnode, QProps.UNIQUE_ID)
        con = self.con_man.get_interactive_loader(xlayer_id)
        if con:
            con.stop_loading()

    def cb_qnodes_deleting(self, parent, i0, i1):
        key = (parent,i0,i1)
        is_parent_root = not parent.parent()
        lst = parent.children()
        for i in range(i0, i1+1):
            qnode = lst[i]
            if (is_parent_root and is_xyz_supported_node(qnode)):
                xlayer_id = get_customProperty_str(qnode, QProps.UNIQUE_ID)
                self.pending_delete_qnodes.setdefault(key, list()).append(xlayer_id)
                self.con_man.remove_persistent_loader(xlayer_id)
            # is possible to handle vlayer delete here
            # instead of handle in layer.py via callbacks

    def cb_qnodes_deleted(self, parent, i0, i1):
        key = (parent,i0,i1)
        for xlayer_id in self.pending_delete_qnodes.pop(key, list()):
            self.con_man.remove_persistent_loader(xlayer_id)

    ############### 
    # Open dialog
    ###############
    def open_clear_cache_dialog(self):
        dialog = ConfirmDialog("Delete cache will make loaded layer unusable !!")
        ret = dialog.exec_()
        if ret != dialog.Ok: return
        
        utils.clear_cache()

    def open_connection_dialog(self):
        dialog = self.new_main_dialog()
        
        vlayer = self.iface.activeLayer()
        dialog.set_layer( vlayer)
        
        dialog.exec_()
        self.con_man.finish_fast()
        # self.startTime = time.time()
    def open_sync_edit_dialog(self):
        vlayer = self.iface.activeLayer()
        layer_buffer = self.edit_buffer.get_layer_buffer(vlayer.id())

        lst_added_feat, removed_ids = layer_buffer.get_sync_feat()
        conn_info = layer_buffer.get_conn_info()

        # print_qgis("lst_added_feat: ",lst_added_feat)
        # print_qgis("removed_feat: ", removed_ids)

        con = EditSyncController(self.network)
        self.con_man.add_on_demand_controller(con)
        con.signal.finished.connect( layer_buffer.sync_complete)
        con.signal.results.connect( self.make_cb_success_args("Sync edit finish") )
        con.signal.error.connect( self.cb_handle_error_msg )

        con.start(conn_info, layer_buffer, lst_added_feat, removed_ids)
    def add_menu_action(self,
                        icon_path: str,
                        text: str,
                        callback: Callable,
                        enabled_flag: bool = True,
                        add_to_menu: bool = True,
                        menu_type: str = None,
                        add_to_toolbar: bool = True,
                        status_tip: str = None,
                        whats_this: str = None,
                        parent: QWidget = None) -> QAction:
        """
        Add a toolbar icon to the toolbar, associating it with an action linked
        to a function called on click; the action is also added to self.actions['menus'] list.

        Args:

            icon_path: Path to the icon for this action. Can be a resource
                path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
            text: Text that should be shown in menu items for this action.
            callback: Function to be called when the action is triggered.
            enabled_flag: Whether the action should be enabled by default or not.
            add_to_menu: Whether the action should be added to the menu.
            menu_type: Type of menu add the action to, other than the main one (None).
            add_to_toolbar: Whether the action should also be added to the toolbar.
            status_tip: Optional text to show in a popup when mouse pointer hovers over the action.
            whats_this: Optional text to show in the status bar when the mouse pointer
                hovers over the action.
            parent: Parent Qt widget for the new action

        Returns:
            The action that was created.
        """
        if not menu_type:
            logger.debug("Add action \"%s\" to \"plugin\" toolbar menu" % text)
        else:
            logger.debug("Add action \"%s\" to \"%s\" toolbar menu" %
                         (text, menu_type))
        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)
        # Optionally setup "Status" tip dialog
        if status_tip is not None:
            action.setStatusTip(status_tip)
        # Optionally setup "what's this?" tip dialog
        if whats_this is not None:
            action.setWhatsThis(whats_this)
        # Optionally adds plugin menu icon to Plugins toolbar
        if add_to_toolbar:
            self.iface.addToolBarIcon(action)
        # Add menu items to proper plugin category
        if add_to_menu:
            if not menu_type:
                self.iface.addPluginToMenu(self.menu, action)
            elif menu_type.lower() == "database":
                self.iface.addPluginToDatabaseMenu(self.menu, action)
            elif menu_type.lower() == "raster":
                self.iface.addPluginToRasterMenu(self.menu, action)
            elif menu_type.lower() == "vector":
                self.iface.addPluginToVectorMenu(self.menu, action)
            elif menu_type.lower() == "web":
                self.iface.addPluginToWebMenu(self.menu, action)
            else:
                logger.error("Invalid menu choice")
                raise RuntimeError("Invalid choice for action adding: %s" %
                                   menu_type)
        # Updates actions array and return inserted elements
        self.actions['menus'].append(action)
        return action
class DEMto3D(object):
    """QGIS Plugin Implementation."""
    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   'DEMto3D_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.action = None
        self.menu = '&DEMto3D'

        self.window = True

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('DEMto3D', message)

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""
        icon = QIcon(":/plugins/DEMto3D/icons/demto3d.png")
        text = self.tr("DEM 3D printing")
        parent = self.iface.mainWindow()
        self.action = QAction(icon, text, parent)
        self.action.setObjectName(text)
        self.action.setStatusTip(text)
        self.action.triggered.connect(self.run)

        self.iface.addRasterToolBarIcon(self.action)
        self.iface.addPluginToRasterMenu(self.menu, self.action)

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        self.iface.removePluginRasterMenu(self.menu, self.action)
        self.iface.removeRasterToolBarIcon(self.action)

    def run(self):
        layers = self.iface.mapCanvas().layers()
        raster = False
        if layers:
            for layer in layers:
                if layer.type() == layer.RasterLayer and QgsProject.instance(
                ).layerTreeRoot().findLayer(layer).isVisible():
                    raster = True
                    break
            if raster and self.window:
                self.window = False
                demto3d_dlg = DEMto3D_dialog.DEMto3DDialog(self.iface)
                demto3d_dlg.exec_()
                canvas = self.iface.mapCanvas()
                if demto3d_dlg.extent:
                    canvas.scene().removeItem(demto3d_dlg.extent)
                if demto3d_dlg.divisions:
                    canvas.scene().removeItem(demto3d_dlg.divisions)
                self.window = True
            elif not raster:
                QMessageBox.information(
                    self.iface.mainWindow(), "DEMto3D",
                    self.tr("No visible raster layer loaded"))
        elif not layers:
            QMessageBox.information(self.iface.mainWindow(), "DEMto3D",
                                    self.tr("No visible raster layer loaded"))
Example #7
0
def create_action(
        icon_path,
        text,
        callback,
        enabled_flag=True,
        status_tip=None,
        whats_this=None,
        parent=None,
        object_name=None):

    """
    # adapted from RedLayers by E. Ferreguti
    Create an action.

    :param icon_path: Path to the icon for this action. Can be a resource
        path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
    :type icon_path: str

    :param text: Text that should be shown in menu items for this action.
    :type text: str

    :param callback: Function to be called when the action is triggered.
    :type callback: function

    :param enabled_flag: A flag indicating if the action should be enabled
        by default. Defaults to True.
    :type enabled_flag: bool

    :param status_tip: Optional text to show in a popup when mouse pointer
        hovers over the action.
    :type status_tip: str

    :param parent: Parent widget for the new action. Defaults None.
    :type parent: QWidget

    :param whats_this: Optional text to show in the status bar when the
        mouse pointer hovers over the action.

    :param object_name: Optional name to identify objects during customization
    :type object_name: str

    :returns: The action that was created.
    :rtype: QAction
    """

    icon = QIcon(icon_path)

    action = QAction(icon, text, parent)
    if callback:
        action.triggered.connect(callback)
    action.setEnabled(enabled_flag)

    if status_tip:
        action.setStatusTip(status_tip)

    if whats_this:
        action.setWhatsThis(whats_this)

    if object_name:
        action.setObjectName(object_name)

    return action
class CreatePoint:
    """QGIS Plugin Implementation."""
    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
         which provides the hook by which you can manipulate the QGIS
          application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   'CreatePoint_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        # Obtaining the map canvas
        self.canvas = iface.mapCanvas()

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/Create_Point/icon.png'
        #create action that will be run by the plugin
        self.action = QAction("Create Point", self.iface.mainWindow())
        self.action.setIcon(QIcon(icon_path))
        self.action.setWhatsThis("Create Point")
        self.action.setStatusTip("Click On Map And Draw Point")

        # add plugin menu to Vector toolbar
        self.iface.addPluginToMenu("Create_Point", self.action)

        # add icon to new menu item in Vector toolbar
        self.iface.addToolBarIcon(self.action)

        # connect action to the run method
        self.action.triggered.connect(self.run)

        # prepare map tool
        self.mapTool = Map_Tool(self.iface)
        #self.iface.mapCanvas().mapToolSet.connect(self.mapToolChanged)

    def unload(self):
        """Actions to run when the plugin is unloaded"""
        # remove menu and icon from the menu
        self.iface.removeToolBarIcon(self.action)
        self.iface.removePluginMenu("Create_Point", self.action)

        if self.iface.mapCanvas().mapTool() == self.mapTool:
            self.iface.mapCanvas().unsetMapTool(self.mapTool)
        del self.mapTool

    def run(self):
        #Check Active Layer Present Or Not
        self.active_point_layer = self.iface.mapCanvas().currentLayer()
        if not self.active_point_layer:
            self.iface.messageBar().pushMessage(
                "Active Layer",
                "No Active Layer Found Please Select Point Layer",
                level=Qgis.Warning)
        else:
            self.layer_type = self.active_point_layer.geometryType()
            if self.layer_type == QgsWkbTypes.PointGeometry:
                self.iface.mapCanvas().setMapTool(self.mapTool)
            else:
                self.iface.messageBar().pushMessage(
                    "Active Layer",
                    "Active Layer Is Not Point Type Layer Please Select Point Layer",
                    level=Qgis.Warning)
Example #9
0
class XYZHubConnector(object):
    """base plugin"""
    def __init__(self, iface):
        """init"""
        import sys
        print(sys.version)
        self.iface = iface
        self.web_menu = "&XYZ Hub Connector"
        self.init_modules()
        self.obj = self

    def initGui(self):
        """startup"""

        parent = self.iface.mainWindow()

        ######## action, button

        icon = QIcon("%s/%s" % (config.PLUGIN_DIR, "images/xyz.png"))
        icon_bbox = QIcon("%s/%s" % (config.PLUGIN_DIR, "images/bbox.svg"))
        self.action_connect = QAction(icon, "XYZ Hub Connection", parent)
        self.action_connect.setWhatsThis(
            QCoreApplication.translate(PLUGIN_NAME, "WhatsThis message"))
        self.action_connect.setStatusTip(
            QCoreApplication.translate(PLUGIN_NAME, "status tip message"))

        self.action_magic_sync = QAction("Magic Sync (EXPERIMENTAL)", parent)

        if self.iface.activeLayer() is None:
            self.action_magic_sync.setEnabled(False)

        # self.action_magic_sync.setVisible(False) # disable magic sync

        ######## CONNECT action, button

        self.action_connect.triggered.connect(self.open_connection_dialog)
        self.action_magic_sync.triggered.connect(self.open_magic_sync_dialog)

        ######## Add the toolbar + button
        self.toolbar = self.iface.addToolBar(PLUGIN_NAME)
        self.toolbar.setObjectName("XYZ Hub Connector")

        self.actions = [self.action_connect]

        for a in self.actions:
            self.iface.addPluginToWebMenu(self.web_menu, a)

        # # uncomment to use menu button
        # tool_btn = QToolButton(self.toolbar)
        # for a in self.actions:
        #     tool_btn.addAction(a)
        # tool_btn.setDefaultAction(self.action_connect)
        # tool_btn.setPopupMode(tool_btn.MenuButtonPopup)
        # self.xyz_widget_action = self.toolbar.addWidget(tool_btn) # uncomment to use menu button

        self.toolbar.addAction(self.action_connect)

        self.action_help = None

        progress = QProgressBar()
        progress.setMinimum(0)
        progress.setMaximum(0)
        progress.reset()
        progress.hide()
        # progress = self.iface.statusBarIface().children()[2] # will be hidden by qgis
        self.iface.statusBarIface().addPermanentWidget(progress)
        self.pb = progress

    def init_modules(self):
        # util.init_module()

        # parent = self.iface.mainWindow()
        parent = QgsProject.instance()

        self.secret = Secret(config.USER_PLUGIN_DIR + "/secret.ini")
        ######## Init xyz modules
        self.map_basemap_meta = basemap.load_default_xml()
        self.auth_manager = AuthManager(config.USER_PLUGIN_DIR + "/auth.ini")

        self.token_model = GroupTokenModel(parent)
        # self.layer = LayerManager(parent, self.iface)

        self.network = NetManager(parent)

        self.con_man = LoaderManager()
        self.con_man.config(self.network)
        self.layer_man = LayerManager()

        ######## data flow
        # self.conn_info = SpaceConnectionInfo()

        ######## token
        self.token_model.load_ini(config.USER_PLUGIN_DIR + "/token.ini")

        ######## CALLBACK

        self.con_man.ld_pool.signal.progress.connect(
            self.cb_progress_busy)  #, Qt.QueuedConnection
        self.con_man.ld_pool.signal.finished.connect(self.cb_progress_done)

        QgsProject.instance().layersWillBeRemoved["QStringList"].connect(
            self.layer_man.remove)
        QgsProject.instance().layersWillBeRemoved["QStringList"].connect(
            self.con_man.remove)

        # self.iface.currentLayerChanged.connect( self.cb_layer_selected) # UNCOMMENT

        if LOG_TO_FILE:
            QgsApplication.messageLog().messageReceived.connect(cb_log_qgis)

    def unload_modules(self):
        # self.con_man.disconnect_ux( self.iface)
        QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect(
            self.layer_man.remove)
        QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect(
            self.con_man.remove)

        # utils.disconnect_silent(self.iface.currentLayerChanged)

        # self.iface.mapCanvas().extentsChanged.disconnect( self.debug_reload)

        self.secret.deactivate()

        # close_file_logger()
        pass

    def unload(self):
        """teardown"""
        self.unload_modules()
        # remove the plugin menu item and icon
        self.iface.removePluginWebMenu(self.web_menu, self.action_help)

        self.toolbar.clear(
        )  # remove action from custom toolbar (toolbar still exist)
        self.toolbar.deleteLater()

        for a in self.actions:
            self.iface.removePluginWebMenu(self.web_menu, a)

    ###############
    # Callback
    ###############
    def cb_layer_selected(self, qlayer):
        flag_xyz = True if qlayer is not None and self.layer.is_xyz_supported_layer(
            qlayer) else False
        # disable magic sync
        # self.action_magic_sync.setEnabled(flag_xyz)

    ###############
    # Callback of action (main function)
    ###############
    def cb_success_msg(self, msg, info=""):
        self.iface.messageBar().pushMessage(msg, info, Qgis.Success, 5)

    def make_cb_success(self, msg, info=""):
        def _cb_success_msg():
            txt = info
            self.cb_success_msg(msg, txt)

        return _cb_success_msg

    def make_cb_success_args(self, msg, info=""):
        def _cb_success_msg(args):
            a, kw = parse_qt_args(args)
            txt = ". ".join(map(str, a))
            self.cb_success_msg(msg, txt)

        return _cb_success_msg

    def cb_handle_error_msg(self, e):
        err = parse_exception_obj(e)
        if isinstance(err, ChainInterrupt):
            e0, idx = err.args[0:2]
            if isinstance(
                    e0,
                (net_handler.NetworkError, net_handler.NetworkTimeout)):
                ok = self.show_net_err(e0)
                if ok: return
            elif isinstance(e0, EmptyXYZSpaceError):
                ret = exec_warning_dialog(
                    "XYZ Hub", "Requested query returns no features")
                return
        self.show_err_msgbar(err)

    def show_net_err(self, err):
        reply_tag, status, reason, body, err_str, url = err.args[:6]
        if reply_tag in ["count", "statistics"]:  # too many error
            # msg = "Network Error: %s: %s. %s"%(status, reason, err_str)
            return 1

        detail = "\n".join(["Request:", url, "", "Response:", body])
        msg = ("%s: %s\n" % (status, reason) +
               "There was a problem connecting to the server")
        if status == 403:
            msg += "\n\n" + "Please make sure that the token has WRITE permission"
        ret = exec_warning_dialog("Network Error", msg, detail)
        return 1

    def show_err_msgbar(self, err):
        self.iface.messageBar().pushMessage(TAG_PLUGIN, repr(err),
                                            Qgis.Warning, 3)
        msg = format_traceback(err)
        QgsMessageLog.logMessage(msg, TAG_PLUGIN, Qgis.Warning)

    def cb_progress_busy(self, n_active):
        if n_active > 1: return
        self.flag_pb_show = True
        self.cb_progress_refresh()

    def cb_progress_done(self):
        self.flag_pb_show = False
        self.cb_progress_refresh()

    def cb_progress_refresh(self):
        if not hasattr(self, "flag_pb_show"): return

        pb = self.pb
        if self.flag_pb_show:
            pb.show()
            # print_qgis("show",pb)
        else:
            pb.hide()
            # print_qgis("hide")

    ###############
    # Action (main function)
    ###############
    # unused
    def load_bbox(self, con, args):
        bbox = bbox_utils.extend_to_bbox(
            bbox_utils.get_bounding_box(self.iface))
        a, kw = parse_qt_args(args)
        kw["bbox"] = bbox
        kw["limit"] = 1000
        con.start(*a, **kw)

    # UNUSED
    def refresh_canvas(self):
        # self.iface.activeLayer().triggerRepaint()
        self.iface.mapCanvas().refresh()

    def previous_canvas_extent(self):
        self.iface.mapCanvas().zoomToPreviousExtent()

    #

    def new_main_dialog(self):
        parent = self.iface.mainWindow()
        dialog = MainDialog(parent)

        dialog.config(self.token_model)
        dialog.config_secret(self.secret)
        auth = self.auth_manager.get_auth()
        dialog.config_basemap(self.map_basemap_meta, auth)

        con = self.con_man.make_con("create")
        con.signal.finished.connect(
            dialog.btn_use.clicked.emit)  # can be optimized !!
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("list")
        con.signal.results.connect(make_fun_args(dialog.cb_display_spaces))
        con.signal.error.connect(self.cb_handle_error_msg)
        con.signal.error.connect(lambda e: dialog.cb_enable_token_ui())
        con.signal.finished.connect(dialog.cb_enable_token_ui)

        con = self.con_man.make_con("edit")
        con.signal.finished.connect(dialog.btn_use.clicked.emit)
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("delete")
        con.signal.results.connect(dialog.btn_use.clicked.emit)
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("stat")
        con.signal.results.connect(make_fun_args(
            dialog.cb_display_space_count))
        con.signal.error.connect(self.cb_handle_error_msg)

        ############ clear cache btn
        dialog.signal_clear_cache.connect(self.open_clear_cache_dialog)

        ############ add map tile btn
        dialog.signal_add_basemap.connect(self.add_basemap_layer)

        ############ btn: new, edit, delete space
        dialog.signal_new_space.connect(self.start_new_space)
        dialog.signal_edit_space.connect(self.start_edit_space)
        dialog.signal_del_space.connect(self.start_delete_space)

        ############ Use Token btn
        dialog.signal_use_token.connect(lambda a: self.con_man.finish_fast())
        dialog.signal_use_token.connect(self.start_use_token)

        ############ get count
        dialog.signal_space_count.connect(
            self.start_count_feat,
            Qt.QueuedConnection)  # queued -> non-blocking ui

        ############ connect btn
        dialog.signal_space_connect.connect(self.start_load_layer)

        ############ upload btn
        dialog.signal_upload_space.connect(self.start_upload_space)

        return dialog

    def start_new_space(self, args):
        con = self.con_man.get_con("create")
        con.start_args(args)

    def start_edit_space(self, args):
        con = self.con_man.get_con("edit")
        con.start_args(args)

    def start_delete_space(self, args):
        con = self.con_man.get_con("delete")
        con.start_args(args)

    def start_use_token(self, args):
        con = self.con_man.get_con("list")
        con.start_args(args)

    def start_count_feat(self, args):
        con = self.con_man.get_con("stat")
        con.start_args(args)

    def start_upload_space(self, args):
        con_upload = UploadLayerController(self.network, n_parallel=2)
        self.con_man.add_background(con_upload)
        # con_upload.signal.finished.connect( self.make_cb_success("Uploading finish") )
        con_upload.signal.results.connect(
            self.make_cb_success_args("Uploading finish"))
        con_upload.signal.error.connect(self.cb_handle_error_msg)

        con = InitUploadLayerController(self.network)
        self.con_man.add_background(con)

        con.signal.results.connect(con_upload.start_args)
        con.signal.error.connect(self.cb_handle_error_msg)

        con.start_args(args)

    def start_load_layer(self, args):
        # create new con
        # config
        # run

        ############ connect btn
        con_load = LoadLayerController(self.network, n_parallel=1)
        self.con_man.add_background(con_load)
        # con_load.signal.finished.connect( self.make_cb_success("Loading finish") )
        con_load.signal.results.connect(
            self.make_cb_success_args("Loading finish"))
        # con_load.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
        con_load.signal.error.connect(self.cb_handle_error_msg)

        con_load.start_args(args)

        # con.signal.results.connect( self.layer_man.add_args) # IMPORTANT

    def add_basemap_layer(self, args):
        a, kw = parse_qt_args(args)
        meta, app_id, app_code = a
        self.auth_manager.save(app_id, app_code)
        basemap.add_basemap_layer(meta, app_id, app_code)

    ###############
    # Open dialog
    ###############
    def open_clear_cache_dialog(self):
        dialog = ConfirmDialog(
            "Delete cache will make loaded layer unusable !!")
        ret = dialog.exec_()
        if ret != dialog.Ok: return

        utils.clear_cache()

    def open_connection_dialog(self):
        dialog = self.new_main_dialog()

        vlayer = self.iface.activeLayer()
        dialog.set_layer(vlayer)

        dialog.exec_()
        self.con_man.finish_fast()
        # self.startTime = time.time()

    # not used
    def open_magic_sync_dialog(self):
        pass
Example #10
0
class MetaSearchPlugin(object):

    """base plugin"""

    def __init__(self, iface):
        """init"""

        self.iface = iface
        self.context = StaticContext()
        self.action_run = None
        self.action_help = None
        self.dialog = None
        self.web_menu = '&MetaSearch'

    def initGui(self):
        """startup"""

        # run
        run_icon = QIcon('%s/%s' % (self.context.ppath,
                                    'images/MetaSearch.png'))
        self.action_run = QAction(run_icon, 'MetaSearch',
                                  self.iface.mainWindow())
        self.action_run.setWhatsThis(QCoreApplication.translate('MetaSearch',
                                                                'MetaSearch plugin'))
        self.action_run.setStatusTip(QCoreApplication.translate('MetaSearch',
                                                                'Search Metadata Catalogues'))

        self.action_run.triggered.connect(self.run)

        self.iface.addWebToolBarIcon(self.action_run)
        self.iface.addPluginToWebMenu(self.web_menu, self.action_run)

        # help
        help_icon = QgsApplication.getThemeIcon('/mActionHelpContents.svg')
        self.action_help = QAction(help_icon, 'Help', self.iface.mainWindow())
        self.action_help.setWhatsThis(QCoreApplication.translate('MetaSearch',
                                                                 'MetaSearch plugin help'))
        self.action_help.setStatusTip(QCoreApplication.translate('MetaSearch',
                                                                 'Get Help on MetaSearch'))
        self.action_help.triggered.connect(self.help)

        self.iface.addPluginToWebMenu(self.web_menu, self.action_help)

        # prefab the dialog but not open it yet
        self.dialog = MetaSearchDialog(self.iface)

    def unload(self):
        """teardown"""

        # remove the plugin menu item and icon
        self.iface.removePluginWebMenu(self.web_menu, self.action_run)
        self.iface.removePluginWebMenu(self.web_menu, self.action_help)
        self.iface.removeWebToolBarIcon(self.action_run)

    def run(self):
        """open MetaSearch"""

        self.dialog.exec_()

    def help(self):
        """open help in user's default web browser"""

        open_url(get_help_url())
Example #11
0
class Plugin():
    """The QGIS interface implementation for the InaSAFE plugin.

    This class acts as the 'glue' between QGIS and our custom logic.
    It creates a toolbar and menu bar entry and launches the InaSAFE user
    interface if these are activated.
    """
    def __init__(self, iface):
        """Class constructor.

        On instantiation, the plugin instance will be assigned a copy
        of the QGIS iface object which will allow this plugin to access and
        manipulate the running QGIS instance that spawned it.

        :param iface:Quantum GIS iface instance. This instance is
            automatically passed to the plugin by QGIS when it loads the
            plugin.
        :type iface: QgisAppInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        self.dock_widget = None

        # Actions
        self.action_add_layers = None
        self.action_add_osm_layer = None
        self.action_add_petabencana_layer = None
        self.action_batch_runner = None
        self.action_dock = None
        self.action_extent_selector = None
        self.action_field_mapping = None
        self.action_multi_exposure = None
        self.action_function_centric_wizard = None
        self.action_import_dialog = None
        self.action_keywords_wizard = None
        self.action_minimum_needs = None
        self.action_minimum_needs_config = None
        self.action_multi_buffer = None
        self.action_options = None
        self.action_run_tests = None
        self.action_save_scenario = None
        self.action_shake_converter = None
        self.action_show_definitions = None
        self.action_toggle_rubberbands = None
        self.action_metadata_converter = None

        self.translator = None
        self.toolbar = None
        self.wizard = None
        self.actions = []  # list of all QActions we create for InaSAFE

        self.message_bar_item = None
        # Flag indicating if toolbar should show only common icons or not
        self.full_toolbar = False
        # print self.tr('InaSAFE')
        # For enable/disable the keyword editor icon
        self.iface.currentLayerChanged.connect(self.layer_changed)

        developer_mode = setting('developer_mode', False, expected_type=bool)
        self.hide_developer_buttons = (inasafe_release_status == 'final'
                                       and not developer_mode)

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('Plugin', message)

    def add_action(self, action, add_to_toolbar=True, add_to_legend=False):
        """Add a toolbar icon to the InaSAFE toolbar.

        :param action: The action that should be added to the toolbar.
        :type action: QAction

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the InaSAFE toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param add_to_legend: Flag indicating whether the action should also
            be added to the layer legend menu. Default to False.
        :type add_to_legend: bool
        """
        # store in the class list of actions for easy plugin unloading
        self.actions.append(action)
        self.iface.addPluginToMenu(self.tr('InaSAFE'), action)
        if add_to_toolbar:
            self.toolbar.addAction(action)
        if add_to_legend:
            # The id is the action name without spaces, tabs ...
            self.iface.addCustomActionForLayerType(action, self.tr('InaSAFE'),
                                                   QgsMapLayer.VectorLayer,
                                                   True)
            self.iface.addCustomActionForLayerType(action, self.tr('InaSAFE'),
                                                   QgsMapLayer.RasterLayer,
                                                   True)

    def _create_dock_toggle_action(self):
        """Create action for plugin dockable window (show/hide)."""
        # pylint: disable=W0201
        icon = resources_path('img', 'icons', 'icon.svg')
        self.action_dock = QAction(QIcon(icon), self.tr('Toggle InaSAFE Dock'),
                                   self.iface.mainWindow())
        self.action_dock.setObjectName('InaSAFEDockToggle')
        self.action_dock.setStatusTip(self.tr('Show/hide InaSAFE dock widget'))
        self.action_dock.setWhatsThis(self.tr('Show/hide InaSAFE dock widget'))
        self.action_dock.setCheckable(True)
        self.action_dock.setChecked(True)
        self.action_dock.triggered.connect(self.toggle_dock_visibility)
        self.add_action(self.action_dock)

        # --------------------------------------
        # Create action for keywords creation wizard
        # -------------------------------------

    def _create_keywords_wizard_action(self):
        """Create action for keywords creation wizard."""
        icon = resources_path('img', 'icons', 'show-keyword-wizard.svg')
        self.action_keywords_wizard = QAction(
            QIcon(icon), self.tr('Keywords Creation Wizard'),
            self.iface.mainWindow())
        self.action_keywords_wizard.setStatusTip(
            self.tr('Open InaSAFE keywords creation wizard'))
        self.action_keywords_wizard.setWhatsThis(
            self.tr('Open InaSAFE keywords creation wizard'))
        self.action_keywords_wizard.setEnabled(False)
        self.action_keywords_wizard.triggered.connect(
            self.show_keywords_wizard)
        self.add_action(self.action_keywords_wizard, add_to_legend=True)

    def _create_analysis_wizard_action(self):
        """Create action for IF-centric wizard."""
        icon = resources_path('img', 'icons', 'show-wizard.svg')
        self.action_function_centric_wizard = QAction(
            QIcon(icon), self.tr('Impact Function Centric Wizard'),
            self.iface.mainWindow())
        self.action_function_centric_wizard.setStatusTip(
            self.tr('Open InaSAFE impact function centric wizard'))
        self.action_function_centric_wizard.setWhatsThis(
            self.tr('Open InaSAFE impact function centric wizard'))
        self.action_function_centric_wizard.setEnabled(True)
        self.action_function_centric_wizard.triggered.connect(
            self.show_function_centric_wizard)
        self.add_action(self.action_function_centric_wizard)

    def _create_options_dialog_action(self):
        """Create action for options dialog."""
        icon = resources_path('img', 'icons', 'configure-inasafe.svg')
        self.action_options = QAction(QIcon(icon), self.tr('Options'),
                                      self.iface.mainWindow())
        self.action_options.setStatusTip(
            self.tr('Open InaSAFE options dialog'))
        self.action_options.setWhatsThis(
            self.tr('Open InaSAFE options dialog'))
        self.action_options.triggered.connect(self.show_options)
        self.add_action(self.action_options, add_to_toolbar=self.full_toolbar)

    def _create_minimum_needs_action(self):
        """Create action for minimum needs dialog."""
        icon = resources_path('img', 'icons', 'show-minimum-needs.svg')
        self.action_minimum_needs = QAction(
            QIcon(icon), self.tr('Minimum Needs Calculator'),
            self.iface.mainWindow())
        self.action_minimum_needs.setStatusTip(
            self.tr('Open InaSAFE minimum needs calculator'))
        self.action_minimum_needs.setWhatsThis(
            self.tr('Open InaSAFE minimum needs calculator'))
        self.action_minimum_needs.triggered.connect(self.show_minimum_needs)
        self.add_action(self.action_minimum_needs,
                        add_to_toolbar=self.full_toolbar)

    def _create_multi_buffer_action(self):
        """Create action for multi buffer dialog."""
        icon = resources_path('img', 'icons', 'show-multi-buffer.svg')
        self.action_multi_buffer = QAction(QIcon(icon),
                                           self.tr('Multi Buffer'),
                                           self.iface.mainWindow())
        self.action_multi_buffer.setStatusTip(
            self.tr('Open InaSAFE multi buffer'))
        self.action_multi_buffer.setWhatsThis(
            self.tr('Open InaSAFE multi buffer'))
        self.action_multi_buffer.triggered.connect(self.show_multi_buffer)
        self.add_action(self.action_multi_buffer,
                        add_to_toolbar=self.full_toolbar)

    def _create_minimum_needs_options_action(self):
        """Create action for global minimum needs dialog."""
        icon = resources_path('img', 'icons', 'show-global-minimum-needs.svg')
        self.action_minimum_needs_config = QAction(
            QIcon(icon), self.tr('Minimum Needs Configuration'),
            self.iface.mainWindow())
        self.action_minimum_needs_config.setStatusTip(
            self.tr('Open InaSAFE minimum needs configuration'))
        self.action_minimum_needs_config.setWhatsThis(
            self.tr('Open InaSAFE minimum needs configuration'))
        self.action_minimum_needs_config.triggered.connect(
            self.show_minimum_needs_configuration)
        self.add_action(self.action_minimum_needs_config,
                        add_to_toolbar=self.full_toolbar)

    def _create_shakemap_converter_action(self):
        """Create action for converter dialog."""
        icon = resources_path('img', 'icons', 'show-converter-tool.svg')
        self.action_shake_converter = QAction(QIcon(icon),
                                              self.tr('Shakemap Converter'),
                                              self.iface.mainWindow())
        self.action_shake_converter.setStatusTip(
            self.tr('Open InaSAFE Converter'))
        self.action_shake_converter.setWhatsThis(
            self.tr('Open InaSAFE Converter'))
        self.action_shake_converter.triggered.connect(
            self.show_shakemap_importer)
        self.add_action(self.action_shake_converter,
                        add_to_toolbar=self.full_toolbar)

    def _create_batch_runner_action(self):
        """Create action for batch runner dialog."""
        icon = resources_path('img', 'icons', 'show-batch-runner.svg')
        self.action_batch_runner = QAction(QIcon(icon),
                                           self.tr('Batch Runner'),
                                           self.iface.mainWindow())
        self.action_batch_runner.setStatusTip(self.tr('Open Batch Runner'))
        self.action_batch_runner.setWhatsThis(self.tr('Open Batch Runner'))
        self.action_batch_runner.triggered.connect(self.show_batch_runner)
        self.add_action(self.action_batch_runner,
                        add_to_toolbar=self.full_toolbar)

    def _create_save_scenario_action(self):
        """Create action for save scenario dialog."""
        icon = resources_path('img', 'icons', 'save-as-scenario.svg')
        self.action_save_scenario = QAction(QIcon(icon),
                                            self.tr('Save Current Scenario'),
                                            self.iface.mainWindow())
        message = self.tr('Save current scenario to text file')
        self.action_save_scenario.setStatusTip(message)
        self.action_save_scenario.setWhatsThis(message)
        # noinspection PyUnresolvedReferences
        self.action_save_scenario.triggered.connect(self.save_scenario)
        self.add_action(self.action_save_scenario,
                        add_to_toolbar=self.full_toolbar)

    def _create_osm_downloader_action(self):
        """Create action for import OSM Dialog."""
        icon = resources_path('img', 'icons', 'show-osm-download.svg')
        self.action_import_dialog = QAction(
            QIcon(icon), self.tr('OpenStreetMap Downloader'),
            self.iface.mainWindow())
        self.action_import_dialog.setStatusTip(
            self.tr('OpenStreetMap Downloader'))
        self.action_import_dialog.setWhatsThis(
            self.tr('OpenStreetMap Downloader'))
        self.action_import_dialog.triggered.connect(self.show_osm_downloader)
        self.add_action(self.action_import_dialog, add_to_toolbar=True)

    def _create_geonode_uploader_action(self):
        """Create action for Geonode uploader dialog."""
        icon = resources_path('img', 'icons', 'geonode.png')
        label = tr('Geonode Uploader')
        self.action_geonode = QAction(QIcon(icon), label,
                                      self.iface.mainWindow())
        self.action_geonode.setStatusTip(label)
        self.action_geonode.setWhatsThis(label)
        self.action_geonode.triggered.connect(self.show_geonode_uploader)
        self.add_action(self.action_geonode, add_to_toolbar=False)

    def _create_add_osm_layer_action(self):
        """Create action for import OSM Dialog."""
        icon = resources_path('img', 'icons', 'add-osm-tiles-layer.svg')
        self.action_add_osm_layer = QAction(
            QIcon(icon), self.tr('Add OpenStreetMap Tile Layer'),
            self.iface.mainWindow())
        self.action_add_osm_layer.setStatusTip(
            self.tr('Add OpenStreetMap Tile Layer'))
        self.action_add_osm_layer.setWhatsThis(
            self.tr('Use this to add an OSM layer to your map. '
                    'It needs internet access to function.'))
        self.action_add_osm_layer.triggered.connect(self.add_osm_layer)
        self.add_action(self.action_add_osm_layer, add_to_toolbar=True)

    def _create_show_definitions_action(self):
        """Create action for showing definitions / help."""
        icon = resources_path('img', 'icons', 'show-inasafe-help.svg')
        self.action_show_definitions = QAction(QIcon(icon),
                                               self.tr('InaSAFE Help'),
                                               self.iface.mainWindow())
        self.action_show_definitions.setStatusTip(self.tr('Show InaSAFE Help'))
        self.action_show_definitions.setWhatsThis(
            self.
            tr('Use this to show a document describing all InaSAFE concepts.'))
        self.action_show_definitions.triggered.connect(self.show_definitions)
        self.add_action(self.action_show_definitions, add_to_toolbar=True)

    def _create_metadata_converter_action(self):
        """Create action for showing metadata converter dialog."""
        icon = resources_path('img', 'icons', 'show-metadata-converter.svg')
        self.action_metadata_converter = QAction(
            QIcon(icon), self.tr('InaSAFE Metadata Converter'),
            self.iface.mainWindow())
        self.action_metadata_converter.setStatusTip(
            self.tr('Convert metadata from version 4.3 to version 3.5.'))
        self.action_metadata_converter.setWhatsThis(
            self.tr('Use this tool to convert metadata 4.3 to version 3.5'))
        self.action_metadata_converter.triggered.connect(
            self.show_metadata_converter)
        self.add_action(self.action_metadata_converter,
                        add_to_toolbar=self.full_toolbar)

    def _create_field_mapping_action(self):
        """Create action for showing field mapping dialog."""
        icon = resources_path('img', 'icons', 'show-mapping-tool.svg')
        self.action_field_mapping = QAction(
            QIcon(icon), self.tr('InaSAFE Field Mapping Tool'),
            self.iface.mainWindow())
        self.action_field_mapping.setStatusTip(
            self.tr('Assign field mapping to layer.'))
        self.action_field_mapping.setWhatsThis(
            self.tr('Use this tool to assign field mapping in layer.'))
        self.action_field_mapping.setEnabled(False)
        self.action_field_mapping.triggered.connect(self.show_field_mapping)
        self.add_action(self.action_field_mapping,
                        add_to_toolbar=self.full_toolbar)

    def _create_multi_exposure_action(self):
        """Create action for showing the multi exposure tool."""
        self.action_multi_exposure = QAction(
            QIcon(resources_path('img', 'icons', 'show-multi-exposure.svg')),
            self.tr('InaSAFE Multi Exposure Tool'), self.iface.mainWindow())
        self.action_multi_exposure.setStatusTip(
            self.tr('Open the multi exposure tool.'))
        self.action_multi_exposure.setWhatsThis(
            self.tr('Open the multi exposure tool.'))
        self.action_multi_exposure.setEnabled(True)
        self.action_multi_exposure.triggered.connect(self.show_multi_exposure)
        self.add_action(self.action_multi_exposure,
                        add_to_toolbar=self.full_toolbar)

    def _create_add_petabencana_layer_action(self):
        """Create action for import OSM Dialog."""
        icon = resources_path('img', 'icons', 'add-petabencana-layer.svg')
        self.action_add_petabencana_layer = QAction(
            QIcon(icon), self.tr('Add PetaBencana Flood Layer'),
            self.iface.mainWindow())
        self.action_add_petabencana_layer.setStatusTip(
            self.tr('Add PetaBencana Flood Layer'))
        self.action_add_petabencana_layer.setWhatsThis(
            self.tr('Use this to add a PetaBencana layer to your map. '
                    'It needs internet access to function.'))
        self.action_add_petabencana_layer.triggered.connect(
            self.add_petabencana_layer)
        self.add_action(self.action_add_petabencana_layer,
                        add_to_toolbar=self.full_toolbar)

    def _create_rubber_bands_action(self):
        """Create action for toggling rubber bands."""
        icon = resources_path('img', 'icons', 'toggle-rubber-bands.svg')
        self.action_toggle_rubberbands = QAction(
            QIcon(icon), self.tr('Toggle Scenario Outlines'),
            self.iface.mainWindow())
        message = self.tr('Toggle rubber bands showing scenario extents.')
        self.action_toggle_rubberbands.setStatusTip(message)
        self.action_toggle_rubberbands.setWhatsThis(message)
        # Set initial state
        self.action_toggle_rubberbands.setCheckable(True)
        flag = setting('showRubberBands', False, expected_type=bool)
        self.action_toggle_rubberbands.setChecked(flag)
        # noinspection PyUnresolvedReferences
        self.action_toggle_rubberbands.triggered.connect(
            self.dock_widget.toggle_rubber_bands)
        self.add_action(self.action_toggle_rubberbands)

    def _create_analysis_extent_action(self):
        """Create action for analysis extent dialog."""
        icon = resources_path('img', 'icons', 'set-extents-tool.svg')
        self.action_extent_selector = QAction(QIcon(icon),
                                              self.tr('Set Analysis Area'),
                                              self.iface.mainWindow())
        self.action_extent_selector.setStatusTip(
            self.tr('Set the analysis area for InaSAFE'))
        self.action_extent_selector.setWhatsThis(
            self.tr('Set the analysis area for InaSAFE'))
        self.action_extent_selector.triggered.connect(
            self.show_extent_selector)
        self.add_action(self.action_extent_selector)

    def _create_test_layers_action(self):
        """Create action for adding layers (developer mode, non final only)."""
        if self.hide_developer_buttons:
            return

        icon = resources_path('img', 'icons', 'add-test-layers.svg')
        self.action_add_layers = QAction(QIcon(icon),
                                         self.tr('Add Test Layers'),
                                         self.iface.mainWindow())
        self.action_add_layers.setStatusTip(self.tr('Add test layers'))
        self.action_add_layers.setWhatsThis(self.tr('Add test layers'))
        self.action_add_layers.triggered.connect(self.add_test_layers)

        self.add_action(self.action_add_layers)

    def _create_run_test_action(self):
        """Create action for running tests (developer mode, non final only)."""
        if self.hide_developer_buttons:
            return

        default_package = str(setting('testPackage', 'safe',
                                      expected_type=str))
        msg = self.tr('Run tests in %s' % default_package)

        self.test_button = QToolButton()
        self.test_button.setMenu(QMenu())
        self.test_button.setPopupMode(QToolButton.MenuButtonPopup)

        icon = resources_path('img', 'icons', 'run-tests.svg')
        self.action_run_tests = QAction(QIcon(icon), msg,
                                        self.iface.mainWindow())

        self.action_run_tests.setStatusTip(msg)
        self.action_run_tests.setWhatsThis(msg)
        self.action_run_tests.triggered.connect(self.run_tests)

        self.test_button.menu().addAction(self.action_run_tests)
        self.test_button.setDefaultAction(self.action_run_tests)

        self.action_select_package = QAction(QIcon(icon),
                                             self.tr('Select package'),
                                             self.iface.mainWindow())

        self.action_select_package.setStatusTip(self.tr('Select Test Package'))
        self.action_select_package.setWhatsThis(self.tr('Select Test Package'))
        self.action_select_package.triggered.connect(self.select_test_package)
        self.test_button.menu().addAction(self.action_select_package)
        self.toolbar.addWidget(self.test_button)

        self.add_action(self.action_run_tests, add_to_toolbar=False)
        self.add_action(self.action_select_package, add_to_toolbar=False)

    def _create_dock(self):
        """Create dockwidget and tabify it with the legend."""
        # Import dock here as it needs to be imported AFTER i18n is set up
        from safe.gui.widgets.dock import Dock
        self.dock_widget = Dock(self.iface)
        self.dock_widget.setObjectName('InaSAFE-Dock')
        self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock_widget)
        legend_tab = self.iface.mainWindow().findChild(QApplication, 'Legend')
        if legend_tab:
            self.iface.mainWindow().tabifyDockWidget(legend_tab,
                                                     self.dock_widget)
            self.dock_widget.raise_()

    # noinspection PyPep8Naming
    def initGui(self):
        """Gui initialisation procedure (for QGIS plugin api).

        .. note:: Don't change the name of this method from initGui!

        This method is called by QGIS and should be used to set up
        any graphical user interface elements that should appear in QGIS by
        default (i.e. before the user performs any explicit action with the
        plugin).
        """
        self.toolbar = self.iface.addToolBar('InaSAFE')
        self.toolbar.setObjectName('InaSAFEToolBar')
        self.dock_widget = None
        # Now create the actual dock
        self._create_dock()
        # And all the menu actions
        # Configuration Group
        self._create_dock_toggle_action()
        self._create_options_dialog_action()
        self._create_minimum_needs_options_action()
        self._create_analysis_extent_action()
        self._create_rubber_bands_action()
        self._add_spacer_to_menu()
        self._create_keywords_wizard_action()
        self._create_analysis_wizard_action()
        self._add_spacer_to_menu()
        self._create_field_mapping_action()
        self._create_multi_exposure_action()
        self._create_metadata_converter_action()
        # TODO: temporarily disabled this until we have a fix
        # issue: https://github.com/inasafe/inasafe/issues/5109
        # self._create_osm_downloader_action()
        self._create_add_osm_layer_action()
        self._create_add_petabencana_layer_action()
        self._create_geonode_uploader_action()
        self._create_shakemap_converter_action()
        self._create_minimum_needs_action()
        self._create_multi_buffer_action()
        self._create_test_layers_action()
        self._create_run_test_action()
        self._add_spacer_to_menu()
        self._create_batch_runner_action()
        self._create_save_scenario_action()
        self._add_spacer_to_menu()
        self._create_show_definitions_action()

        # Hook up a slot for when the dock is hidden using its close button
        # or  view-panels
        #
        self.dock_widget.visibilityChanged.connect(self.toggle_inasafe_action)
        # Also deal with the fact that on start of QGIS dock may already be
        # hidden.
        self.action_dock.setChecked(self.dock_widget.isVisible())

        self.iface.initializationCompleted.connect(
            partial(self.show_welcome_message))

    def _add_spacer_to_menu(self):
        """Create a spacer to the menu to separate action groups."""
        separator = QAction(self.iface.mainWindow())
        separator.setSeparator(True)
        self.iface.addPluginToMenu(self.tr('InaSAFE'), separator)

    @staticmethod
    def clear_modules():
        """Unload inasafe functions and try to return QGIS to before InaSAFE.

        .. todo:: I think this function can be removed. TS.
        """
        # next lets force remove any inasafe related modules
        modules = []
        for module in sys.modules:
            if 'inasafe' in module:
                # Check if it is really one of our modules i.e. exists in the
                # plugin directory
                tokens = module.split('.')
                path = ''
                for myToken in tokens:
                    path += os.path.sep + myToken
                parent = os.path.abspath(
                    os.path.join(__file__, os.path.pardir, os.path.pardir))
                full_path = os.path.join(parent, path + '.py')
                if os.path.exists(os.path.abspath(full_path)):
                    LOGGER.debug('Removing: %s' % module)
                    modules.append(module)
        for module in modules:
            del (sys.modules[module])
        for module in sys.modules:
            if 'inasafe' in module:
                print(module)

        # Lets also clean up all the path additions that were made
        package_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__), os.path.pardir))
        LOGGER.debug('Path to remove: %s' % package_path)
        # We use a list comprehension to ensure duplicate entries are removed
        LOGGER.debug(sys.path)
        sys.path = [y for y in sys.path if package_path not in y]
        LOGGER.debug(sys.path)

    def unload(self):
        """GUI breakdown procedure (for QGIS plugin api).

        .. note:: Don't change the name of this method from unload!

        This method is called by QGIS and should be used to *remove*
        any graphical user interface elements that should appear in QGIS.
        """
        # Remove the plugin menu item and icon
        if self.wizard:
            self.wizard.deleteLater()
        for myAction in self.actions:
            self.iface.removePluginMenu(self.tr('InaSAFE'), myAction)
            self.iface.removeToolBarIcon(myAction)
            self.iface.removeCustomActionForLayerType(myAction)
        self.iface.mainWindow().removeDockWidget(self.dock_widget)
        self.iface.mainWindow().removeToolBar(self.toolbar)
        self.dock_widget.setVisible(False)
        self.dock_widget.destroy()
        self.iface.currentLayerChanged.disconnect(self.layer_changed)

        # Unload QGIS expressions loaded by the plugin.
        for qgis_expression in list(qgis_expressions().keys()):
            QgsExpression.unregisterFunction(qgis_expression)

    def toggle_inasafe_action(self, checked):
        """Check or un-check the toggle inaSAFE toolbar button.

        This slot is called when the user hides the inaSAFE panel using its
        close button or using view->panels.

        :param checked: True if the dock should be shown, otherwise False.
        :type checked: bool
        """
        self.action_dock.setChecked(checked)

    # Run method that performs all the real work
    def toggle_dock_visibility(self):
        """Show or hide the dock widget."""
        if self.dock_widget.isVisible():
            self.dock_widget.setVisible(False)
        else:
            self.dock_widget.setVisible(True)
            self.dock_widget.raise_()

    def add_test_layers(self):
        """Add standard test layers."""
        from safe.test.utilities import load_standard_layers
        load_standard_layers()
        rect = QgsRectangle(106.806, -6.195, 106.837, -6.167)
        self.iface.mapCanvas().setExtent(rect)

    def select_test_package(self):
        """Select the test package."""
        default_package = 'safe'
        user_package = str(
            setting('testPackage', default_package, expected_type=str))

        test_package, _ = QInputDialog.getText(
            self.iface.mainWindow(), self.tr('Select the python test package'),
            self.tr('Select the python test package'), QLineEdit.Normal,
            user_package)

        if test_package == '':
            test_package = default_package

        set_setting('testPackage', test_package)
        msg = self.tr('Run tests in %s' % test_package)
        self.action_run_tests.setWhatsThis(msg)
        self.action_run_tests.setText(msg)

    def run_tests(self):
        """Run unit tests in the python console."""
        from qgis.PyQt.QtWidgets import QDockWidget
        main_window = self.iface.mainWindow()
        action = main_window.findChild(QAction, 'mActionShowPythonDialog')
        action.trigger()
        package = str(setting('testPackage', 'safe', expected_type=str))
        for child in main_window.findChildren(QDockWidget, 'PythonConsole'):
            if child.objectName() == 'PythonConsole':
                child.show()
                for widget in child.children():
                    if 'PythonConsoleWidget' in str(widget.__class__):
                        # print "Console widget found"
                        shell = widget.shell
                        shell.runCommand(
                            'from inasafe.test_suite import test_package')
                        shell.runCommand('test_package(\'%s\')' % package)
                        break

    def show_extent_selector(self):
        """Show the extent selector widget for defining analysis extents."""
        # import here only so that it is AFTER i18n set up
        from safe.gui.tools.extent_selector_dialog import ExtentSelectorDialog
        widget = ExtentSelectorDialog(
            self.iface,
            self.iface.mainWindow(),
            extent=self.dock_widget.extent.user_extent,
            crs=self.dock_widget.extent.crs)
        widget.clear_extent.connect(
            self.dock_widget.extent.clear_user_analysis_extent)
        widget.extent_defined.connect(
            self.dock_widget.define_user_analysis_extent)
        # This ensures that run button state is updated on dialog close
        widget.extent_selector_closed.connect(
            self.dock_widget.validate_impact_function)
        # Needs to be non modal to support hide -> interact with map -> show
        widget.show()  # non modal

    def show_minimum_needs(self):
        """Show the minimum needs dialog."""
        # import here only so that it is AFTER i18n set up
        from safe.gui.tools.minimum_needs.needs_calculator_dialog import (
            NeedsCalculatorDialog)

        dialog = NeedsCalculatorDialog(self.iface.mainWindow())
        dialog.exec_()

    def show_minimum_needs_configuration(self):
        """Show the minimum needs dialog."""
        # import here only so that it is AFTER i18n set up
        from safe.gui.tools.minimum_needs.needs_manager_dialog import (
            NeedsManagerDialog)

        dialog = NeedsManagerDialog(parent=self.iface.mainWindow(),
                                    dock=self.dock_widget)
        dialog.exec_()  # modal

    def show_options(self):
        """Show the options dialog."""
        # import here only so that it is AFTER i18n set up
        from safe.gui.tools.options_dialog import OptionsDialog

        dialog = OptionsDialog(iface=self.iface,
                               parent=self.iface.mainWindow())
        dialog.show_option_dialog()
        if dialog.exec_():  # modal
            self.dock_widget.read_settings()
            from safe.gui.widgets.message import getting_started_message
            send_static_message(self.dock_widget, getting_started_message())
            # Issue #4734, make sure to update the combobox after update the
            # InaSAFE option
            self.dock_widget.get_layers()

    def show_welcome_message(self):
        """Show the welcome message."""
        # import here only so that it is AFTER i18n set up
        from safe.gui.tools.options_dialog import OptionsDialog

        # Do not show by default
        show_message = False

        previous_version = StrictVersion(setting('previous_version'))
        current_version = StrictVersion(inasafe_version)

        # Set previous_version to the current inasafe_version
        set_setting('previous_version', inasafe_version)

        if setting('always_show_welcome_message', expected_type=bool):
            # Show if it the setting said so
            show_message = True
        elif previous_version < current_version:
            # Always show if the user installed new version
            show_message = True

        # Allow to disable welcome message when running automated tests
        if os.environ.get('INASAFE_DISABLE_WELCOME_MESSAGE', False):
            show_message = False

        if show_message:
            dialog = OptionsDialog(iface=self.iface,
                                   parent=self.iface.mainWindow())
            dialog.show_welcome_dialog()
            if dialog.exec_():  # modal
                self.dock_widget.read_settings()

    def show_keywords_wizard(self):
        """Show the keywords creation wizard."""
        # import here only so that it is AFTER i18n set up
        from safe.gui.tools.wizard.wizard_dialog import WizardDialog

        if self.iface.activeLayer() is None:
            return

        # Don't break an existing wizard session if accidentally clicked
        if self.wizard and self.wizard.isVisible():
            return

        # Prevent spawning multiple copies since the IFCW is non modal
        if not self.wizard:
            self.wizard = WizardDialog(self.iface.mainWindow(), self.iface,
                                       self.dock_widget)
        self.wizard.set_keywords_creation_mode()
        self.wizard.exec_()  # modal

    def show_function_centric_wizard(self):
        """Show the function centric wizard."""
        # import here only so that it is AFTER i18n set up
        from safe.gui.tools.wizard.wizard_dialog import WizardDialog

        # Don't break an existing wizard session if accidentally clicked
        if self.wizard and self.wizard.isVisible():
            return

        # Prevent spawning multiple copies since it is non modal
        if not self.wizard:
            self.wizard = WizardDialog(self.iface.mainWindow(), self.iface,
                                       self.dock_widget)
        self.wizard.set_function_centric_mode()
        # non-modal in order to hide for selecting user extent
        self.wizard.show()

    def show_shakemap_importer(self):
        """Show the converter dialog."""
        # import here only so that it is AFTER i18n set up
        from safe.gui.tools.shake_grid.shakemap_converter_dialog import (
            ShakemapConverterDialog)

        dialog = ShakemapConverterDialog(self.iface.mainWindow(), self.iface,
                                         self.dock_widget)
        dialog.exec_()  # modal

    def show_multi_buffer(self):
        """Show the multi buffer tool."""
        from safe.gui.tools.multi_buffer_dialog import (MultiBufferDialog)

        dialog = MultiBufferDialog(self.iface.mainWindow(), self.iface,
                                   self.dock_widget)
        dialog.exec_()  # modal

    def show_osm_downloader(self):
        """Show the OSM buildings downloader dialog."""
        from safe.gui.tools.osm_downloader_dialog import OsmDownloaderDialog

        dialog = OsmDownloaderDialog(self.iface.mainWindow(), self.iface)
        # otherwise dialog is never deleted
        dialog.setAttribute(Qt.WA_DeleteOnClose, True)
        dialog.show()  # non modal

    def show_geonode_uploader(self):
        """Show the Geonode uploader dialog."""
        from safe.gui.tools.geonode_uploader import GeonodeUploaderDialog

        dialog = GeonodeUploaderDialog(self.iface.mainWindow())
        dialog.show()  # non modal

    def add_osm_layer(self):
        """Add OSM tile layer to the map.

        This uses a gdal wrapper around the OSM tile service - see the
        WorldOSM.gdal file for how it is constructed.
        """
        path = resources_path('osm', 'WorldOSM.gdal')
        layer = QgsRasterLayer(path, self.tr('OpenStreetMap'))
        project = QgsProject.instance()

        # Try to add it as the last layer in the list
        # False flag prevents layer being added to legend
        project.addMapLayer(layer, False)
        root = QgsProject.instance().layerTreeRoot()
        index = len(root.findLayers()) + 1
        # LOGGER.info('Inserting layer %s at position %s' % (
        #    layer.source(), index))
        root.insertLayer(index, layer)
        project.addMapLayer(layer)

    def show_definitions(self):
        """Show InaSAFE Definitions (a report showing all key metadata)."""
        from safe.utilities.help import show_help
        from safe.gui.tools.help import definitions_help
        show_help(definitions_help.definitions_help())

    def show_field_mapping(self):
        """Show InaSAFE Field Mapping."""
        from safe.gui.tools.field_mapping_dialog import FieldMappingDialog
        dialog = FieldMappingDialog(
            parent=self.iface.mainWindow(),
            iface=self.iface,
        )
        if dialog.exec_():  # modal
            LOGGER.debug('Show field mapping accepted')
            self.dock_widget.layer_changed(self.iface.activeLayer())
        else:
            LOGGER.debug('Show field mapping not accepted')

    def show_metadata_converter(self):
        """Show InaSAFE Metadata Converter."""
        from safe.gui.tools.metadata_converter_dialog import (
            MetadataConverterDialog)
        dialog = MetadataConverterDialog(
            parent=self.iface.mainWindow(),
            iface=self.iface,
        )
        dialog.exec_()

    def show_multi_exposure(self):
        """Show InaSAFE Multi Exposure."""
        from safe.gui.tools.multi_exposure_dialog import MultiExposureDialog
        dialog = MultiExposureDialog(self.iface.mainWindow(), self.iface)
        dialog.exec_()  # modal

    def add_petabencana_layer(self):
        """Add petabencana layer to the map.

        This uses the PetaBencana API to fetch the latest floods in JK. See
        https://data.petabencana.id/floods
        """
        from safe.gui.tools.peta_bencana_dialog import PetaBencanaDialog
        dialog = PetaBencanaDialog(self.iface.mainWindow(), self.iface)
        dialog.show()  # non modal

    def show_batch_runner(self):
        """Show the batch runner dialog."""
        from safe.gui.tools.batch.batch_dialog import BatchDialog

        dialog = BatchDialog(parent=self.iface.mainWindow(),
                             iface=self.iface,
                             dock=self.dock_widget)
        dialog.exec_()  # modal

    def save_scenario(self):
        """Save current scenario to text file."""
        from safe.gui.tools.save_scenario import SaveScenarioDialog

        dialog = SaveScenarioDialog(iface=self.iface, dock=self.dock_widget)
        dialog.save_scenario()

    def layer_changed(self, layer):
        """Enable or disable keywords editor icon when active layer changes.

        :param layer: The layer that is now active.
        :type layer: QgsMapLayer
        """
        if not layer:
            enable_keyword_wizard = False
        elif not hasattr(layer, 'providerType'):
            enable_keyword_wizard = False
        elif layer.providerType() == 'wms':
            enable_keyword_wizard = False
        else:
            enable_keyword_wizard = True

        try:
            if layer:
                if is_raster_layer(layer):
                    enable_field_mapping_tool = False
                else:
                    keywords = KeywordIO().read_keywords(layer)
                    keywords_version = keywords.get('keyword_version')
                    if not keywords_version:
                        supported = False
                    else:
                        supported = (
                            is_keyword_version_supported(keywords_version))
                    if not supported:
                        enable_field_mapping_tool = False
                    else:
                        layer_purpose = keywords.get('layer_purpose')

                        if not layer_purpose:
                            enable_field_mapping_tool = False
                        else:
                            if layer_purpose == layer_purpose_exposure['key']:
                                layer_subcategory = keywords.get('exposure')
                            elif layer_purpose == layer_purpose_hazard['key']:
                                layer_subcategory = keywords.get('hazard')
                            else:
                                layer_subcategory = None
                            field_groups = get_field_groups(
                                layer_purpose, layer_subcategory)
                            if len(field_groups) == 0:
                                # No field group, disable field mapping tool.
                                enable_field_mapping_tool = False
                            else:
                                enable_field_mapping_tool = True
            else:
                enable_field_mapping_tool = False
        except (KeywordNotFoundError, NoKeywordsFoundError, MetadataReadError):
            # No keywords, disable field mapping tool.
            enable_field_mapping_tool = False

        self.action_keywords_wizard.setEnabled(enable_keyword_wizard)
        self.action_field_mapping.setEnabled(enable_field_mapping_tool)

    def shortcut_f7(self):
        """Executed when user press F7 - will show the shakemap importer."""
        self.show_shakemap_importer()
    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):
        """Add a toolbar icon to the InaSAFE toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToVectorMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action
Example #13
0
class XYZHubConnector(object):
    """base plugin"""
    def __init__(self, iface):
        """init"""
        import sys
        print(sys.version)
        self.iface = iface
        self.web_menu = "&XYZ Hub Connector"
        self.init_modules()
        self.obj = self

    def initGui(self):
        """startup"""

        parent = self.iface.mainWindow()

        ######## action, button

        icon = QIcon("%s/%s" % (config.PLUGIN_DIR, "images/xyz.png"))
        icon_bbox = QIcon("%s/%s" % (config.PLUGIN_DIR, "images/bbox.svg"))
        self.action_connect = QAction(icon, "New XYZ Hub Connection", parent)
        self.action_connect.setWhatsThis(
            QCoreApplication.translate(PLUGIN_NAME, "WhatsThis message"))
        self.action_connect.setStatusTip(
            QCoreApplication.translate(PLUGIN_NAME, "status tip message"))

        self.action_clear_cache = QAction("Clear cache", parent)
        self.action_upload = QAction("Upload to New XYZ Geospace", parent)
        self.action_basemap = QAction("Add HERE Map Tile", parent)

        self.action_magic_sync = QAction("Magic Sync (EXPERIMENTAL)", parent)
        self.action_manage = QAction("Manage XYZ Geospace (EXPERIMENTAL)",
                                     parent)
        self.action_edit = QAction("Edit/Delete XYZ Geospace (EXPERIMENTAL)",
                                   parent)

        if self.iface.activeLayer() is None:
            # self.action_upload.setEnabled(False)
            self.action_edit.setEnabled(False)
            self.action_magic_sync.setEnabled(False)

        # self.action_magic_sync.setVisible(False) # disable magic sync

        ######## CONNECT action, button

        self.action_connect.triggered.connect(self.open_connection_dialog)
        self.action_manage.triggered.connect(self.open_manage_dialog)
        self.action_edit.triggered.connect(self.open_edit_dialog)
        self.action_upload.triggered.connect(self.open_upload_dialog)
        self.action_magic_sync.triggered.connect(self.open_magic_sync_dialog)
        self.action_clear_cache.triggered.connect(self.open_clear_cache_dialog)
        self.action_basemap.triggered.connect(self.open_basemap_dialog)

        ######## Add the toolbar + button
        self.toolbar = self.iface.addToolBar(PLUGIN_NAME)
        self.toolbar.setObjectName("XYZ Hub Connector")

        tool_btn = QToolButton(self.toolbar)

        self.actions = [
            self.action_connect, self.action_upload, self.action_basemap,
            self.action_clear_cache
        ]  # , self.action_magic_sync, self.action_manage, self.action_edit
        for a in self.actions:
            tool_btn.addAction(a)
            self.iface.addPluginToWebMenu(self.web_menu, a)

        tool_btn.setDefaultAction(self.action_connect)
        tool_btn.setPopupMode(tool_btn.MenuButtonPopup)

        self.xyz_widget_action = self.toolbar.addWidget(tool_btn)

        self.action_help = None

        self.action_reload = QAction(icon_bbox, "Reload BBox", parent)
        self.action_reload.triggered.connect(self.layer_reload_bbox)
        self.action_reload.setVisible(False)  # disable
        self.toolbar.addAction(self.action_reload)

        progress = QProgressBar()
        progress.setMinimum(0)
        progress.setMaximum(0)
        progress.reset()
        progress.hide()
        # progress = self.iface.statusBarIface().children()[2] # will be hidden by qgis
        self.iface.statusBarIface().addPermanentWidget(progress)
        self.pb = progress

    def init_modules(self):
        # util.init_module()

        # parent = self.iface.mainWindow()
        parent = QgsProject.instance()

        ######## Init xyz modules
        self.map_basemap_meta = basemap.load_default_xml()
        self.auth_manager = AuthManager(config.PLUGIN_DIR + "/auth.ini")

        self.token_model = GroupTokenModel(parent)
        # self.layer = LayerManager(parent, self.iface)

        self.network = NetManager(parent)

        self.con_man = ControllerManager()
        self.layer_man = LayerManager()

        ######## data flow
        self.conn_info = SpaceConnectionInfo()

        ######## token
        print(config.PLUGIN_DIR)
        self.token_model.load_ini(config.PLUGIN_DIR + "/token.ini")

        ######## CALLBACK
        # self.iface.mapCanvas().extentsChanged.connect( self.debug_reload)
        # self.con_man.connect_ux( self.iface) # canvas ux
        # self.con_man.signal.canvas_span.connect( self.loader_reload_bbox)

        self.con_man.ld_pool.signal.progress.connect(
            self.cb_progress_busy)  #, Qt.QueuedConnection
        self.con_man.ld_pool.signal.finished.connect(self.cb_progress_done)

        QgsProject.instance().layersWillBeRemoved["QStringList"].connect(
            self.layer_man.remove)
        QgsProject.instance().layersWillBeRemoved["QStringList"].connect(
            self.con_man.remove)

        # self.iface.currentLayerChanged.connect( self.cb_layer_selected) # UNCOMMENT

        if DEBUG:
            QgsApplication.messageLog().messageReceived.connect(print_qgis)

    def unload_modules(self):
        # self.con_man.disconnect_ux( self.iface)
        QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect(
            self.layer_man.remove)
        QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect(
            self.con_man.remove)

        # utils.disconnect_silent(self.iface.currentLayerChanged)

        # self.con_man.unload()
        # del self.con_man

        # self.iface.mapCanvas().extentsChanged.disconnect( self.debug_reload)

        close_print_qgis()
        pass

    def unload(self):
        """teardown"""
        self.unload_modules()
        # remove the plugin menu item and icon
        self.iface.removePluginWebMenu(self.web_menu, self.action_help)

        self.toolbar.clear(
        )  # remove action from custom toolbar (toolbar still exist)
        self.toolbar.deleteLater()

        for a in self.actions:
            self.iface.removePluginWebMenu(self.web_menu, a)

    ###############
    # Callback
    ###############
    def cb_layer_selected(self, qlayer):
        flag_xyz = True if qlayer is not None and self.layer.is_xyz_supported_layer(
            qlayer) else False
        # disable magic sync
        # self.action_magic_sync.setEnabled(flag_xyz)
        flag_layer = True
        self.action_upload.setEnabled(flag_layer)
        self.action_edit.setEnabled(flag_layer)

    ###############
    # Callback of action (main function)
    ###############
    def cb_success_msg(self, msg, info=""):
        self.iface.messageBar().pushMessage(msg, info, Qgis.Success, 1)

    def make_cb_success(self, msg, info=""):
        def _cb_success_msg():
            txt = info
            self.cb_success_msg(msg, txt)

        return _cb_success_msg

    def cb_handle_error_msg(self, e):
        err = parse_exception_obj(e)
        if isinstance(err, ChainInterrupt):
            e0, idx = err.args[0:2]
            if isinstance(e0, net_handler.NetworkError):
                ok = self.show_net_err_dialog(e0)
                if ok: return
            elif isinstance(e0, loader.EmptyXYZSpaceError):
                ret = exec_warning_dialog(
                    "Warning", "Requested query returns no features")
        self.show_err_msgbar(err)

    def show_net_err_dialog(self, err):
        assert isinstance(err, net_handler.NetworkError)
        reply_tag, status, reason, body = err.args[:4]
        if reply_tag in ["count"]:  # too many error
            return 0

        msg = ("%s: %s\n" % (status, reason) +
               "There was a problem connecting to the server")
        if status == 403:
            msg += "\n\n" + "Please make sure that the token has WRITE permission"
        ret = exec_warning_dialog("Network Error", msg, body)
        return 1

    def show_err_msgbar(self, err):
        self.iface.messageBar().pushMessage(TAG_PLUGIN, repr(err),
                                            Qgis.Warning, 5)
        msg = format_traceback(err)
        QgsMessageLog.logMessage(msg, TAG_PLUGIN, Qgis.Warning)

    def cb_progress_busy(self, n_active):
        if n_active > 1: return
        self.flag_pb_show = True
        self.cb_progress_refresh()

    def cb_progress_done(self):
        self.flag_pb_show = False
        self.cb_progress_refresh()

    def cb_progress_refresh(self):
        if not hasattr(self, "flag_pb_show"): return

        pb = self.pb
        if self.flag_pb_show:
            pb.show()
            print_qgis("show", pb)
        else:
            pb.hide()
            print_qgis("hide")

    ###############
    # Action (main function)
    ###############
    def load_bbox(self, con, args):
        bbox = bbox_utils.extend_to_bbox(
            bbox_utils.get_bounding_box(self.iface))
        a, kw = parse_qt_args(args)
        kw["bbox"] = bbox
        kw["limit"] = 1000
        con.start(*a, **kw)

    def layer_reload_bbox(self):
        con_bbox_reload = ReloadLayerController_bbox(self.network)
        self.con_man.add(con_bbox_reload)
        # con_bbox_reload.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
        con_bbox_reload.signal.finished.connect(
            self.make_cb_success("Bounding box loading finish"))
        con_bbox_reload.signal.error.connect(self.cb_handle_error_msg)

        # TODO: set/get params from vlayer
        layer_id = self.iface.activeLayer().id()
        layer = self.layer_man.get(layer_id)
        self.load_bbox(con_bbox_reload, make_qt_args(layer))

    # UNUSED
    def debug_reload(self):
        print("debug_reload")

    def refresh_canvas(self):
        self.iface.mapCanvas().refresh()
        # assert False # debug unload module

    def previous_canvas_extent(self):
        self.iface.mapCanvas().zoomToPreviousExtent()

    def open_clear_cache_dialog(self):
        parent = self.iface.mainWindow()
        dialog = ConfirmDialog(
            parent, "Delete cache will make loaded layer unusable !!")
        ret = dialog.exec_()
        if ret != dialog.Ok: return

        utils.clear_cache()

    def open_connection_dialog(self):
        parent = self.iface.mainWindow()
        dialog = ConnectManageSpaceDialog(parent)
        dialog.config(self.token_model, self.conn_info)

        ############ edit btn

        con = EditSpaceController(self.network)
        self.con_man.add(con)
        con.signal.finished.connect(dialog.btn_use.clicked.emit)
        con.signal.error.connect(self.cb_handle_error_msg)
        dialog.signal_edit_space.connect(con.start_args)

        ############ delete btn

        con = DeleteSpaceController(self.network)
        self.con_man.add(con)
        con.signal.results.connect(dialog.btn_use.clicked.emit)
        con.signal.error.connect(self.cb_handle_error_msg)
        dialog.signal_del_space.connect(con.start_args)

        ############ Use Token btn

        con = LoadSpaceController(self.network)
        self.con_man.add(con)
        con.signal.results.connect(make_fun_args(dialog.cb_display_spaces))
        con.signal.error.connect(self.cb_handle_error_msg)
        con.signal.error.connect(lambda e: dialog.cb_enable_token_ui())
        con.signal.finished.connect(dialog.cb_enable_token_ui)
        dialog.signal_use_token.connect(con.start_args)

        ############ get statisitics
        con = StatSpaceController(self.network)
        self.con_man.add(con)
        con.signal.results.connect(make_fun_args(
            dialog.cb_display_space_count))
        con.signal.error.connect(self.cb_handle_error_msg)
        dialog.signal_space_count.connect(con.start_args)

        ############ TODO: bbox btn

        ############ connect btn
        con_load = loader.ReloadLayerController(self.network, n_parallel=2)
        self.con_man.add(con_load)
        # con_load.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
        con_load.signal.finished.connect(
            self.make_cb_success("Loading finish"))
        con_load.signal.error.connect(self.cb_handle_error_msg)

        dialog.signal_space_connect.connect(con_load.start_args)

        # con.signal.results.connect( self.layer_man.add_args) # IMPORTANT

        dialog.exec_()
        # self.startTime = time.time()

    def open_manage_dialog(self):
        pass

    def open_edit_dialog(self):
        pass

    def open_upload_dialog(self):
        vlayer = self.iface.activeLayer()
        parent = self.iface.mainWindow()
        dialog = UploadNewSpaceDialog(parent)
        dialog.config(self.token_model, self.network, vlayer)

        ############ Use Token btn
        con = LoadSpaceController(self.network)
        self.con_man.add(con)
        con.signal.results.connect(make_fun_args(
            dialog.cb_set_valid_token))  # finished signal !?
        con.signal.error.connect(self.cb_handle_error_msg)
        con.signal.finished.connect(dialog.cb_enable_token_ui)
        dialog.signal_use_token.connect(con.start_args)

        con_upload = UploadLayerController(self.network, n_parallel=2)
        self.con_man.add(con_upload)
        con_upload.signal.finished.connect(
            self.make_cb_success("Uploading finish"))
        con_upload.signal.error.connect(self.cb_handle_error_msg)

        con = InitUploadLayerController(self.network)
        self.con_man.add(con)

        dialog.signal_upload_new_space.connect(con.start_args)
        con.signal.results.connect(con_upload.start_args)
        con.signal.error.connect(self.cb_handle_error_msg)

        dialog.exec_()

    def open_magic_sync_dialog(self):
        pass

    def open_basemap_dialog(self):
        parent = self.iface.mainWindow()
        auth = self.auth_manager.get_auth()
        dialog = BaseMapDialog(parent)
        dialog.config(self.map_basemap_meta, auth)
        dialog.signal_add_basemap.connect(self.add_basemap_layer)

        dialog.exec_()

    def add_basemap_layer(self, args):
        a, kw = parse_qt_args(args)
        meta, app_id, app_code = a
        self.auth_manager.save(app_id, app_code)
        basemap.add_basemap_layer(meta, app_id, app_code)
Example #14
0
class GdalTools:

    def __init__(self, iface):
        if not valid:
            return

        # Save reference to the QGIS interface
        self.iface = iface
        try:
            self.QgisVersion = unicode(Qgis.QGIS_VERSION_INT)
        except:
            self.QgisVersion = unicode(Qgis.qgisVersion)[0]

        if Qgis.QGIS_VERSION[0:3] < "1.5":
            # For i18n support
            userPluginPath = qgis.utils.home_plugin_path + "/GdalTools"
            systemPluginPath = qgis.utils.sys_plugin_path + "/GdalTools"

            overrideLocale = QSettings().value("locale/overrideFlag", False, type=bool)
            if not overrideLocale:
                localeFullName = QLocale.system().name()
            else:
                localeFullName = QSettings().value("locale/userLocale", "", type=str)

            if QFileInfo(userPluginPath).exists():
                translationPath = userPluginPath + "/i18n/GdalTools_" + localeFullName + ".qm"
            else:
                translationPath = systemPluginPath + "/i18n/GdalTools_" + localeFullName + ".qm"

            self.localePath = translationPath
            if QFileInfo(self.localePath).exists():
                self.translator = QTranslator()
                self.translator.load(self.localePath)
                QCoreApplication.installTranslator(self.translator)

        # The list of actions added to menus, so we can remove them when unloading the plugin
        self._menuActions = []

    def initGui(self):
        if not valid:
            return
        if int(self.QgisVersion) < 1:
            QMessageBox.warning(
                self.iface.getMainWindow(), "Gdal Tools",
                QCoreApplication.translate("GdalTools", "QGIS version detected: ") + unicode(self.QgisVersion) + ".xx\n"
                + QCoreApplication.translate("GdalTools", "This version of Gdal Tools requires at least QGIS version 1.0.0\nPlugin will not be enabled."))
            return None

        from .tools.GdalTools_utils import GdalConfig, LayerRegistry
        self.GdalVersionNum = GdalConfig.versionNum()
        LayerRegistry.setIface(self.iface)

        # find the Raster menu
        rasterMenu = None
        menu_bar = self.iface.mainWindow().menuBar()
        actions = menu_bar.actions()

        rasterText = QCoreApplication.translate("QgisApp", "&Raster")

        for a in actions:
            if a.menu() is not None and a.menu().title() == rasterText:
                rasterMenu = a.menu()
                break

        if rasterMenu is None:
            # no Raster menu, create and insert it before the Help menu
            self.menu = QMenu(rasterText, self.iface.mainWindow())
            lastAction = actions[len(actions) - 1]
            menu_bar.insertMenu(lastAction, self.menu)
        else:
            self.menu = rasterMenu
            self._menuActions.append(self.menu.addSeparator())

        # projections menu (Warp (Reproject), Assign projection)
        self.projectionsMenu = QMenu(QCoreApplication.translate("GdalTools", "Projections"), self.iface.mainWindow())
        self.projectionsMenu.setObjectName("projectionsMenu")

        self.warp = QAction(QIcon(":/icons/warp.png"), QCoreApplication.translate("GdalTools", "Warp (Reproject)..."), self.iface.mainWindow())
        self.warp.setObjectName("warp")
        self.warp.setStatusTip(QCoreApplication.translate("GdalTools", "Warp an image into a new coordinate system"))
        self.warp.triggered.connect(self.doWarp)

        self.projection = QAction(QIcon(":icons/projection-add.png"), QCoreApplication.translate("GdalTools", "Assign Projection..."), self.iface.mainWindow())
        self.projection.setObjectName("projection")
        self.projection.setStatusTip(QCoreApplication.translate("GdalTools", "Add projection info to the raster"))
        self.projection.triggered.connect(self.doProjection)

        self.extractProj = QAction(QIcon(":icons/projection-export.png"), QCoreApplication.translate("GdalTools", "Extract Projection..."), self.iface.mainWindow())
        self.extractProj.setObjectName("extractProj")
        self.extractProj.setStatusTip(QCoreApplication.translate("GdalTools", "Extract projection information from raster(s)"))
        self.extractProj.triggered.connect(self.doExtractProj)

        self.projectionsMenu.addActions([self.warp, self.projection, self.extractProj])

        # conversion menu (Rasterize (Vector to raster), Polygonize (Raster to vector), Translate, RGB to PCT, PCT to RGB)
        self.conversionMenu = QMenu(QCoreApplication.translate("GdalTools", "Conversion"), self.iface.mainWindow())
        self.conversionMenu.setObjectName("conversionMenu")

        if self.GdalVersionNum >= 1300:
            self.rasterize = QAction(QIcon(":/icons/rasterize.png"), QCoreApplication.translate("GdalTools", "Rasterize (Vector to Raster)..."), self.iface.mainWindow())
            self.rasterize.setObjectName("rasterize")
            self.rasterize.setStatusTip(QCoreApplication.translate("GdalTools", "Burns vector geometries into a raster"))
            self.rasterize.triggered.connect(self.doRasterize)
            self.conversionMenu.addAction(self.rasterize)

        if self.GdalVersionNum >= 1600:
            self.polygonize = QAction(QIcon(":/icons/polygonize.png"), QCoreApplication.translate("GdalTools", "Polygonize (Raster to Vector)..."), self.iface.mainWindow())
            self.polygonize.setObjectName("polygonize")
            self.polygonize.setStatusTip(QCoreApplication.translate("GdalTools", "Produces a polygon feature layer from a raster"))
            self.polygonize.triggered.connect(self.doPolygonize)
            self.conversionMenu.addAction(self.polygonize)

        self.translate = QAction(QIcon(":/icons/translate.png"), QCoreApplication.translate("GdalTools", "Translate (Convert Format)..."), self.iface.mainWindow())
        self.translate.setObjectName("translate")
        self.translate.setStatusTip(QCoreApplication.translate("GdalTools", "Converts raster data between different formats"))
        self.translate.triggered.connect(self.doTranslate)

        self.paletted = QAction(QIcon(":icons/24-to-8-bits.png"), QCoreApplication.translate("GdalTools", "RGB to PCT..."), self.iface.mainWindow())
        self.paletted.setObjectName("paletted")
        self.paletted.setStatusTip(QCoreApplication.translate("GdalTools", "Convert a 24bit RGB image to 8bit paletted"))
        self.paletted.triggered.connect(self.doPaletted)

        self.rgb = QAction(QIcon(":icons/8-to-24-bits.png"), QCoreApplication.translate("GdalTools", "PCT to RGB..."), self.iface.mainWindow())
        self.rgb.setObjectName("rgb")
        self.rgb.setStatusTip(QCoreApplication.translate("GdalTools", "Convert an 8bit paletted image to 24bit RGB"))
        self.rgb.triggered.connect(self.doRGB)

        self.conversionMenu.addActions([self.translate, self.paletted, self.rgb])

        # extraction menu (Clipper, Contour)
        self.extractionMenu = QMenu(QCoreApplication.translate("GdalTools", "Extraction"), self.iface.mainWindow())
        self.extractionMenu.setObjectName("extractionMenu")

        if self.GdalVersionNum >= 1600:
            self.contour = QAction(QIcon(":/icons/contour.png"), QCoreApplication.translate("GdalTools", "Contour..."), self.iface.mainWindow())
            self.contour.setObjectName("contour")
            self.contour.setStatusTip(QCoreApplication.translate("GdalTools", "Builds vector contour lines from a DEM"))
            self.contour.triggered.connect(self.doContour)
            self.extractionMenu.addAction(self.contour)

        self.clipper = QAction(QIcon(":icons/raster-clip.png"), QCoreApplication.translate("GdalTools", "Clipper..."), self.iface.mainWindow())
        self.clipper.setObjectName("clipper")
        #self.clipper.setStatusTip( QCoreApplication.translate( "GdalTools", "Converts raster data between different formats") )
        self.clipper.triggered.connect(self.doClipper)

        self.extractionMenu.addActions([self.clipper])

        # analysis menu (DEM (Terrain model), Grid (Interpolation), Near black, Proximity (Raster distance), Sieve)
        self.analysisMenu = QMenu(QCoreApplication.translate("GdalTools", "Analysis"), self.iface.mainWindow())
        self.analysisMenu.setObjectName("analysisMenu")

        if self.GdalVersionNum >= 1600:
            self.sieve = QAction(QIcon(":/icons/sieve.png"), QCoreApplication.translate("GdalTools", "Sieve..."), self.iface.mainWindow())
            self.sieve.setObjectName("sieve")
            self.sieve.setStatusTip(QCoreApplication.translate("GdalTools", "Removes small raster polygons"))
            self.sieve.triggered.connect(self.doSieve)
            self.analysisMenu.addAction(self.sieve)

        if self.GdalVersionNum >= 1500:
            self.nearBlack = QAction(QIcon(":/icons/nearblack.png"), QCoreApplication.translate("GdalTools", "Near Black..."), self.iface.mainWindow())
            self.nearBlack.setObjectName("nearBlack")
            self.nearBlack.setStatusTip(QCoreApplication.translate("GdalTools", "Convert nearly black/white borders to exact value"))
            self.nearBlack.triggered.connect(self.doNearBlack)
            self.analysisMenu.addAction(self.nearBlack)

        if self.GdalVersionNum >= 1700:
            self.fillNodata = QAction(QIcon(":/icons/fillnodata.png"), QCoreApplication.translate("GdalTools", "Fill nodata..."), self.iface.mainWindow())
            self.fillNodata.setObjectName("fillNodata")
            self.fillNodata.setStatusTip(QCoreApplication.translate("GdalTools", "Fill raster regions by interpolation from edges"))
            self.fillNodata.triggered.connect(self.doFillNodata)
            self.analysisMenu.addAction(self.fillNodata)

        if self.GdalVersionNum >= 1600:
            self.proximity = QAction(QIcon(":/icons/proximity.png"), QCoreApplication.translate("GdalTools", "Proximity (Raster Distance)..."), self.iface.mainWindow())
            self.proximity.setObjectName("proximity")
            self.proximity.setStatusTip(QCoreApplication.translate("GdalTools", "Produces a raster proximity map"))
            self.proximity.triggered.connect(self.doProximity)
            self.analysisMenu.addAction(self.proximity)

        if self.GdalVersionNum >= 1500:
            self.grid = QAction(QIcon(":/icons/grid.png"), QCoreApplication.translate("GdalTools", "Grid (Interpolation)..."), self.iface.mainWindow())
            self.grid.setObjectName("grid")
            self.grid.setStatusTip(QCoreApplication.translate("GdalTools", "Create raster from the scattered data"))
            self.grid.triggered.connect(self.doGrid)
            self.analysisMenu.addAction(self.grid)

        if self.GdalVersionNum >= 1700:
            self.dem = QAction(QIcon(":icons/dem.png"), QCoreApplication.translate("GdalTools", "DEM (Terrain Models)..."), self.iface.mainWindow())
            self.dem.setObjectName("dem")
            self.dem.setStatusTip(QCoreApplication.translate("GdalTools", "Tool to analyze and visualize DEMs"))
            self.dem.triggered.connect(self.doDEM)
            self.analysisMenu.addAction(self.dem)

        #self.analysisMenu.addActions( [  ] )

        # miscellaneous menu (Build overviews (Pyramids), Tile index, Information, Merge, Build Virtual Raster (Catalog))
        self.miscellaneousMenu = QMenu(QCoreApplication.translate("GdalTools", "Miscellaneous"), self.iface.mainWindow())
        self.miscellaneousMenu.setObjectName("miscellaneousMenu")

        if self.GdalVersionNum >= 1600:
            self.buildVRT = QAction(QIcon(":/icons/vrt.png"), QCoreApplication.translate("GdalTools", "Build Virtual Raster (Catalog)..."), self.iface.mainWindow())
            self.buildVRT.setObjectName("buildVRT")
            self.buildVRT.setStatusTip(QCoreApplication.translate("GdalTools", "Builds a VRT from a list of datasets"))
            self.buildVRT.triggered.connect(self.doBuildVRT)
            self.miscellaneousMenu.addAction(self.buildVRT)

        self.merge = QAction(QIcon(":/icons/merge.png"), QCoreApplication.translate("GdalTools", "Merge..."), self.iface.mainWindow())
        self.merge.setObjectName("merge")
        self.merge.setStatusTip(QCoreApplication.translate("GdalTools", "Build a quick mosaic from a set of images"))
        self.merge.triggered.connect(self.doMerge)

        self.info = QAction(QIcon(":/icons/raster-info.png"), QCoreApplication.translate("GdalTools", "Information..."), self.iface.mainWindow())
        self.info.setObjectName("info")
        self.info.setStatusTip(QCoreApplication.translate("GdalTools", "Lists information about raster dataset"))
        self.info.triggered.connect(self.doInfo)

        self.overview = QAction(QIcon(":icons/raster-overview.png"), QCoreApplication.translate("GdalTools", "Build Overviews (Pyramids)..."), self.iface.mainWindow())
        self.overview.setObjectName("overview")
        self.overview.setStatusTip(QCoreApplication.translate("GdalTools", "Builds or rebuilds overview images"))
        self.overview.triggered.connect(self.doOverview)

        self.tileindex = QAction(QIcon(":icons/tiles.png"), QCoreApplication.translate("GdalTools", "Tile Index..."), self.iface.mainWindow())
        self.tileindex.setObjectName("tileindex")
        self.tileindex.setStatusTip(QCoreApplication.translate("GdalTools", "Build a shapefile as a raster tileindex"))
        self.tileindex.triggered.connect(self.doTileIndex)

        self.miscellaneousMenu.addActions([self.merge, self.info, self.overview, self.tileindex])

        self._menuActions.append(self.menu.addMenu(self.projectionsMenu))
        self._menuActions.append(self.menu.addMenu(self.conversionMenu))
        self._menuActions.append(self.menu.addMenu(self.extractionMenu))

        if not self.analysisMenu.isEmpty():
            self._menuActions.append(self.menu.addMenu(self.analysisMenu))

        self._menuActions.append(self.menu.addMenu(self.miscellaneousMenu))

        self.settings = QAction(QCoreApplication.translate("GdalTools", "GdalTools Settings..."), self.iface.mainWindow())
        self.settings.setObjectName("settings")
        self.settings.setStatusTip(QCoreApplication.translate("GdalTools", "Various settings for Gdal Tools"))
        self.settings.triggered.connect(self.doSettings)
        self.menu.addAction(self.settings)
        self._menuActions.append(self.settings)

    def unload(self):
        if not valid:
            return
        for a in self._menuActions:
            self.menu.removeAction(a)

    def doBuildVRT(self):
        from .tools.doBuildVRT import GdalToolsDialog as BuildVRT
        d = BuildVRT(self.iface)
        self.runToolDialog(d)

    def doContour(self):
        from .tools.doContour import GdalToolsDialog as Contour
        d = Contour(self.iface)
        self.runToolDialog(d)

    def doRasterize(self):
        from .tools.doRasterize import GdalToolsDialog as Rasterize
        d = Rasterize(self.iface)
        self.runToolDialog(d)

    def doPolygonize(self):
        from .tools.doPolygonize import GdalToolsDialog as Polygonize
        d = Polygonize(self.iface)
        self.runToolDialog(d)

    def doMerge(self):
        from .tools.doMerge import GdalToolsDialog as Merge
        d = Merge(self.iface)
        self.runToolDialog(d)

    def doSieve(self):
        from .tools.doSieve import GdalToolsDialog as Sieve
        d = Sieve(self.iface)
        self.runToolDialog(d)

    def doProximity(self):
        from .tools.doProximity import GdalToolsDialog as Proximity
        d = Proximity(self.iface)
        self.runToolDialog(d)

    def doNearBlack(self):
        from .tools.doNearBlack import GdalToolsDialog as NearBlack
        d = NearBlack(self.iface)
        self.runToolDialog(d)

    def doFillNodata(self):
        from .tools.doFillNodata import GdalToolsDialog as FillNodata
        d = FillNodata(self.iface)
        self.runToolDialog(d)

    def doWarp(self):
        from .tools.doWarp import GdalToolsDialog as Warp
        d = Warp(self.iface)
        self.runToolDialog(d)

    def doGrid(self):
        from .tools.doGrid import GdalToolsDialog as Grid
        d = Grid(self.iface)
        self.runToolDialog(d)

    def doTranslate(self):
        from .tools.doTranslate import GdalToolsDialog as Translate
        d = Translate(self.iface)
        self.runToolDialog(d)

    def doInfo(self):
        from .tools.doInfo import GdalToolsDialog as Info
        d = Info(self.iface)
        self.runToolDialog(d)

    def doProjection(self):
        from .tools.doProjection import GdalToolsDialog as Projection
        d = Projection(self.iface)
        self.runToolDialog(d)

    def doOverview(self):
        from .tools.doOverview import GdalToolsDialog as Overview
        d = Overview(self.iface)
        self.runToolDialog(d)

    def doClipper(self):
        from .tools.doClipper import GdalToolsDialog as Clipper
        d = Clipper(self.iface)
        self.runToolDialog(d)

    def doPaletted(self):
        from .tools.doRgbPct import GdalToolsDialog as RgbPct
        d = RgbPct(self.iface)
        self.runToolDialog(d)

    def doRGB(self):
        from .tools.doPctRgb import GdalToolsDialog as PctRgb
        d = PctRgb(self.iface)
        self.runToolDialog(d)

    def doTileIndex(self):
        from .tools.doTileIndex import GdalToolsDialog as TileIndex
        d = TileIndex(self.iface)
        self.runToolDialog(d)

    def doExtractProj(self):
        from .tools.doExtractProj import GdalToolsDialog as ExtractProj
        d = ExtractProj(self.iface)
        d.exec_()

    def doDEM(self):
        from .tools.doDEM import GdalToolsDialog as DEM
        d = DEM(self.iface)
        self.runToolDialog(d)

    def runToolDialog(self, dlg):
        dlg.show_()
        dlg.exec_()
        del dlg

    def doSettings(self):
        from .tools.doSettings import GdalToolsSettingsDialog as Settings
        d = Settings(self.iface)
        d.exec_()
class Cbers4aDownloader:
    """Plugin implementation
    """
    def __init__(self, iface):
        """
        Constructor

        Args:
            iface (qgis.gui.QgisInterface): a reference to the QGIS GUI interface
        """
        self.iface = iface
        self.plugin_dir = os.path.dirname(__file__)
        self.dockwidget = None
        self.provider = None
        self.action = None
        self.name = 'CBERS4A Downloader'
        self.about = 'Search and download CBERS4A imagery'
        self.search = None
        self.collections = None
        self.result = None
        self.footprint = QgsRubberBand(iface.mapCanvas(), True)
        self.mux_grid = None
        self.wfi_grid = None
        self.downloading = False
        self.outputfolder = QgsProject.instance().absolutePath() or gettempdir(
        )

    def initProcessing(self):
        self.provider = Provider()
        QgsApplication.processingRegistry().addProvider(self.provider)

    def initGui(self):
        """
        This method is called by QGIS when the main GUI starts up or 
        when the plugin is enabled in the Plugin Manager.
        Only want to register the menu items and toolbar buttons here,
        and connects action with run method.
        """

        self.initProcessing()

        icon = QIcon(':/plugins/cbers4a/cbers4a.svg')
        self.action = QAction(icon, self.name, self.iface.mainWindow())
        self.action.setWhatsThis(self.about)
        self.action.setStatusTip(self.about)
        self.action.setCheckable(True)
        self.action.triggered.connect(self.run)

        # for raster menu/toolbar
        # self.iface.addRasterToolBarIcon(self.action)
        # self.iface.addPluginToRasterMenu(self.name, self.action)

        # for plugin menu/toolbar
        self.iface.addToolBarIcon(self.action)
        self.iface.addPluginToMenu(self.name, self.action)

        # for custom toolbar
        # self.toolbar = self.iface.addToolBar('Plugin')
        # self.toolbar.setObjectName('Plugin')
        # self.toolbar.addAction(self.action)

    def unload(self):
        """
        Will be executed when the plugin is disabled. 
        Either in the Plugin Manager or when QGIS shuts down.
        It removes the previously defined QAction object from
        the menu and remove the plugin icon from the toolbar.
        """

        # disconnect triggers
        if self.search is not None:
            self.iface.mapCanvas().extentsChanged.disconnect(
                self.update_extent)
            self.iface.mapCanvas().destinationCrsChanged.disconnect(
                self.update_extent)
            self.dockwidget.startdate.dateChanged.disconnect(
                self.update_startdate)
            self.dockwidget.enddate.dateChanged.disconnect(self.update_enddate)
            self.iface.projectRead.disconnect(self.update_outputfolder)
            self.iface.newProjectCreated.disconnect(self.update_outputfolder)
            QgsProject.instance().projectSaved.disconnect(
                self.update_outputfolder)
            self.dockwidget.limit.valueChanged.disconnect(self.update_limit)
            self.dockwidget.cloudcover.valueChanged.disconnect(
                self.update_cloudcover)
            self.dockwidget.collection.currentIndexChanged.disconnect(
                self.update_collection)
            self.dockwidget.searchButton.clicked.disconnect(self.do_search)
            self.dockwidget.resultsList.itemClicked.disconnect(self.preview)
            # self.dockwidget.credential.textChanged[str].disconnect(self.credential_changed)
            # self.dockwidget.credential.editingFinished.disconnect(self.credential_filled)
            self.dockwidget.tabs.currentChanged[int].disconnect(
                self.toggle_download_button)
            self.dockwidget.downloadButton.clicked.disconnect(
                self.start_download)
            # footprint pushbutton
            self.dockwidget.footprint.clicked.disconnect(self.toggle_footprint)

            # close search session
            self.search.close()

        # for raster menu/toolbar
        # self.iface.removePluginRasterMenu(self.name, self.action)
        # self.iface.removeRasterToolBarIcon(self.action)

        # for plugin menu/toolbar
        self.iface.removeToolBarIcon(self.action)
        self.iface.removePluginMenu(self.name, self.action)

        # remove canvas item (footprint)
        self.iface.mapCanvas().scene().removeItem(self.footprint)

        if self.dockwidget is not None:
            self.dockwidget.close()
            self.dockwidget.visibilityChanged.disconnect(
                self.visibility_changed)
            self.iface.removeDockWidget(self.dockwidget)
            del self.dockwidget

        # remove custom toolbar
        # if self.toolbar is not None:
        # del self.toolbar

        # remove processing provider
        QgsApplication.processingRegistry().removeProvider(self.provider)

        # clean up task
        try:
            del globals()['cbers4a_tasks']
        except KeyError:
            pass

    def run(self):
        """
        Executes the custom plugin functionality.
        This method is called by the previously defined QAction object (callback), 
        which went into the toolbar icon and the menu entry.
        """
        if self.dockwidget is None:
            self.dockwidget = DockCbers4aDownloader()
            self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget)
            self.dockwidget.visibilityChanged.connect(self.visibility_changed)
            self.init()
            self.dockwidget.show()
        else:
            if self.dockwidget.isVisible():
                self.dockwidget.footprint.setChecked(False)
                self.footprint.hide()
                self.dockwidget.hide()
            else:
                self.dockwidget.show()

    def visibility_changed(self, change):
        """
        Change icon checked status with dockwidget visibility
        """
        self.action.setChecked(change)

    def get_canvas_extent(self):
        """
        Return a tuple with current extent and crs
        """
        crs = self.iface.mapCanvas().mapSettings().destinationCrs()
        extent = self.iface.mapCanvas().extent()
        return extent, crs

    def update_extent(self):
        """
        Update extent from mapcanvas
        """
        current_extent, current_crs = self.get_canvas_extent()
        output_crs = QgsCoordinateReferenceSystem('EPSG:4326')  # WGS84
        transform = QgsCoordinateTransform(current_crs, output_crs,
                                           QgsProject.instance())
        extent = transform.transformBoundingBox(current_extent)
        self.dockwidget.north.setText('{0:.6f}'.format(extent.yMaximum()))
        self.dockwidget.south.setText('{0:.6f}'.format(extent.yMinimum()))
        self.dockwidget.east.setText('{0:.6f}'.format(extent.xMaximum()))
        self.dockwidget.west.setText('{0:.6f}'.format(extent.xMinimum()))
        self.search.bbox([
            extent.xMinimum(),
            extent.yMinimum(),
            extent.xMaximum(),
            extent.yMaximum()
        ])
        # also update footprint
        self.update_footprint()

    def update_startdate(self, new_date):
        defaults = QgsSettings()
        self.dockwidget.enddate.setDate(
            new_date.addDays(int(defaults.value("cbers4a/timespan", 30))))
        self.search.date(
            self.dockwidget.startdate.date().toString('yyyy-MM-dd'),
            self.dockwidget.enddate.date().toString('yyyy-MM-dd'))

    def update_enddate(self, end_date):
        if end_date <= self.dockwidget.startdate.date():
            self.dockwidget.startdate.setDate(end_date.addDays(-1))
        self.search.date(
            self.dockwidget.startdate.date().toString('yyyy-MM-dd'),
            self.dockwidget.enddate.date().toString('yyyy-MM-dd'))

    def update_outputfolder(self):
        """
        Update default output folder on new/read project
        """
        self.outputfolder = QgsProject.instance().absolutePath() or gettempdir(
        )
        self.dockwidget.outputfolder.lineEdit().setPlaceholderText(
            self.outputfolder)

    def update_limit(self):
        """
        Set max number of scenes in the search
        """
        self.search.limit(self.dockwidget.limit.value())

    def update_cloudcover(self):
        """
        Set max cloud cover in the search
        """
        self.search.cloud_cover('<=', self.dockwidget.cloudcover.value())

    def update_collection(self):
        """
        Set collection to search in and valid start and end dates for this collection
        """
        collection_id = self.dockwidget.collection.currentText()
        inpe_collections = {
            description: id
            for id, description in self.collections
        }

        # set collection to search
        self.search.collections([inpe_collections[collection_id]])

        # set valid minimum dates
        mindate = self.collections.get_temporal_extent(
            inpe_collections[collection_id])[0].split('-')
        year, month, day = int(mindate[0]), int(mindate[1]), int(mindate[2])
        self.dockwidget.startdate.setMinimumDate(QDate(year, month, day))
        self.dockwidget.enddate.setMinimumDate(QDate(year, month, day))

        # set valid maximum dates
        self.dockwidget.startdate.setMaximumDate(QDate.currentDate())
        self.dockwidget.enddate.setMaximumDate(QDate.currentDate())

    def do_search(self):
        """
        Run search in INPE STAC Catalog
        """
        self.result = self.search()
        self.dockwidget.resultsList.clear()
        # add items to result list
        # self.dockwidget.resultsList.addItems([str(item) for item in self.result])
        icon = QIcon(":/images/themes/default/mIconRaster.svg")
        for item in self.result:
            list_item = QListWidgetItem(icon, str(item.id),
                                        self.dockwidget.resultsList)
            self.dockwidget.resultsList.addItem(list_item)
        if self.result.complete:
            self.dockwidget.resultsMsg.setText('Returned {0} scenes.'.format(
                self.result.returned))
        else:
            self.dockwidget.resultsMsg.setText(
                'Returned {0} scenes from {1} matched.'.format(
                    self.result.returned, self.result.matched))
        self.dockwidget.tabs.setCurrentIndex(1)
        self.clear_preview()

    def clear_preview(self):
        """
        Clear preview tab
        """
        self.dockwidget.webView.setHtml('')
        self.dockwidget.item_id.clear()
        self.dockwidget.item_date.clear()
        self.dockwidget.item_cloudcover.clear()
        self.dockwidget.footprint.setChecked(False)
        self.footprint.hide()
        self.dockwidget.footprint.setDisabled(True)
        self.dockwidget.assetsList.clear()
        self.dockwidget.assetsList.setDefaultText('')

    def preview(self, item):
        """
        Preview selected scene
        """
        self.clear_preview()
        item_id = item.text()
        self.dockwidget.webView.setHtml(self.result[item_id].html)
        self.dockwidget.item_id.setText(item_id)
        self.dockwidget.item_date.setText('{0}'.format(
            self.result[item_id].date))
        self.dockwidget.item_cloudcover.setText('{0:.1f} %'.format(
            self.result[item_id].cloud_cover))
        self.update_footprint()
        self.dockwidget.footprint.setEnabled(True)
        self.dockwidget.assetsList.setDefaultText('ALL')
        self.dockwidget.assetsList.addItems(self.result[item_id].assets)
        self.dockwidget.tabs.setCurrentIndex(2)

    def toggle_download_button(self, tab):
        """
        Enable/disable download button and hide footprint on tab change
        """
        if tab == 2:
            item_id = self.dockwidget.item_id.text()
            credential = self.dockwidget.credential.text()
            if item_id and credential:
                self.dockwidget.downloadButton.setEnabled(True)
            elif self.downloading:
                self.dockwidget.downloadButton.setEnabled(True)
            else:
                self.dockwidget.downloadButton.setDisabled(True)
        else:
            self.dockwidget.footprint.setChecked(False)
            self.footprint.hide()

        # update valid maximum dates on tab change
        self.dockwidget.startdate.setMaximumDate(QDate.currentDate())
        self.dockwidget.enddate.setMaximumDate(QDate.currentDate())

    def start_download(self):
        """
        Start/cancel task to download item asset(s)
        """
        if self.downloading:
            globals()['cbers4a_tasks'].cancel()
        else:
            item_id = self.dockwidget.item_id.text()
            sel_assets = self.dockwidget.assetsList.checkedItems()
            if sel_assets:
                assets = [asset for asset in sel_assets]
            else:
                assets = self.result[item_id].assets

            #
            # create the task variable with global scope
            # workaround for QGIS Issue #28531
            # https://gis.stackexchange.com/questions/296175/issues-with-qgstask-and-task-manager/304801#304801
            #

            globals()['cbers4a_tasks'] = QgsTask.fromFunction(
                'Download {0}'.format(item_id),
                self.download_asset,
                on_finished=self.download_finished,
                item_id=item_id,
                assets=assets)

            # self.info('Starting task to download {} asset(s)'.format(item_id), duration=1)
            QgsApplication.taskManager().addTask(globals()['cbers4a_tasks'])

            # lock downloading
            self.downloading = True
            self.dockwidget.options.setDisabled(True)
            self.dockwidget.results.setDisabled(True)
            self.dockwidget.downloadButton.setText('Cancel Download')
            self.dockwidget.downloadButton.setStyleSheet(
                'background-color: #ff3336')
            self.dockwidget.assetsList.setDisabled(True)

    def info(self, msg, level=Qgis.Info, duration=5):
        """
        docstring
        """
        self.iface.messageBar().pushMessage(msg, level, duration)

    def log(self, msg, level=Qgis.Info):
        """
        docstring
        """
        QgsMessageLog.logMessage(msg, self.name, level)

    def download_asset(self, task, item_id, assets):
        """
        Task function to download asset(s) for a given item id
        """
        self.log('Started task {}'.format(task.description()))
        credential = self.dockwidget.credential.text()
        collection = self.result[item_id].collection
        outdir = self.dockwidget.outputfolder.lineEdit().text(
        ) or self.outputfolder
        session = self.search.session
        filenames = {}
        for asset in assets:
            url = self.result[item_id].url(asset)
            filename = url.split("/")[-1]
            outfile = os.path.join(outdir, filename)
            r = session.get(url,
                            params={
                                'email': credential,
                                'item_id': item_id,
                                'collection': collection
                            },
                            stream=True,
                            allow_redirects=True)
            if r.status_code == 200:
                total_size = int(r.headers.get('content-length'))
                size = 0
                with open(outfile + '.part', 'wb') as f:
                    for ch in r.iter_content(chunk_size=4096):  # page size 4kb
                        if ch:
                            f.write(ch)
                            size = size + len(ch)
                            progress = (size / total_size) * 100
                            # use task.setProgress to report progress
                            task.setProgress(progress)
                            # check task.isCanceled() to handle cancellation
                            if task.isCanceled():
                                self.log('Task "{name}" was canceled'.format(
                                    name=task.description()))
                                return None
                os.rename(outfile + '.part', outfile)
                self.log(
                    f'Task {task.description()}:\nDownloaded file: {outfile} ({size} of {total_size} bytes)'
                )
                filenames[asset] = outfile
            else:
                raise Exception('ERROR in ' + url + ' (' + r.reason + ')')

        return {
            'description': item_id,
            'filenames': filenames,
            'outdir': outdir
        }

    def download_finished(self, exception, result=None):
        """
        This is called when download_asset is finished.
        Exception is not None if download_asset raises an exception.
        result is the return value of download_asset.
        """

        # VERY SLOW TO DO HERE
        # def __pszXML(nbands):
        #     specband = '\n'.join([f"""<SpectralBand dstBand="{n+1}">\n</SpectralBand>""" for n in range(nbands)])
        #     return f"""<VRTDataset subClass="VRTPansharpenedDataset">
        # <PansharpeningOptions>
        # {specband}
        # </PansharpeningOptions>
        # </VRTDataset>"""

        def __add_images(images,
                         description,
                         outdir,
                         msgbar,
                         check,
                         vrt=True,
                         shown=True):
            if check.isChecked() and vrt:
                pan = images.pop('pan', None)  # take out pan
                if len(images) > 1:
                    # stack images in a VRT file if there are more than one image left
                    vrtfile = os.path.join(outdir, description + '.vrt')
                    # to sort in r/g/b/nir order here sort(images.keys, key=lambda k: rgbn[k])
                    _RGBN = {'red': 1, 'green': 2, 'blue': 3, 'nir': 4}
                    images_keys_ordered = sorted(images.keys(),
                                                 key=lambda k: _RGBN.get(k, 5))
                    filenames = [images[k] for k in images_keys_ordered
                                 ]  # filenames list in RGBN order
                    ds = gdal.BuildVRT(
                        vrtfile,
                        filenames,
                        options=gdal.BuildVRTOptions(separate=True,
                                                     resolution='highest'))
                    for i, bn in enumerate(images_keys_ordered):
                        b = ds.GetRasterBand(i + 1)
                        b.SetDescription(bn)
                    ds.FlushCache()
                    ds = None
                    if pan is None:
                        # nothing to do anymore, add VRT file
                        __add_images({'vrt': vrtfile},
                                     description,
                                     outdir,
                                     msgbar,
                                     check,
                                     vrt=False)
                    else:
                        # VERY SLOW TO DO HERE (perhaps as ProcessingProvider?)
                        # nbands = len(images)
                        # if nbands > 2: # may be a setting here for pansharpening choice
                        #     pan_ds = gdal.Open(pan)
                        #     pansharpened_ds = gdal.CreatePansharpenedVRT(__pszXML(nbands), pan_ds.GetRasterBand(1),
                        #     [ds.GetRasterBand(i + 1) for i in range(nbands)])
                        #     pansharpened_filename = os.path.join(outdir, description + '_pansharpened.vrt')
                        #     driver = gdal.GetDriverByName('VRT')
                        #     driver.CreateCopy(pansharpened_filename, pansharpened_ds, 0)
                        #     # add ALL
                        #     __add_images({'pansharpened': pansharpened_filename,
                        #                 'pan': pan, 'vrt': vrtfile}, description, outdir, msgbar, check, vrt=False, shown=False)
                        # else:
                        #     # just add pan and vrt files
                        __add_images({
                            'pan': pan,
                            'vrt': vrtfile
                        },
                                     description,
                                     outdir,
                                     msgbar,
                                     check,
                                     vrt=False)
                else:
                    # do not stack since there are not enough images
                    assert pan is not None  # 'pan' should be not 'None' here
                    images.update({'pan': pan})  # restore pan
                    __add_images(images,
                                 description,
                                 outdir,
                                 msgbar,
                                 check,
                                 vrt=False)
            else:
                for k in images.keys():
                    fn = images[k]
                    name = os.path.basename(fn).split('.')[0]
                    layer = QgsRasterLayer(fn, name)
                    if layer.isValid():
                        QgsProject.instance().addMapLayer(layer)
                        if not shown:
                            QgsProject.instance().layerTreeRoot().findLayer(
                                layer.id()).setItemVisibilityChecked(shown)
                msgbar.dismiss()

        # unlock another downloading
        self.downloading = False
        self.dockwidget.options.setEnabled(True)
        self.dockwidget.results.setEnabled(True)
        self.dockwidget.downloadButton.setText('Download')
        self.dockwidget.downloadButton.setStyleSheet(
            'background-color: None')  # restore default background color
        self.dockwidget.assetsList.setEnabled(True)

        if exception is None:
            if result is None:
                self.log(
                    'Finished with no exception and no result (probably manually canceled by the user)',
                    level=Qgis.Warning)
            else:
                assets = result['filenames'].keys()
                images = {
                    asset: result['filenames'][asset]
                    for asset in assets
                    if not asset.lower().endswith(('_xml', 'thumbnail'))
                }
                if images:
                    # ask to add image(s) to canvas
                    widget = self.iface.messageBar().createMessage(
                        f"Download {result['description']} finished")
                    button = QPushButton(widget)
                    button.setText('Add image(s)')
                    widget.layout().addWidget(button)
                    check = QCheckBox(widget)
                    check.setText('Stack in VRT file')
                    widget.layout().addWidget(check)
                    if len(images) < 2:
                        check.setDisabled(True)
                    button.clicked.connect(
                        lambda: __add_images(images, result['description'],
                                             result['outdir'], widget, check))
                    self.iface.messageBar().pushWidget(widget, Qgis.Info)
                else:
                    self.info(f"Download {result['description']} finished")
        else:
            self.log("Exception: {}".format(exception), level=Qgis.Critical)
            raise exception

    def toggle_footprint(self):
        """
        Toggle footprint image view on canvas
        """
        if self.dockwidget.footprint.isChecked():
            self.footprint.show()
        else:
            self.footprint.hide()

    def update_footprint(self):
        """
        Update image footprint (image extent)
        """
        item_id = self.dockwidget.item_id.text()
        if item_id and self.result is not None:
            try:
                if self.result[item_id].sensor == 'WFI':
                    geojson = self.wfi_grid[self.result[item_id].path_row]
                else:
                    geojson = self.mux_grid[
                        self.result[item_id].path_row]  # MUX and WPAN
                qgis_geom = QgsGeometry.fromPolygonXY([[
                    QgsPointXY(pt[0], pt[1])
                    for pt in geojson['coordinates'][0]
                ]])
            except KeyError:  # use bbox
                xmin, ymin, xmax, ymax = self.result[item_id].bbox
                qgis_geom = QgsGeometry.fromRect(
                    QgsRectangle(xmin, ymin, xmax, ymax))

            self.footprint.setToGeometry(
                qgis_geom, QgsCoordinateReferenceSystem('EPSG:4326'))
            defaults = QgsSettings()
            self.footprint.setStrokeColor(
                defaults.value("cbers4a/colorstroke", QColor(255, 255, 82)))
            self.footprint.setWidth(3)
            self.toggle_footprint()

    def load_grids(self):
        """
        Load CBERS4A MUX and WFI grids
        """
        # json files
        json_mux = os.path.join(self.plugin_dir, 'grid/cbers4a_mux.json')
        json_wfi = os.path.join(self.plugin_dir, 'grid/cbers4a_wfi.json')

        # read mux grid
        with open(json_mux) as mux:
            self.mux_grid = json.load(mux)

        # read wfi grid
        with open(json_wfi) as wfi:
            self.wfi_grid = json.load(wfi)

    def defaults_settings(self):
        """
        Init plugin settings defaults:
            - cbers4a/colorstroke
            - cbers4a/cloudcover
            - cbers4a/credential
            - cbers4a/timespan
            - cbers4a/limit
        """
        s = QgsSettings()
        if not s.contains("cbers4a/colorstroke"):
            s.setValue("cbers4a/colorstroke", QColor(255, 255, 82))
            s.setValue("cbers4a/cloudcover", 10)
            s.setValue("cbers4a/credential", '')
            s.setValue("cbers4a/timespan", 30)
            s.setValue("cbers4a/limit", 100)

    def init(self):
        """
        Fill options, default values and connect triggers of the dockwidget
        """
        # init settings
        self.defaults_settings()

        # init Search instance
        self.search = Search()

        # get collections
        self.collections = self.search.get_collections()
        inpe_collections = {
            description: id
            for id, description in self.collections
        }

        # load grids
        self.load_grids()

        # connect triggers
        self.iface.mapCanvas().extentsChanged.connect(self.update_extent)
        self.iface.mapCanvas().destinationCrsChanged.connect(
            self.update_extent)
        self.dockwidget.startdate.dateChanged.connect(self.update_startdate)
        self.dockwidget.enddate.dateChanged.connect(self.update_enddate)
        self.iface.projectRead.connect(self.update_outputfolder)
        self.iface.newProjectCreated.connect(self.update_outputfolder)
        QgsProject.instance().projectSaved.connect(self.update_outputfolder)
        self.dockwidget.limit.valueChanged.connect(self.update_limit)
        self.dockwidget.cloudcover.valueChanged.connect(self.update_cloudcover)
        self.dockwidget.collection.currentIndexChanged.connect(
            self.update_collection)
        self.dockwidget.searchButton.clicked.connect(self.do_search)
        self.dockwidget.resultsList.itemClicked.connect(self.preview)
        # self.dockwidget.credential.textChanged[str].connect(self.credential_changed)
        # self.dockwidget.credential.editingFinished.connect(self.credential_filled)
        self.dockwidget.tabs.currentChanged[int].connect(
            self.toggle_download_button)
        self.dockwidget.downloadButton.clicked.connect(self.start_download)
        self.dockwidget.footprint.clicked.connect(self.toggle_footprint)

        # fill collections combobox
        self.dockwidget.collection.addItems(inpe_collections.keys())

        # set defaults
        defaults = QgsSettings()
        self.dockwidget.cloudcover.setValue(
            int(defaults.value("cbers4a/cloudcover", 10)))
        self.dockwidget.limit.setValue(
            int(defaults.value("cbers4a/limit", 100)))
        self.dockwidget.startdate.setDate(QDate.currentDate().addDays(
            -int(defaults.value("cbers4a/timespan", 30))))
        self.dockwidget.credential.setText(
            defaults.value("cbers4a/credential", ''))
        self.update_extent()
        self.update_outputfolder()
        # disable download button
        # self.dockwidget.downloadButton.setDisabled(True)
        # validate credential (seems email)
        # self.dockwidget.credential.clear()
        # validator = QRegExpValidator(QRegExp("[^@]+@[^@]+\.[^@]+"))
        # self.dockwidget.credential.setValidator(validator)

        # disable footprint pushbutton
        self.dockwidget.footprint.setIcon(
            QIcon(':/images/themes/default/mActionShowSelectedLayers.svg'))
        self.dockwidget.footprint.setCheckable(True)
        self.dockwidget.footprint.setDisabled(True)

        self.dockwidget.downloadButton.setText('Download')
        self.dockwidget.assetsList.setDefaultText('')
        # start in first tab
        self.dockwidget.tabs.setCurrentIndex(0)
Example #16
0
    def add_action(self,
                   name,
                   icon_path,
                   text,
                   callback,
                   toggle_flag=False,
                   enabled_flag=True,
                   checkable_flag=False,
                   visible_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):
        """Add a toolbar icon to the toolbar.
        :param name: Objectname of the action. Serves also as key for the stored actions.
        :type name: str

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param toggle_flag: A flag indicating if the action should connect
            the toggled or triggered signal by default.
            Defaults to triggered (False)
        :type toggle_flag: bool

        :param checkable_flag: A flag indicating if the action should be checkable
            by default. Defaults to False.
        :type checkable: bool

        :param visible_flag: A flag indicating if the action should be displayed
            by default. Defaults to True.
        :type visible: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.setObjectName(name)
        if toggle_flag:
            action.toggled.connect(callback)
        else:
            action.triggered.connect(callback)
        action.setEnabled(enabled_flag)
        action.setCheckable(checkable_flag)
        action.setVisible(visible_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions[name] = action

        return action
Example #17
0
class MainPlugin(object):
    def __init__(self, iface):
        self.name = "groupLayers"
        self.iface = iface
        self.project = QgsProject.instance()
        self.treeBeforeSave = None

    def initGui(self):
        self.action = QAction(
            QIcon(os.path.dirname(os.path.realpath(__file__)) + "/icon.png"),
            u"Group Layers by similar type (keep visibility)",
            self.iface.mainWindow()
        )
        self.action.setObjectName("groupAction")
        self.action.setWhatsThis("Group/ungroup layers by type")
        self.action.setStatusTip("Group/ungroup layers by type")
        self.action.setCheckable(True)
        self.action.triggered.connect(self.run)

        self.resetAction = QAction("Group and make all layers visible")
        self.resetAction.triggered.connect(self.run_reset_visibility)

        # the icon pressed status could be used, but it is already
        # changed when run method is called, so this is ambiguous
        # therefore a dedicated boolean status is used
        self.grouped = False
        self.groupAdditionalTypes = False
        self.defSelection = groupHierarchies.keys().__iter__().__next__()
        self.hierarchyDefinition = groupHierarchies[self.defSelection]

        # add toolbar button and menu item
        layersDock = self.iface.mainWindow().findChild(QDockWidget, "Layers")
        self.layersToolBar = layersDock.widget().layout().itemAt(0).widget()
        assert isinstance(self.layersToolBar, QToolBar)
        self.layersToolBar.addAction(self.action)
        self.menuButton = [btn for btn in self.layersToolBar.children()
                           if isinstance(btn, QToolButton)
                           if self.action in btn.actions()][0]
        self.buttonMenu = QMenu()
        self.menuButton.setMenu(self.buttonMenu)
        self.menuButton.setPopupMode(QToolButton.MenuButtonPopup)
        self.buttonMenu.addAction(self.action)
        self.buttonMenu.addAction(self.resetAction)
        # self.iface.addToolBarIcon(self.action)
        self.iface.addPluginToMenu("&Group Layers", self.action)

        self.defSelector = QAction(
            u"Select hierarchy definitions",
            self.iface.mainWindow()
        )
        self.defSelector.setObjectName("defSelector")
        self.defSelector.triggered.connect(self.selectDefs)
        self.iface.addPluginToMenu("&Group Layers", self.defSelector)
        # connect hook to reset the plugin state
        # when a new or existing project is opened
        self.project.cleared.connect(self.reset_state)
        self.project.writeProject.connect(self.write)
        self.project.projectSaved.connect(self.saved)

    def unload(self):
        # remove the plugin menu item and icon
        self.iface.removePluginMenu("&Group Layers", self.action)
        self.iface.removePluginMenu("&Group Layers", self.defSelector)
        self.layersToolBar.removeAction(self.action)
        self.project.cleared.disconnect(self.reset_state)
        self.project.writeProject.disconnect(self.write)
        self.project.projectSaved.disconnect(self.saved)
        try:
            self.project.layerWasAdded.disconnect(self.add_layer_sync)
        except Exception:
            print('could not disconnect add_layer_sync in unload')
        try:
            self.project.layerRemoved.disconnect(self.remove_layer_sync)
        except Exception:
            print('could not disconnect remove_layer_sync in unload')
        # self.iface.removeToolBarIcon(self.action)

    def selectDefs(self):
        dialog = DefSelectDialog(self.defSelection, self.groupAdditionalTypes)
        if dialog.exec_():
            self.defSelection = dialog.comboBox.currentText()
            self.hierarchyDefinition = groupHierarchies[self.defSelection]
            self.groupAdditionalTypes = dialog.checkBox.isChecked()

    def run(self, checked=False, reset=False):
        try:
            if self.grouped:
                try:
                    self.project.layerWasAdded.disconnect(self.add_layer_sync)
                except Exception:
                    print('could not disconnect add_layer_sync')
                try:
                    self.project.layerRemoved.disconnect(self.remove_layer_sync)
                except Exception:
                    print('could not disconnect remove_layer_sync')
                self.groupToTree(reset_initial_visibility=reset)
                self.resetAction.setText("Group and make all layers visible")
            else:
                self.treeToGroup(all_visible=reset)
                self.resetAction.setText("Ungroup and restore initial (ungrouped) visibility")
                self.project.layerWasAdded.connect(self.add_layer_sync)
                self.project.layerRemoved.connect(self.remove_layer_sync)
        except Exception as e:
            raise(e)
        finally:
            # synchronize plugin state with button state in case of exceptions
            self.grouped = checked

    def run_reset_visibility(self):
        self.action.toggle()
        self.run(checked=self.action.isChecked(), reset=True)

    def reset_state(self):
        self.action.setChecked(False)
        self.grouped = False

    def write(self):
        if self.grouped:
            answer = QMessageBox.question(self.iface.mainWindow(),
                                          "Save ungrouped state",
                                          "The layers are currently grouped by the "
                                          "groupLayers plugin\n\n"
                                          "Would you like to save the initial (ungrouped) state?\n"
                                          "(save current (grouped) layer tree if answer = NO)",
                                          QMessageBox.Yes|QMessageBox.No)
            if answer == QMessageBox.Yes:
                self.treeBeforeSave = self.iface.layerTreeCanvasBridge().rootGroup().clone()
                self.groupToTree(reset_initial_visibility=True)
                self.iface.messageBar().pushMessage(
                    'CAUTION: The layer tree has been saved in its original format, '
                    'check options if you want to change this behavior.', Qgis.Info
                )

    def saved(self):
        if self.treeBeforeSave is not None:
            tempOldTree = self.oldTree
            self.oldTree = self.treeBeforeSave
            self.groupToTree(reset_initial_visibility=True)
            self.oldTree = tempOldTree
        self.treeBeforeSave = None

    def add_layer_sync(self, addedLayer):
        self.oldTree.addLayer(addedLayer)

    def remove_layer_sync(self, removedLayerId):
        removedLayer = self.oldTree.findLayer(removedLayerId)
        self.recursiveRemoveFromGroup(self.oldTree, removedLayer)

    def recursiveRemoveFromGroup(self, group, layer):
        group.removeChildNode(layer)
        for subGroup in group.findGroups():
            self.recursiveRemoveFromGroup(subGroup, layer)

    def initTreeRec(self, hierarchyDefinition, tree):
        for (k, v) in hierarchyDefinition.items():
            if "groupCriteria" in v:
                tree[k] = {}
                self.initTreeRec(v["values"], tree[k])
            else:
                tree[k] = []

    def treeToGroup(self, all_visible=True):
        self.layerDict = {}
        self.treeRoot = self.project.layerTreeRoot()
        self.initTreeRec(self.hierarchyDefinition['values'], self.layerDict)
        layerTree = self.iface.layerTreeCanvasBridge().rootGroup()
        self.oldTree = layerTree.clone()
        self.parseTreeRec(layerTree)  # into self.layerDict
        self.layerDict = self.cleanTree(self.layerDict)
        oldLen = len(layerTree.children())
        self.layerDictToTree(self.layerDict, layerTree, all_visible)

        # caution: commented instruction below removes all layers !!
        # iface.layerTreeCanvasBridge().rootGroup().clear()
        layerTree.removeChildren(0, oldLen)

    def groupToTree(self, reset_initial_visibility=True):
        self.treeRoot = self.project.layerTreeRoot()
        layerTree = self.iface.layerTreeCanvasBridge().rootGroup()
        oldLen = len(layerTree.children())
        self.insertInto(self.oldTree, layerTree, reset_initial_visibility)
        layerTree.removeChildren(0, oldLen)

    def layerDictToTree(self, layerDict, destinationGroup, all_visible):
        if isinstance(layerDict, dict):
            for (layerType, layers) in layerDict.items():
                grp = destinationGroup.addGroup(layerType)
                self.layerDictToTree(layers, grp, all_visible)
        elif isinstance(layerDict, list):
            for l in layerDict:
                isVisible = self.treeRoot.findLayer(l).isVisible()
                node = destinationGroup.addLayer(l)
                if not all_visible:
                    node.setItemVisibilityChecked(isVisible)

        else:
            raise Exception("Tree dictionary has been initialized incorrectly.")

    def insertInto(self, origin, destination, reset_initial_visibility):
        for el in origin.children():
            if QgsLayerTree.isLayer(el):
                node = destination.addLayer(el.layer())
                node.setItemVisibilityChecked(
                    self.treeRoot.findLayer(el.layer()).isVisible()
                )
            elif QgsLayerTree.isGroup(el):
                node = destination.addGroup(el.name())
                self.insertInto(el, node, reset_initial_visibility)
            if reset_initial_visibility:
                # overwrite visibility with previously saved visibility
                node.setItemVisibilityChecked(el.itemVisibilityChecked())

    def parseTreeRec(self, treeLeaf):
        for el in treeLeaf.children():
            if QgsLayerTree.isLayer(el):
                l = el.layer()
                self.sortInto(l, self.layerDict, self.hierarchyDefinition)
            elif QgsLayerTree.isGroup(el):
                self.parseTreeRec(el)

    def sortInto(self, layer, destination, definitions):
        if "groupCriteria" in definitions:
            groupValue = layer.__getattribute__(definitions["groupCriteria"])()
            itemFound = False
            for (label, criteria) in definitions["values"].items():
                if groupValue == criteria["value"]:
                    itemFound = True
                    self.sortInto(layer, destination[label], criteria)
            if not itemFound:
                if self.groupAdditionalTypes:
                    groupName = "others"
                else:
                    groupName = str(groupValue)
                try:
                    destination[groupName].append(layer)
                except KeyError:
                    destination[groupName] = [layer]
        else:
            destination.append(layer)

    def cleanTree(self, sourceTree):
        # remove all branches without end leaves
        if isinstance(sourceTree, dict):
            groupContents = {}
            for (layerType, layers) in sourceTree.items():
                groupLayers = self.cleanTree(layers)
                if groupLayers:
                    groupContents[layerType] = groupLayers
            return groupContents
        elif isinstance(sourceTree, list):
            return sourceTree
        else:
            raise Exception("Tree dictionary has been initialized incorrectly.")
Example #18
0
class PostNAS_Search:
    def __init__(self, iface):
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   'PostNAS_Search_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Create the dialog (after translation) and keep reference
        self.dlg = PostNAS_SearchDialog(iface=self.iface)
        self.conf = PostNAS_ConfDialog(iface=self.iface)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&PostNAS_Search')

        self.searchDockWidget = None
        self.searchDockWidgetArea = Qt.LeftDockWidgetArea

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('PostNAS_Search', message)

    def initGui(self):
        # Create Conf-Action and Menuentry
        self.confAction = QAction("Einstellungen", self.iface.mainWindow())
        self.confAction.setWhatsThis("Konfiguration der PostNAS-Suche")
        self.confAction.setStatusTip("Konfiguration der PostNAS-Suche")
        self.confAction.triggered.connect(self.showConf)

        if hasattr(self.iface, "addPluginToDatabaseMenu"):
            self.iface.addPluginToDatabaseMenu("&PostNAS-Suche",
                                               self.confAction)
        else:
            self.iface.addPluginToMenu("&PostNAS-Suche", self.confAction)

        self.toggleSearchAction = QAction(u"Flurstücksuche",
                                          self.iface.mainWindow())
        self.toggleSearchAction.setWhatsThis(
            u"Starten/Schliessen der Flurstücksuche")
        self.toggleSearchAction.setStatusTip(
            u"Starten/Schliessen der Flurstücksuche")
        self.toggleSearchAction.triggered.connect(self.toggleWidget)

        if hasattr(self.iface, "addPluginToDatabaseMenu"):
            self.iface.addPluginToDatabaseMenu("&PostNAS-Suche",
                                               self.toggleSearchAction)
        else:
            self.iface.addPluginToMenu("&PostNAS-Suche",
                                       self.toggleSearchAction)

        self.fulltextindex = QAction(u"Volltextindex erstellen",
                                     self.iface.mainWindow())
        self.fulltextindex.setWhatsThis(
            u"Erzeugt einen Volltextindex in der Datenbank um die Suche zu beschleunigen"
        )
        self.fulltextindex.setStatusTip(
            u"Erzeugt einen Volltextindex in der Datenbank um die Suche zu beschleunigen"
        )
        self.fulltextindex.triggered.connect(self.createFulltextindex)

        if hasattr(self.iface, "addPluginToDatabaseMenu"):
            self.iface.addPluginToDatabaseMenu("&PostNAS-Suche",
                                               self.fulltextindex)
        else:
            self.iface.addPluginToMenu("&PostNAS-Suche", self.fulltextindex)

        # Create action that will start plugin configuration
        self.action = QAction(
            QIcon(":/plugins/PostNAS_Search/search_24x24.png"),
            u"Flurstücksuche", self.iface.mainWindow())
        self.action.setCheckable(True)
        # connect the action to the run method
        self.action.triggered.connect(self.toggleWidget)

        # Add toolbar button and menu item
        self.iface.addToolBarIcon(self.action)

    def toggleWidget(self, event):
        if self.searchDockWidget == None:
            self.searchDockWidget = QDockWidget(self.iface.mainWindow())
            self.searchDockWidget.setWindowTitle(self.tr(u'Suche'))
            self.searchDockWidget.setWidget(self.dlg)
            self.searchDockWidget.closeEvent = self.toggleWidget
            self.iface.addDockWidget(self.searchDockWidgetArea,
                                     self.searchDockWidget)
            self.action.setChecked(True)
        else:
            self.searchDockWidgetArea = self.iface.mainWindow().dockWidgetArea(
                self.searchDockWidget)
            self.iface.removeDockWidget(self.searchDockWidget)
            self.searchDockWidget = None
            self.action.setChecked(False)

    def showConf(self):
        dlg = PostNAS_ConfDialog(self)
        dlg.exec_()

    def createFulltextindex(self):
        dlg = PostNAS_CreateFulltextindex(self)
        dlg.exec_()

    def unload(self):
        # Remove the Toolbar Icon
        self.iface.removeToolBarIcon(self.action)
        # Remove DockWidget
        if self.searchDockWidget != None:
            self.iface.removeDockWidget(self.searchDockWidget)

        if hasattr(self.iface, "removePluginDatabaseMenu"):
            self.iface.removePluginDatabaseMenu("&PostNAS-Suche",
                                                self.confAction)
            self.iface.removePluginDatabaseMenu("&PostNAS-Suche",
                                                self.toggleSearchAction)
            self.iface.removePluginDatabaseMenu("&PostNAS-Suche",
                                                self.fulltextindex)
        else:
            self.iface.removePluginMenu("&PostNAS-Suche", self.confAction)
            self.iface.removePluginMenu("&PostNAS-Suche",
                                        self.toggleSearchAction)
            self.iface.removePluginMenu("&PostNAS-Suche", self.fulltextindex)

        if self.confAction:
            self.confAction.deleteLater()
            self.confAction = None

        if self.toggleSearchAction:
            self.toggleSearchAction.deleteLater()
            self.toggleSearchAction = None

        if self.fulltextindex:
            self.fulltextindex.deleteLater()
            self.fulltextindex = None
Example #19
0
class XYZHubConnector(object):
    """base plugin"""
    def __init__(self, iface):
        """init"""
        import sys
        print(sys.version)
        self.iface = iface
        self.web_menu = "&XYZ Hub Connector"
        self.init_modules()
        self.obj = self

    def initGui(self):
        """startup"""

        parent = self.iface.mainWindow()

        ######## action, button

        icon = QIcon("%s/%s" % (config.PLUGIN_DIR, "images/xyz.png"))
        icon_bbox = QIcon("%s/%s" % (config.PLUGIN_DIR, "images/bbox.svg"))
        self.action_connect = QAction(icon, "XYZ Hub Connection", parent)
        self.action_connect.setWhatsThis(
            QCoreApplication.translate(PLUGIN_NAME, "WhatsThis message"))
        self.action_connect.setStatusTip(
            QCoreApplication.translate(PLUGIN_NAME, "status tip message"))

        self.action_sync_edit = QAction(
            QIcon("%s/%s" % (config.PLUGIN_DIR, "images/sync.svg")),
            "Push changes to XYZ Hub", parent)

        self.action_clear_cache = QAction(
            QIcon("%s/%s" % (config.PLUGIN_DIR, "images/delete.svg")),
            "Clear cache", parent)

        # self.action_sync_edit.setVisible(False) # disable magic sync
        self.edit_buffer.config_ui(self.enable_sync_btn)

        self.cb_layer_selected(self.iface.activeLayer())

        ######## CONNECT action, button

        self.action_connect.triggered.connect(self.open_connection_dialog)
        self.action_sync_edit.triggered.connect(self.open_sync_edit_dialog)
        self.action_clear_cache.triggered.connect(self.open_clear_cache_dialog)

        ######## Add the toolbar + button
        self.toolbar = self.iface.addToolBar(PLUGIN_NAME)
        self.toolbar.setObjectName("XYZ Hub Connector")

        self.actions_menu = [
            self.action_connect, self.action_sync_edit, self.action_clear_cache
        ]

        for a in [self.action_connect, self.action_sync_edit]:
            self.toolbar.addAction(a)

        for a in self.actions_menu:
            self.iface.addPluginToWebMenu(self.web_menu, a)

        # # uncomment to use menu button
        # tool_btn = QToolButton(self.toolbar)
        # tool_btn.setDefaultAction(self.action_connect)
        # tool_btn.setPopupMode(tool_btn.MenuButtonPopup)
        # self.xyz_widget_action = self.toolbar.addWidget(tool_btn) # uncomment to use menu button

        # self.toolbar.addAction(self.action_connect)

        self.action_help = None

        progress = QProgressBar()
        progress.setMinimum(0)
        progress.setMaximum(0)
        progress.reset()
        progress.hide()
        # progress = self.iface.statusBarIface().children()[2] # will be hidden by qgis
        self.iface.statusBarIface().addPermanentWidget(progress)
        self.pb = progress

    def init_modules(self):
        if LOG_TO_FILE:
            QgsApplication.messageLog().messageReceived.connect(
                cb_log_qgis)  #, Qt.QueuedConnection

        # util.init_module()

        # parent = self.iface.mainWindow()
        parent = QgsProject.instance()

        self.secret = Secret(config.USER_PLUGIN_DIR + "/secret.ini")
        ######## Init xyz modules
        self.map_basemap_meta = basemap.load_default_xml()
        self.auth_manager = AuthManager(config.USER_PLUGIN_DIR + "/auth.ini")

        self.token_model = GroupTokenModel(parent)

        self.network = NetManager(parent)

        self.con_man = LoaderManager()
        self.con_man.config(self.network)
        self.edit_buffer = EditBuffer()
        ######## data flow
        # self.conn_info = SpaceConnectionInfo()

        ######## token
        self.token_model.load_ini(config.USER_PLUGIN_DIR + "/token.ini")

        ######## CALLBACK

        self.con_man.ld_pool.signal.progress.connect(
            self.cb_progress_busy)  #, Qt.QueuedConnection
        self.con_man.ld_pool.signal.finished.connect(self.cb_progress_done)

        QgsProject.instance().layersWillBeRemoved["QStringList"].connect(
            self.edit_buffer.remove_layers)
        # QgsProject.instance().layersWillBeRemoved["QStringList"].connect( self.layer_man.remove_layers)

        # QgsProject.instance().layersAdded.connect( self.edit_buffer.config_connection)

        self.iface.currentLayerChanged.connect(
            self.cb_layer_selected)  # UNCOMMENT

        self.iface.mapCanvas().extentsChanged.connect(self.reload_tile,
                                                      Qt.QueuedConnection)

        QgsProject.instance().readProject.connect(self.import_project)
        self.import_project()

    def unload_modules(self):
        # self.con_man.disconnect_ux( self.iface)
        QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect(
            self.edit_buffer.remove_layers)
        # QgsProject.instance().layersWillBeRemoved["QStringList"].disconnect( self.layer_man.remove_layers)

        # QgsProject.instance().layersAdded.disconnect( self.edit_buffer.config_connection)
        self.edit_buffer.unload_connection()

        self.iface.currentLayerChanged.disconnect(
            self.cb_layer_selected)  # UNCOMMENT

        self.iface.mapCanvas().extentsChanged.disconnect(self.reload_tile)

        # utils.disconnect_silent(self.iface.currentLayerChanged)

        self.secret.deactivate()

        if LOG_TO_FILE:
            QgsApplication.messageLog().messageReceived.disconnect(cb_log_qgis)
        # close_file_logger()
        pass

    def unload(self):
        """teardown"""
        self.unload_modules()
        # remove the plugin menu item and icon
        self.iface.removePluginWebMenu(self.web_menu, self.action_help)

        self.toolbar.clear(
        )  # remove action from custom toolbar (toolbar still exist)
        self.toolbar.deleteLater()

        for a in self.actions_menu:
            self.iface.removePluginWebMenu(self.web_menu, a)
        # remove progress
        self.iface.statusBarIface().removeWidget(self.pb)

    ###############
    # Callback
    ###############
    def cb_layer_selected(self, qlayer):
        flag_xyz = True if qlayer is not None and is_xyz_supported_layer(
            qlayer) else False
        if flag_xyz:
            self.edit_buffer.config_connection([qlayer])
            self.edit_buffer.enable_ui(qlayer.id())
        else:
            msg = "No XYZHub Layer selected"
            self.enable_sync_btn(False, msg)
        # disable magic sync
        # self.action_sync_edit.setEnabled(flag_xyz)
    def enable_sync_btn(self, flag, msg=""):
        msg = msg or ("No changes detected since last push"
                      if not flag else "Push changes")
        self.action_sync_edit.setToolTip(msg)
        self.action_sync_edit.setEnabled(flag)

    ###############
    # Callback of action (main function)
    ###############
    def cb_success_msg(self, title, msg="", dt=5):
        self.iface.messageBar().pushMessage(config.TAG_PLUGIN,
                                            ": ".join([title,
                                                       msg]), Qgis.Success, dt)

    def make_cb_success(self, title, msg="", dt=5):
        def _cb_success_msg():
            self.cb_success_msg(title, msg, dt=dt)

        return _cb_success_msg

    def make_cb_success_args(self, title, msg="", dt=5):
        def _cb_success_msg(args):
            a, kw = parse_qt_args(args)
            txt = ". ".join(map(str, a))
            self.cb_success_msg(title, txt, dt=dt)

        return _cb_success_msg

    def cb_handle_error_msg(self, e):
        err = parse_exception_obj(e)
        if isinstance(err, ChainInterrupt):
            e0, idx = err.args[0:2]
        else:
            e0 = err
        if isinstance(e0,
                      (net_handler.NetworkError, net_handler.NetworkTimeout)):
            ok = self.show_net_err(e0)
            if ok: return
        elif isinstance(e0, EmptyXYZSpaceError):
            ret = exec_warning_dialog("XYZ Hub",
                                      "Requested query returns no features")
            return
        self.show_err_msgbar(err)

    def show_net_err(self, err):
        reply_tag, status, reason, body, err_str, url = err.args[:6]
        if reply_tag in ["count", "statistics"]:  # too many error
            # msg = "Network Error: %s: %s. %s"%(status, reason, err_str)
            return 1

        detail = "\n".join(["Request:", url, "", "Response:", body])
        msg = ("%s: %s\n" % (status, reason) +
               "There was a problem connecting to the server")
        if status in [401, 403]:
            msg += (
                "\n\n" + "Please input valid token with correct permissions." +
                "\n" + "Token is generated via " +
                "<a href='https://xyz.api.here.com/token-ui/'>https://xyz.api.here.com/token-ui/</a>"
            )
        ret = exec_warning_dialog("Network Error", msg, detail)
        return 1

    def show_err_msgbar(self, err):
        self.iface.messageBar().pushMessage("Error", repr(err), Qgis.Warning,
                                            3)
        msg = format_traceback(err)
        QgsMessageLog.logMessage(msg, config.TAG_PLUGIN, Qgis.Warning)

    def cb_progress_busy(self, n_active):
        if n_active > 1: return
        self.flag_pb_show = True
        self.cb_progress_refresh()

    def cb_progress_done(self):
        self.flag_pb_show = False
        self.cb_progress_refresh()

    def cb_progress_refresh(self):
        if not hasattr(self, "flag_pb_show"): return

        pb = self.pb
        if self.flag_pb_show:
            pb.show()
            # print_qgis("show",pb)
        else:
            pb.hide()
            # print_qgis("hide")

    ###############
    # Action (main function)
    ###############

    # UNUSED
    def refresh_canvas(self):
        # self.iface.activeLayer().triggerRepaint()
        self.iface.mapCanvas().refresh()

    def previous_canvas_extent(self):
        self.iface.mapCanvas().zoomToPreviousExtent()

    #

    def new_main_dialog(self):
        parent = self.iface.mainWindow()
        dialog = MainDialog(parent)

        dialog.config(self.token_model)
        dialog.config_secret(self.secret)
        auth = self.auth_manager.get_auth()
        dialog.config_basemap(self.map_basemap_meta, auth)

        con = self.con_man.make_con("create")
        con.signal.finished.connect(
            dialog.btn_use.clicked.emit)  # can be optimized !!
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("list")
        con.signal.results.connect(make_fun_args(dialog.cb_display_spaces))
        con.signal.error.connect(self.cb_handle_error_msg)
        con.signal.error.connect(lambda e: dialog.cb_enable_token_ui())
        con.signal.finished.connect(dialog.cb_enable_token_ui)

        con = self.con_man.make_con("edit")
        con.signal.finished.connect(dialog.btn_use.clicked.emit)
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("delete")
        con.signal.results.connect(dialog.btn_use.clicked.emit)
        con.signal.error.connect(self.cb_handle_error_msg)

        con = self.con_man.make_con("stat")
        con.signal.results.connect(make_fun_args(
            dialog.cb_display_space_count))
        con.signal.error.connect(self.cb_handle_error_msg)

        ############ clear cache btn
        dialog.signal_clear_cache.connect(self.open_clear_cache_dialog)

        ############ add map tile btn
        dialog.signal_add_basemap.connect(self.add_basemap_layer)

        ############ btn: new, edit, delete space
        dialog.signal_new_space.connect(self.start_new_space)
        dialog.signal_edit_space.connect(self.start_edit_space)
        dialog.signal_del_space.connect(self.start_delete_space)

        ############ Use Token btn
        dialog.signal_use_token.connect(lambda a: self.con_man.finish_fast())
        dialog.signal_use_token.connect(self.start_use_token)

        ############ get count
        dialog.signal_space_count.connect(
            self.start_count_feat,
            Qt.QueuedConnection)  # queued -> non-blocking ui

        ############ connect btn
        dialog.signal_space_connect.connect(self.start_load_layer)
        dialog.signal_space_tile.connect(self.start_load_tile)

        ############ upload btn
        dialog.signal_upload_space.connect(self.start_upload_space)

        return dialog

    def start_new_space(self, args):
        con = self.con_man.get_con("create")
        con.start_args(args)

    def start_edit_space(self, args):
        con = self.con_man.get_con("edit")
        con.start_args(args)

    def start_delete_space(self, args):
        con = self.con_man.get_con("delete")
        con.start_args(args)

    def start_use_token(self, args):
        con = self.con_man.get_con("list")
        con.start_args(args)

    def start_count_feat(self, args):
        con = self.con_man.get_con("stat")
        con.start_args(args)

    def start_upload_space(self, args):
        con_upload = UploadLayerController(self.network, n_parallel=2)
        self.con_man.add_background(con_upload)
        # con_upload.signal.finished.connect( self.make_cb_success("Uploading finish") )
        con_upload.signal.results.connect(
            self.make_cb_success_args("Uploading finish"))
        con_upload.signal.error.connect(self.cb_handle_error_msg)

        con = InitUploadLayerController(self.network)
        self.con_man.add_background(con)

        con.signal.results.connect(con_upload.start_args)
        con.signal.error.connect(self.cb_handle_error_msg)

        con.start_args(args)

    def start_load_layer(self, args):
        # create new con
        # config
        # run

        ############ connect btn
        con_load = LoadLayerController(self.network, n_parallel=1)
        self.con_man.add_background(con_load)
        # con_load.signal.finished.connect( self.make_cb_success("Loading finish") )
        con_load.signal.results.connect(
            self.make_cb_success_args("Loading finish"))
        # con_load.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
        con_load.signal.error.connect(self.cb_handle_error_msg)

        con_load.start_args(args)

        # con.signal.results.connect( self.layer_man.add_args) # IMPORTANT
    def start_load_tile(self, args):
        canvas = self.iface.mapCanvas()
        rect = bbox_utils.extend_to_rect(bbox_utils.get_bounding_box(canvas))
        level = tile_utils.get_zoom_for_current_map_scale(canvas)
        # rect = (-180,-90,180,90)
        # level = 0
        a, kw = parse_qt_args(args)

        kw["tile_schema"] = "here"
        kw["tile_ids"] = tile_utils.bboxToListColRow(*rect, level)
        # kw["limit"] = 100

        ############ connect btn
        con_load = TileLayerLoader(self.network, n_parallel=1)
        self.con_man.add_layer(con_load)
        # con_load.signal.finished.connect( self.make_cb_success("Tiles loaded") )
        con_load.signal.results.connect(
            self.make_cb_success_args("Tiles loaded", dt=2))
        # con_load.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
        con_load.signal.error.connect(self.cb_handle_error_msg)

        con_load.start_args(make_qt_args(*a, **kw))

    def reload_tile(self):
        canvas = self.iface.mapCanvas()
        rect = bbox_utils.extend_to_rect(bbox_utils.get_bounding_box(canvas))
        level = tile_utils.get_zoom_for_current_map_scale(canvas)
        kw = dict()
        kw["tile_schema"] = "here"
        kw["tile_ids"] = tile_utils.bboxToListColRow(*rect, level)
        # kw["limit"] = 100

        unique_con = set()
        lst_con = list()
        for qnode in [
                vl
                for vl in QgsProject.instance().layerTreeRoot().checkedLayers(
                ) if is_xyz_supported_layer(vl)
        ] + [
                g for g in QgsProject.instance().layerTreeRoot().findGroups()
                if len(g.children()) == 0 and g.isVisible()
                and is_xyz_supported_node(g)
        ]:
            xlayer_id = qnode.customProperty("xyz-hub-id")
            con = self.con_man.get_from_xyz_layer(xlayer_id)
            if con is None: continue
            if con in unique_con: continue
            lst_con.append(con)
            unique_con.add(con)
        # print_qgis(lst_con)
        # print_qgis(self.con_man._layer_ptr)

        for con in lst_con:
            print_qgis(con.status)
            print_qgis("loading tile", level, rect)
            con.restart(**kw)

    def add_basemap_layer(self, args):
        a, kw = parse_qt_args(args)
        meta, app_id, app_code = a
        self.auth_manager.save(app_id, app_code)
        basemap.add_basemap_layer(meta, app_id, app_code)

    ###############
    # import project function
    ###############

    def import_project(self):
        self.init_tile_loader()

    def init_tile_loader(self):
        cnt = 0
        for qnode in [
                g for g in QgsProject.instance().layerTreeRoot().findGroups()
                if is_xyz_supported_node(g)
        ]:
            try:
                layer = XYZLayer.load_from_qnode(qnode)
                con_load = TileLayerLoader(self.network,
                                           n_parallel=1,
                                           layer=layer)
                ptr = self.con_man.add_layer(con_load)
                # con_load.signal.finished.connect( self.make_cb_success("Tiles loaded") )
                con_load.signal.results.connect(
                    self.make_cb_success_args("Tiles loaded", dt=2))
                # con_load.signal.finished.connect( self.refresh_canvas, Qt.QueuedConnection)
                con_load.signal.error.connect(self.cb_handle_error_msg)

                cnt += 1
            except:
                pass

        # print_qgis(self.con_man._layer_ptr)
        self.cb_success_msg("Import XYZ Layer",
                            "%s XYZ Layer imported" % cnt,
                            dt=2)

    ###############
    # Open dialog
    ###############
    def open_clear_cache_dialog(self):
        dialog = ConfirmDialog(
            "Delete cache will make loaded layer unusable !!")
        ret = dialog.exec_()
        if ret != dialog.Ok: return

        utils.clear_cache()

    def open_connection_dialog(self):
        dialog = self.new_main_dialog()

        vlayer = self.iface.activeLayer()
        dialog.set_layer(vlayer)

        dialog.exec_()
        self.con_man.finish_fast()
        # self.startTime = time.time()
    def open_sync_edit_dialog(self):
        vlayer = self.iface.activeLayer()
        layer_buffer = self.edit_buffer.get_layer_buffer(vlayer.id())

        lst_added_feat, removed_ids = layer_buffer.get_sync_feat()
        conn_info = layer_buffer.get_conn_info()

        # print_qgis("lst_added_feat: ",lst_added_feat)
        # print_qgis("removed_feat: ", removed_ids)

        con = EditSyncController(self.network)
        self.con_man.add_background(con)
        con.signal.finished.connect(layer_buffer.sync_complete)
        con.signal.results.connect(
            self.make_cb_success_args("Sync edit finish"))
        con.signal.error.connect(self.cb_handle_error_msg)

        con.start(conn_info, layer_buffer, lst_added_feat, removed_ids)
Example #20
0
class QVDistrictesBarris(QObject):

    __distBarrisCSV = r'Dades\DIST_BARRIS.csv'
    __zones = r'Dades\Zones.gpkg'

    def __init__(self):
        super().__init__()
        self.labels = []
        self.registre = {}

        # Model
        self.model = QStandardItemModel()
        self.llegirZonesGPKG()
        #self.llegirDistrictesBarrisCSV()

        # View
        self.view = QTreeView()

        self.view.setContextMenuPolicy(Qt.ActionsContextMenu)

        self.actExpand = QAction("Expandeix/Contreu Tot", self)
        self.actExpand.setStatusTip("Expand")
        self.actExpand.triggered.connect(self.expand_all)
        self.view.addAction(self.actExpand)

        self.view.setModel(self.model)
        self.iniView()

    def expand_all(self):
        """Expandir o contraer todo el arbol, dependiendo de si detecta que esta extendido o contraido
        """
        if self.view.isExpanded(self.model.index(0, 0, QModelIndex())):
            self.view.collapseAll()
        else:
            self.view.expandAll()

    def iniView(self):
        self.view.setHeaderHidden(True)
        for i in range(self.model.columnCount()):
            if i == 0:
                self.view.setColumnHidden(i, False)
                self.view.resizeColumnToContents(i)
                self.view.resizeColumnToContents(i)
            else:
                self.view.setColumnHidden(i, True)
        self.view.setEditTriggers(QTreeView.NoEditTriggers)
        self.expand_all()

    def llegirZonesGPKG(self):
        try:
            QvFuncions.setReadOnlyFile(self.__zones)
            pathDistrictes = self.__zones + '|layername=districtes'
            layerDistrictes = QgsVectorLayer(pathDistrictes, 'ogr')
            pathBarris = self.__zones + '|layername=barris'
            layerBarris = QgsVectorLayer(pathBarris, 'ogr')

            rowsDistrictes = layerDistrictes.getFeatures()
            llistaDistrictes = []
            for rowD in rowsDistrictes:
                #print(rowD.attributes())
                #zona = ""
                num_districte = rowD.attributes()[1]
                nom_districte = rowD.attributes()[2]
                num_barri = ""

                nom_barri = ""
                geometria = rowD.geometry().boundingBox()
                x_min = str(geometria.xMinimum())
                y_min = str(geometria.yMinimum())
                x_max = str(geometria.xMaximum())
                y_max = str(geometria.yMaximum())
                item = [
                    num_districte, nom_districte, num_barri, nom_barri, x_min,
                    y_min, x_max, y_max
                ]
                llistaDistrictes.append(item)

            def ordenaPerNumDistricte(elem):
                return elem[0]

            llistaDistrictes.sort(key=ordenaPerNumDistricte)
            #print(llistaDistrictes)

            rowsBarris = layerBarris.getFeatures()
            llistaBarris = []
            for rowB in rowsBarris:
                #print(rowB.attributes())
                #zona = ""
                num_districte = rowB.attributes()[3]
                nom_districte = llistaDistrictes[int(num_districte) - 1][1]
                num_barri = rowB.attributes()[1]
                nom_barri = rowB.attributes()[2]
                geometria = rowB.geometry().boundingBox()
                x_min = str(geometria.xMinimum())
                y_min = str(geometria.yMinimum())
                x_max = str(geometria.xMaximum())
                y_max = str(geometria.yMaximum())
                item = [
                    num_districte, nom_districte, num_barri, nom_barri, x_min,
                    y_min, x_max, y_max
                ]
                llistaBarris.append(item)

            def ordenaPerNumBarri(elem):
                return elem[2]

            llistaBarris.sort(key=ordenaPerNumBarri)
            #print(llistaBarris)

            self.labels = [
                "ZONA", "DISTRICTE", "NOM_DISTRICTE", "BARRI", "NOM_BARRI",
                "X_MIN", "Y_MIN", "X_MAX", "Y_MAX"
            ]
            root = self.model.invisibleRootItem()
            self.model.setColumnCount(len(self.labels))
            self.model.setHorizontalHeaderLabels(self.labels)

            #Afegir Barcelona com a arrel de l'arbre
            bcn_dades = [
                "00", "Barcelona", "00", "Barcelona", "419710.0553820258",
                "4573818.80776309", "436533.35", "4591775.02"
            ]
            bcn = [QStandardItem("Barcelona")]
            for item in bcn_dades:
                bcn.append(QStandardItem(item))
            root.appendRow(bcn)

            ultimaDistr = -1
            itDist = 0
            for b in llistaBarris:
                if ultimaDistr != int(b[0]):  #Afegir següent districte
                    dist = [QStandardItem(llistaDistrictes[itDist][1])]
                    for i in range(0, len(llistaDistrictes[itDist])):
                        dist.append(QStandardItem(llistaDistrictes[itDist][i]))
                    bcn[0].appendRow(dist)

                    itDist = itDist + 1
                    #Afegir següent Barri
                barri = [QStandardItem(b[3])]
                for item in b:
                    barri.append(QStandardItem(item))
                dist[0].appendRow(barri)
                ultimaDistr = int(b[0])
            return True
        except:
            print("Error en construcció de l'arbre de zones")
            return False

    # def llegirDistrictesBarrisCSV(self):
    #     try:
    #         first = True
    #         with open(self.__distBarrisCSV, newline='') as csvFile:
    #             reader = csv.DictReader(csvFile, delimiter=';')
    #             root = self.model.invisibleRootItem()
    #             for row in reader:
    #                 if first: # Primer registro
    #                     self.labels = ['ZONA']
    #                     for item in row:
    #                         self.labels.append(item)
    #                     self.model.setColumnCount(len(self.labels))
    #                     self.model.setHorizontalHeaderLabels(self.labels)
    #                     first = False
    #                 if row['BARRI'] == '': # Registro de distrito
    #                     dist = [QStandardItem(row['NOM_DISTRICTE'])]
    #                     for item in row.values():
    #                         dist.append(QStandardItem(item))
    #                     root.appendRow(dist)
    #                 else: # Registro de barrio
    #                     barri = [QStandardItem(row['NOM_BARRI'])]
    #                     for item in row.values():
    #                         barri.append(QStandardItem(item))
    #                     dist[0].appendRow(barri)
    #         return True
    #     except:
    #         print('QDistrictesBarris.llegirDistrictesBarrisCSV(): ', sys.exc_info()[0], sys.exc_info()[1])
    #         return False

    def llegirRegistre(self):
        try:
            click = self.view.currentIndex()
            #Controlarem si s'ha canviat d'índex o no
            if hasattr(self, 'ultimIndex') and self.ultimIndex == click:
                return self.registre
            self.ultimIndex = click
            self.registre = {}
            for i in range(self.model.columnCount()):
                index = click.sibling(click.row(), i)
                item = self.model.itemFromIndex(index)
                self.registre[self.labels[i]] = item.text()
            self.registre['RANG'] = QgsRectangle(float(self.registre['X_MIN']), \
                                                float(self.registre['Y_MIN']), \
                                                float(self.registre['X_MAX']), \
                                                float(self.registre['Y_MAX']))
        except:
            print('QDistrictesBarris.llegirRegistre(): ',
                  sys.exc_info()[0],
                  sys.exc_info()[1])
        finally:
            return self.registre

    def llegirRang(self):
        return self.llegirRegistre()['RANG']

    def esDistricte(self):
        return self.llegirRegistre()['BARRI'] == ''

    def esBarri(self):
        return not self.esDistricte()

    def llegirNom(self):
        if self.esDistricte():
            distr = self.llegirRegistre()["NOM_DISTRICTE"]
            distr_d = distr + "_d"
            return distr_d
        else:
            barri = self.llegirRegistre()["NOM_BARRI"]
            if barri == "Barcelona":
                return barri
            else:
                barri_b = barri + "_b"
                return barri_b

    def llegirID(self):
        if self.esDistricte():
            return self.llegirRegistre()['DISTRICTE']
        return self.llegirRegistre()['BARRI']
class GimpSelectionFeaturePlugin(QObject):

  def __init__(self, iface):
    super().__init__()
    self.iface = iface
    self.name = u"&Gimp Selection Feature"
    self.dock = self.exitsPluginGimp = None
    self.translate = Translate( 'gimpselectionfeature' )

  def initGui(self):
    def setExistsPluginGimp():
      def getDirPluginGimp():
        dirPlugin = None
        mask = r".*gimp.[0-9]+.[0-9]+/%s" % nameDirPlugin # Linux Format
        for root, dirs, files in os.walk( dirHome ):
          if re.match( mask, root.replace('\\', '/'), re.IGNORECASE ):
            dirPlugin = root
            break

        return dirPlugin

      def copyNewPlugin():
        shutil.copy2( gimpPlugin, gimpPluginInstall )
        if sys.platform != 'win32': # Add executable
          st =  os.stat( gimpPluginInstall )
          os.chmod( gimpPluginInstall, st.st_mode | stat.S_IEXEC )

      dirHome = os.path.expanduser('~')
      nameDirPlugin = "plug-ins"
      dirPluginGimp = getDirPluginGimp()
      if dirPluginGimp is None:
        msg = "Not found diretory 'GIMP' or 'GIMP {}' in '{}'".format( nameDirPlugin, dirHome )
        self.exitsPluginGimp = { 'isOk': False, 'msg': msg }
        return

      namePlugin = 'socket_server_selection.py'
      gimpPlugin = os.path.join( os.path.dirname(__file__), namePlugin )
      gimpPluginInstall = os.path.join( dirPluginGimp, namePlugin )
      if not os.path.exists( gimpPluginInstall ) or not filecmp.cmp( gimpPlugin, gimpPluginInstall ):
        copyNewPlugin()

      self.exitsPluginGimp = { 'isOk': True }

    name = "Gimp Selection Feature"
    about = QCoreApplication.translate('GimpSelectionFeature', 'Adding selected area in GIMP how a feature in shapefile')
    icon = QIcon( os.path.join( os.path.dirname(__file__), 'gimpselectionfeature.svg' ) )
    self.action = QAction( icon, name, self.iface.mainWindow() )
    self.action.setObjectName( name.replace(' ', '') )
    self.action.setWhatsThis( about )
    self.action.setStatusTip( about )
    self.action.setCheckable( True )
    self.action.triggered.connect( self.run )

    self.iface.addRasterToolBarIcon( self.action )
    self.iface.addPluginToRasterMenu( self.name, self.action )

    setExistsPluginGimp()
    if not self.exitsPluginGimp['isOk']:
      return

    self.dock = DockWidgetGimpSelectionFeature( self.iface )
    self.iface.addDockWidget( Qt.RightDockWidgetArea , self.dock )
    self.dock.visibilityChanged.connect( self.dockVisibilityChanged )

  def unload(self):
    self.iface.removeRasterToolBarIcon( self.action )
    self.iface.removePluginRasterMenu( self.name, self.action )

    if self.exitsPluginGimp['isOk']:
      self.dock.close()
      del self.dock
      self.dock = None

    del self.action

  @pyqtSlot()
  def run(self):
    if not self.exitsPluginGimp['isOk']:
      ( t, m ) = ( GimpSelectionFeature.nameModulus, self.exitsPluginGimp['msg'] )
      self.iface.messageBar().pushMessage( t, m, Qgis.Critical, 5 )
      self.action.setChecked( False )
      return

    if self.dock.isVisible():
      self.dock.hide()
    else:
      self.dock.show()

  @pyqtSlot(bool)
  def dockVisibilityChanged(self, visible):
    self.action.setChecked( visible )
    def add_action(self,
                   icon_path,
                   text,
                   callback,
                   enabled_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            # Adds plugin icon to Plugins toolbar
            self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToVectorMenu(self.menu, action)

        self.actions.append(action)
        # Connect all actions
        self.dlg.dirOldButton.clicked.connect(self.select_oldDir)
        self.dlg.dirNewButton.clicked.connect(self.select_newDir)
        self.dlg.dirResultButton.clicked.connect(self.select_dirResult)
        self.dlg.mengdertilcsvButton.clicked.connect(self.getStats)
        self.dlg.selectdir_MButton.clicked.connect(self.select_output_dirM)
        self.dlg.skrivtilcsvButton.clicked.connect(self.exportLayers)
        self.dlg.compareButton.clicked.connect(self.comparefiles)
        self.dlg.comboBox.currentIndexChanged[str].connect(
            self.comboBox_itemChanged)
        self.dlg.addButton.clicked.connect(self.addItem)
        self.dlg.removeButton.clicked.connect(self.removeItem)
        self.dlg.clearButton.clicked.connect(self.clearSelection)
        self.dlg.kommuneCheck.toggled.connect(self.kommuneSelected)
        self.dlg.checkBox_fritekst.toggled.connect(
            self.comp_checkbox_handler_free)
        self.dlg.checkBox_nvdbid.toggled.connect(
            self.comp_checkbox_handler_nvdbid)
        self.dlg.checkBox_objekt.toggled.connect(
            self.comp_checkbox_handler_object)
        self.dlg.kontraktCheck.toggled.connect(self.kontraktSelected)
        self.dlg.fylkeBox.currentIndexChanged[str].connect(self.getKommune)
        self.dlg.fylkeBox.currentIndexChanged[str].connect(self.getKontrakt)
        self.dlg.fylkeBox.currentIndexChanged.connect(
            self.fylkeBox_itemChanged)
        self.dlg.kommuneBox.currentIndexChanged[str].connect(
            self.kommuneBox_itemChanged)
        self.dlg.kontraktBox.currentIndexChanged[str].connect(
            self.kontraktBox_itemChanged)
        self.dlg.mergeButton.clicked.connect(self.mergeLayers)
        self.dlg.selectdirButton.clicked.connect(self.select_output_dir)
        self.dlg.kjorButton.clicked.connect(self.runTask)
        self.dlg.saveButton.clicked.connect(self.saveAsPreset)
        self.dlg.vegsystemBox.currentIndexChanged[str].connect(
            self.vegsystemBox_itemChanged)
        self.dlg.searchTable.doubleClicked.connect(self.searchPreset)
        self.dlg.deleteButton.clicked.connect(self.deletePreset)
        self.dlg.statsButton.clicked.connect(self.loadStats)
        self.dlg.folderButton.clicked.connect(self.openFolder)
        getAllAreaData()
        getAllObjectData()
        getAllPresetData()
        self.dlg.vegsystemBox.addItems(returnVegreferanseData())
        return action
Example #23
0
    def add_action(self,
                   icon_path,
                   text,
                   callback,
                   enabled_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):
        """Add a toolbar icon to the toolbar.

        Parameters
        ----------
        icon_path : str
            Path to the icon for this action. Can be a resource path
            (e.g. ':/plugins/foo/bar.png') or a normal file system path
        text : str
            Text that should be shown in menu items for this action
        callback : function
            Function to be called when the action is triggered
        enabled_flag : bool, optional
            A flag indicating if the action should be enabled by default 
            (defaults to True)
        add_to_menu : bool, optional
            Flag indicating whether the action should also be added to the menu 
            (defaults to True)
        add_to_toolbar : bool, optional
            Flag indicating whether the action should also be added to the toolbar
            (defaults to True)
        status_tip : str or None, optional
            Text to show in a popup when mouse pointer hovers over the action 
            (defaults to None)
        whats_this : str or None, optional
            Text to show in the status bar when the mouse pointer hovers over
            the action (defaults to None)
        parent : QWidget or None, optional
            Parent widget for the new action (defaults to None)
        
        Returns
        ----------
        QAction
            The action that was created. Note that the action is also added to
            self.actions list.
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            # Adds plugin icon to Plugins toolbar
            self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action
Example #24
0
class HuntRegister:
    """QGIS Plugin Implementation."""
    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)

        # Check if plugin was started the first time in current QGIS session
        # Must be set in initGui() to survive plugin reloads
        self.first_start = None

        # This plugin depends on alkisplugin
        self.alkisplugin = None

        # Members
        self.alkisToolBar = None  # the toolbar instance where the alkisplugin and huntplugin QAction symbols are placed

        self.alkisSelectAreaLayer = None  # QgsVectorLayer selected parcels of alkisplugin
        self.huntSelectAreaLayer = None  # QgsVectorLayer selected parcels of huntplugin

        # help web view
        self.helpiew = None  # webview containing manuals

        # All actions are assigned to alter self.huntSelectAreaLayer
        self.hswapAction = None  # single QAction copy selected parcels from alkisplugin to huntplugin
        self.hAddMarkAction = None  # checkable QAction select and unselect parcels
        self.hlistAction = None  # single QAction select parcels by parcel attributes
        self.hclearAction = None  # single QAction unselect all selected parcels
        self.hownerAction = None  # single QAction get parcel certificates for all selected parcels
        self.hhuntAction = None  # single QAction create a hunt register
        self.hinfoAction = None  # single QAction get a basic summary of all selected parcels
        self.helpAction = None  # single QAction open help files webview browser window
        # self.testAction = None                                                                # single QAction used for testing program fragments

        self.hAddMarkTool = None  # click recognizing map tool for self.hAddMarkAction
        self.core = None  # function core for this plugin
        self.initTimer = QTimer(
            self.iface
        )  # timer used to init self.huntSelectAreaLayer dynamically when alkis layers are added
        self.initTimer.setInterval(1000)  # 1 sec interval
        self.initTimer.timeout.connect(self.initLayers)
        self.init()  # init main instances

    def init(self):
        """init main instances"""

        if (alkisAvailable):
            try:
                self.alkisplugin = Alkis.alkisplugin.alkisplugin(
                    self.iface)  # create alkisplugin object
                self.alkisplugin.queryOwnerAction = QAction(
                    None
                )  # this is used in akisplugin "opendb" and must therefore be set to prevent runtime errors
            except AttributeError:
                QMessageBox.critical(
                    None, "Fehler",
                    "norGIS ALKIS-Einbindung zuerst aktivieren und \"JagdKataster\" erneut aktivieren"
                )
                raise AttributeError("alkisplugin not active")
        else:
            QMessageBox.critical(
                None, "Fehler",
                "norGIS ALKIS-Einbindung installieren und zuerst aktivieren. Dann \"JagdKataster\" erneut aktivieren"
            )
            raise AttributeError("alkisplugin not installed")

        self.core = HuntCore(self)  # function core for this plugin

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        # will be set False in run()
        self.first_start = True

        self.helpview = QWebView()
        self.helpview.resize(1280, 850)
        self.helpview.setWindowTitle("JagdKataster Anleitungen")
        help_dir = os.path.join(self.plugin_dir, "help", "build", "html",
                                "index.html")
        self.helpview.load(QUrl.fromLocalFile(help_dir))

        # the toolbar entries of this plugin should be placed alongside the alkisplugin entries
        # therefore the alkisplugin toolbar is derived
        tBars = self.iface.mainWindow().findChildren(
            QToolBar)  # get all toolbars from main window
        self.alkisToolBar = next(
            (n
             for n in tBars if n.objectName() == "norGIS_ALKIS_Toolbar"), None
        )  # find the instance of alkisplugin toolbar by its static name

        if self.alkisToolBar is None:  # in case the toolbar is not yet loaded create the instance with its static name
            # create alkis toolbar in case it is not yet loaded
            self.alkisToolBar = self.iface.addToolBar(u"norGIS: ALKIS")
            self.alkisToolBar.setObjectName("norGIS_ALKIS_Toolbar")

        #create toolbar items
        self.hswapAction = QAction(QIcon("hunt:mark_transfer.svg"),
                                   "Flächenmarkierung übertragen",
                                   self.iface.mainWindow())
        self.hswapAction.setWhatsThis(
            "Flächenmarkierung (gelb) nach Jagd-Flächenmarkierung (blau) übertragen"
        )
        self.hswapAction.setStatusTip(
            "Flächenmarkierung (gelb) nach Jagd-Flächenmarkierung (blau) übertragen"
        )
        self.hswapAction.triggered.connect(lambda: self.core.swapAlkisToHunt())
        self.alkisToolBar.addAction(self.hswapAction)

        self.hAddMarkAction = QAction(QIcon("hunt:mark_add.svg"),
                                      u"Flurstück (de)selektieren",
                                      self.iface.mainWindow())
        self.hAddMarkAction.setWhatsThis(
            "Flurstück in Jagd-Flächenmarkierung selektieren oder deselektieren"
        )
        self.hAddMarkAction.setStatusTip(
            "Flurstück in Jagd-Flächenmarkierung selektieren oder deselektieren"
        )
        self.hAddMarkAction.setCheckable(True)
        self.hAddMarkAction.triggered.connect(
            lambda: self.iface.mapCanvas().setMapTool(self.hAddMarkTool))
        self.alkisToolBar.addAction(self.hAddMarkAction)
        self.hAddMarkTool = HAdd(self)
        self.hAddMarkTool.setAction(self.hAddMarkAction)

        self.hlistAction = QAction(QIcon("hunt:mark_list.svg"),
                                   "Selektieren nach Flurstückseigenschaft",
                                   self.iface.mainWindow())
        self.hlistAction.setWhatsThis(
            "Selektierung der Flurstücke in Jagd-Flächenmarkierung anhand Flurstückseigenschaften"
        )
        self.hlistAction.setStatusTip(
            "Selektierung der Flurstücke in Jagd-Flächenmarkierung anhand Flurstückseigenschaften"
        )
        self.hlistAction.triggered.connect(
            lambda: self.core.showListSelection())
        self.alkisToolBar.addAction(self.hlistAction)

        self.hclearAction = QAction(QIcon("hunt:mark_clear.svg"),
                                    "Alle deselektieren",
                                    self.iface.mainWindow())
        self.hclearAction.setWhatsThis(
            "Alle Flurstücke in Jagd-Flächenmarkierung deselektieren")
        self.hclearAction.setStatusTip(
            "Alle Flurstücke in Jagd-Flächenmarkierung deselektieren")
        self.hclearAction.triggered.connect(lambda: self.core.clearHighlight())
        self.alkisToolBar.addAction(self.hclearAction)

        self.hownerAction = QAction(QIcon("hunt:mark_own.svg"),
                                    "Flurstücksnachweise anzeigen",
                                    self.iface.mainWindow())
        self.hownerAction.setWhatsThis(
            "Flurstücksnachweise für selektierte Flurstücke in Jagd-Flächenmarkierung anzeigen"
        )
        self.hownerAction.setStatusTip(
            "Flurstücksnachweise für selektierte Flurstücke in Jagd-Flächenmarkierung anzeigen"
        )
        self.hownerAction.triggered.connect(
            lambda: self.core.showParcelCerts())
        self.alkisToolBar.addAction(self.hownerAction)

        self.hhuntAction = QAction(QIcon("hunt:mark_hunt.svg"),
                                   "Jagdkataster erstellen",
                                   self.iface.mainWindow())
        self.hhuntAction.setWhatsThis(
            "Jagdkataster für selektierte Flurstücke in Jagd-Flächenmarkierung erstellen"
        )
        self.hhuntAction.setStatusTip(
            "Jagdkataster für selektierte Flurstücke in Jagd-Flächenmarkierung erstellen"
        )
        self.hhuntAction.triggered.connect(lambda: self.core.showHuntReg())
        self.alkisToolBar.addAction(self.hhuntAction)

        self.hinfoAction = QAction(QIcon("hunt:mark_info.svg"),
                                   "Flurstückszusammenfassung anzeigen",
                                   self.iface.mainWindow())
        self.hinfoAction.setWhatsThis(
            "Flurstückszusammenfassung für selektierte Flurstücke in Jagd-Flächenmarkierung anzeigen"
        )
        self.hinfoAction.setStatusTip(
            "Flurstückszusammenfassung für selektierte Flurstücke in Jagd-Flächenmarkierung anzeigen"
        )
        self.hinfoAction.triggered.connect(
            lambda: self.core.showSummaryDialog())
        self.alkisToolBar.addAction(self.hinfoAction)

        self.helpAction = QAction(QIcon("hunt:logo.svg"), "Anleitungen",
                                  self.iface.mainWindow())
        self.helpAction.setWhatsThis("JagdKataster-Anleitungen")
        self.helpAction.setStatusTip("JagdKataster-Anleitungen")
        self.helpAction.triggered.connect(lambda: self.helpview.show())

        # self.testAction = QAction(QIcon("hunt:test.svg"), "Test", self.iface.mainWindow())
        # self.testAction.setWhatsThis("Test action")
        # self.testAction.setStatusTip("Test action")
        # self.testAction.triggered.connect(lambda: self.core.test(self.huntSelectAreaLayer))
        # self.alkisToolBar.addAction(self.testAction)

        self.iface.addPluginToDatabaseMenu("&ALKIS", self.helpAction)

        QgsProject.instance().layersAdded.connect(
            self.initTimer.start
        )  # react to changes in the layer tree. Maybe the alkis layers were added
        QgsProject.instance().layersWillBeRemoved.connect(
            self.layersRemoved
        )  # remove entries in case this plugin layers are to be removed

    def initLayers(self):
        """init self.huntSelectAreaLayer in case the alkis layers from alkisplugin are loaded"""
        self.initTimer.stop(
        )  # this methode may be called by a timer started when layers are added => stop the timer after first timeout event
        if self.alkisSelectAreaLayer is None:  # are alkisplugin layers loaded ad readable from entry?
            (layerId,
             res) = QgsProject.instance().readEntry("alkis",
                                                    "/areaMarkerLayer")
            if res and layerId:
                self.alkisSelectAreaLayer = QgsProject.instance().mapLayer(
                    layerId)
        if self.huntSelectAreaLayer is None:  # is the huntplugin layer already loaded?
            (layerId,
             res) = QgsProject.instance().readEntry("hunt", "/areaMarkerLayer")
            if res and layerId:
                self.huntSelectAreaLayer = QgsProject.instance().mapLayer(
                    layerId)
        if self.huntSelectAreaLayer is None and self.alkisSelectAreaLayer is not None:  # alkisplugin layers are loaded but huntplugin layer is not
            self.createLayer()  # create huntplugin layer

    def layersRemoved(self, layersIds):
        """remove entries and references in case this plugin layers are to be removed"""
        if self.alkisSelectAreaLayer is not None and self.alkisSelectAreaLayer.id(
        ) in layersIds:
            self.alkisSelectAreaLayer = None
        if self.huntSelectAreaLayer is not None and self.huntSelectAreaLayer.id(
        ) in layersIds:
            QgsProject.instance().removeEntry("hunt", "/areaMarkerLayer")
            self.core.hlayer = None
            self.huntSelectAreaLayer = None

    def createLayer(self):
        """create and add huntplugin layer to the layer tree"""
        if (self.alkisSelectAreaLayer is not None):
            parent = QgsProject.instance().layerTreeRoot().findLayer(
                self.alkisSelectAreaLayer).parent()
            layeropts = QgsVectorLayer.LayerOptions(False, False)

            self.init(
            )  # reinit main instances because alkis instance conninfo might have changed
            (db, conninfo) = self.core.openDB()
            if db is None:
                return

            self.huntSelectAreaLayer = QgsVectorLayer(
                u"%s estimatedmetadata=true checkPrimaryKeyUnicity=0 key='ogc_fid' type=MULTIPOLYGON srid=%d table=%s.po_polygons (polygon) sql=false"
                % (conninfo, self.alkisplugin.epsg,
                   self.alkisplugin.quotedschema()), u"Jagd-Flächenmarkierung",
                "postgres", layeropts)

            sym = QgsSymbol.defaultSymbol(QgsWkbTypes.PolygonGeometry)
            sym.setColor(Qt.blue)
            sym.setOpacity(0.3)

            self.huntSelectAreaLayer.setRenderer(QgsSingleSymbolRenderer(sym))
            QgsProject.instance().addMapLayer(self.huntSelectAreaLayer, False)
            parent.insertLayer(0, self.huntSelectAreaLayer)

            self.core.hlayer = None
            QgsProject.instance().writeEntry("hunt", "/areaMarkerLayer",
                                             self.huntSelectAreaLayer.id())

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        if self.hswapAction:
            self.hswapAction.deleteLater()
            self.hswapAction = None
        if self.hAddMarkAction:
            self.hAddMarkAction.deleteLater()
            self.hAddMarkAction = None
        if self.hlistAction:
            self.hlistAction.deleteLater()
            self.hlistAction = None
        if self.hclearAction:
            self.hclearAction.deleteLater()
            self.hclearAction = None
        if self.hownerAction:
            self.hownerAction.deleteLater()
            self.hownerAction = None
        if self.hhuntAction:
            self.hhuntAction.deleteLater()
            self.hhuntAction = None
        if self.hinfoAction:
            self.hinfoAction.deleteLater()
            self.hinfoAction = None
        if self.helpAction:
            self.helpAction.deleteLater()
            self.helpAction = None
        # if self.testAction:
        #     self.testAction.deleteLater()
        #     self.testAction = None

        QgsProject.instance().layersAdded.disconnect(self.initTimer.start)
        QgsProject.instance().layersWillBeRemoved.disconnect(
            self.layersRemoved)

    def run(self):
        """Run method"""
        if self.first_start:
            self.first_start = False
class PointConnector(object):
    """QGIS Plugin Implementation."""
    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   'PointConnector_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Create the dialog (after translation) and keep reference
        self.dlg = PointConnectorDialog()

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&Point Connector')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'PointConnector')
        self.toolbar.setObjectName(u'PointConnector')
        self.addedLayers = 1

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('PointConnector', message)

    def add_action(self,
                   icon_path,
                   text,
                   callback,
                   enabled_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):
        """Add a toolbar icon to the InaSAFE toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action

    def initGui(self):
        self.action = QAction(QIcon(":/plugins/PointConnector/icon.png"),
                              "PointConnector", self.iface.mainWindow())
        self.action.setWhatsThis("Connect points")
        self.action.setStatusTip("Connect points following a from-to list")

        self.action.triggered.connect(self.run)

        if hasattr(self.iface, "addPluginToVectorMenu"):
            self.iface.addVectorToolBarIcon(self.action)
            self.iface.addPluginToVectorMenu("&PointConnector", self.action)
        else:
            self.iface.addToolBarIcon(self.action)
            self.iface.addPluginToMenu("&PointConnector", self.action)

    def unload(self):
        self.iface.removePluginVectorMenu("&PointConnector", self.action)
        self.iface.removeVectorToolBarIcon(self.action)
        self.iface.removeToolBarIcon(self.action)

    def run(self):

        self.dlg.populateComboBox()
        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result == 1:
            # create layers dict
            #layers = QgsProject.instance().mapLayers()
            layers = {}
            for name, layer in QgsProject.instance().mapLayers().items():
                layers[layer.name()] = layer

            #choose point-source
            chosenPoint = self.dlg.pointsComboBox.currentText()

            if chosenPoint != 'Choose layer...':
                point_layer = layers[chosenPoint]

            else:
                pointPath = self.dlg.pointPathLineEdit.text()
                point_layer = QgsVectorLayer(
                    pointPath, 'points',
                    'ogr')  #shp-file with attribute field name

                try:
                    p = open(pointPath, 'r')
                    p.close()
                except IOError:
                    QMessageBox.information(
                        None, "Error",
                        "Shape-file not found. Check file path.")
                    return

            point_name_index = 0

            # choose csv-source
            lines_list = []
            chosenCsv = self.dlg.csvComboBox.currentText()

            if chosenCsv != 'Choose layer...':
                csv_layer = layers[chosenCsv]
                csv_features = csv_layer.getFeatures()
                for line in csv_features:
                    attrs = line.attributes()
                    lines_list.append(((str(attrs[0])), str(attrs[1])))
            else:
                csvPath = self.dlg.csvPathLineEdit.text()
                # test if csv is valid
                try:
                    f = codecs.open(csvPath, 'r', 'utf-8-sig')
                    for line in f:
                        pass
                    f = codecs.open(csvPath, 'r', 'utf-8-sig')

                except UnicodeDecodeError:
                    try:
                        f = open(csvPath, 'r')
                        re.search('\\\\', f) == None

                    except:
                        QMessageBox.information(
                            None, "Error",
                            "PointConnector can not read csv-file. Try saving it with utf-8 encoding or import it as a layer."
                        )
                        return
                except IOError:
                    QMessageBox.information(
                        None, "Error",
                        "Csv-file not found. Check file path or select a csv-layer."
                    )
                    return

                #creating lines list from file
                for line in f:
                    line = line.splitlines()
                    for s in line[:1]:
                        s = tuple(s.split(','))
                        lines_list.append(s)
                f.close()

            point_layer_crs = point_layer.crs().authid()
            lines_layer = QgsVectorLayer(
                'LineString?crs=' + point_layer_crs,
                'PointConnector lines ' + str(self.addedLayers), 'memory')
            pr = lines_layer.dataProvider()

            lines_layer.startEditing()
            pr.addAttributes([
                QgsField('id', QVariant.Int),
                QgsField('from', QVariant.String),
                QgsField('to', QVariant.String)
            ])

            #creating point coordinate dict
            points = point_layer.getFeatures()
            points_dict = {}

            #Progress bar widget
            progressMessageBar = iface.messageBar().createMessage(
                "Building point database...")
            progress = QProgressBar()
            progress.setMaximum(point_layer.featureCount())
            progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
            progressMessageBar.layout().addWidget(progress)
            iface.messageBar().pushWidget(progressMessageBar, Qgis.Info)

            i = 0
            for p in points:
                geom = p.geometry()
                attrs = p.attributes()
                p = geom.asPoint()
                key = attrs[point_name_index]
                points_dict[str(
                    key)] = p  #attrs[point_name_index] = name field
                i += 1
                progress.setValue(i)

            iface.messageBar().clearWidgets()
            QgsProject.instance().addMapLayer(point_layer)

            #Progress bar widget
            progressMessageBar = iface.messageBar().createMessage(
                "Drawing lines...")
            progress = QProgressBar()
            progress.setMaximum(len(lines_list))
            progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
            progressMessageBar.layout().addWidget(progress)
            iface.messageBar().pushWidget(progressMessageBar, Qgis.Info)

            #Drawing the lines
            i = 1
            not_processed_list = []

            for line in lines_list:
                if (line[0] in list(points_dict.keys())
                        and line[1] in list(points_dict.keys())):
                    frPoint = points_dict[line[0]]
                    toPoint = points_dict[line[1]]
                    attrs = [i, line[0], line[1]]
                    new_line = QgsGeometry.fromPolyline(
                        [QgsPoint(frPoint),
                         QgsPoint(toPoint)])
                    feat = QgsFeature()
                    feat.setGeometry(new_line)
                    feat.setAttributes(attrs)
                    (res, outFeats) = pr.addFeatures([feat])
                    lines_layer.commitChanges()
                    if res != True:
                        pass
                    i += 1
                    progress.setValue(i)
                else:
                    not_processed_list.append(line)
                    progress.setValue(i)

            iface.messageBar().clearWidgets()

            # add lines layer to canvas
            QgsProject.instance().addMapLayer(lines_layer)
            self.addedLayers += 1

            if not not_processed_list:
                QMessageBox.information(None, 'Success',
                                        'All lines drawn without error')
            else:
                QMessageBox.information(
                    None, 'Error',
                    str(len(not_processed_list)) + ' out of ' +
                    str(len(lines_list)) + ' line(s) not drawn.')
Example #26
0
    def add_action(self,
                   icon_path,
                   text,
                   callback,
                   enabled_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon("icon.png")
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action
Example #27
0
class GdalTools:

    def __init__(self, iface):
        if not valid:
            return

        # Save reference to the QGIS interface
        self.iface = iface
        try:
            self.QgisVersion = unicode(QGis.QGIS_VERSION_INT)
        except:
            self.QgisVersion = unicode(QGis.qgisVersion)[0]

        if QGis.QGIS_VERSION[0:3] < "1.5":
            # For i18n support
            userPluginPath = qgis.utils.home_plugin_path + "/GdalTools"
            systemPluginPath = qgis.utils.sys_plugin_path + "/GdalTools"

            overrideLocale = QSettings().value("locale/overrideFlag", False, type=bool)
            if not overrideLocale:
                localeFullName = QLocale.system().name()
            else:
                localeFullName = QSettings().value("locale/userLocale", "", type=str)

            if QFileInfo(userPluginPath).exists():
                translationPath = userPluginPath + "/i18n/GdalTools_" + localeFullName + ".qm"
            else:
                translationPath = systemPluginPath + "/i18n/GdalTools_" + localeFullName + ".qm"

            self.localePath = translationPath
            if QFileInfo(self.localePath).exists():
                self.translator = QTranslator()
                self.translator.load(self.localePath)
                QCoreApplication.installTranslator(self.translator)

        # The list of actions added to menus, so we can remove them when unloading the plugin
        self._menuActions = []

    def initGui(self):
        if not valid:
            return
        if int(self.QgisVersion) < 1:
            QMessageBox.warning(
                self.iface.getMainWindow(), "Gdal Tools",
                QCoreApplication.translate("GdalTools", "QGIS version detected: ") + unicode(self.QgisVersion) + ".xx\n"
                + QCoreApplication.translate("GdalTools", "This version of Gdal Tools requires at least QGIS version 1.0.0\nPlugin will not be enabled."))
            return None

        from .tools.GdalTools_utils import GdalConfig, LayerRegistry
        self.GdalVersionNum = GdalConfig.versionNum()
        LayerRegistry.setIface(self.iface)

        # find the Raster menu
        rasterMenu = None
        menu_bar = self.iface.mainWindow().menuBar()
        actions = menu_bar.actions()

        rasterText = QCoreApplication.translate("QgisApp", "&Raster")

        for a in actions:
            if a.menu() is not None and a.menu().title() == rasterText:
                rasterMenu = a.menu()
                break

        if rasterMenu is None:
            # no Raster menu, create and insert it before the Help menu
            self.menu = QMenu(rasterText, self.iface.mainWindow())
            lastAction = actions[len(actions) - 1]
            menu_bar.insertMenu(lastAction, self.menu)
        else:
            self.menu = rasterMenu
            self._menuActions.append(self.menu.addSeparator())

        # projections menu (Warp (Reproject), Assign projection)
        self.projectionsMenu = QMenu(QCoreApplication.translate("GdalTools", "Projections"), self.iface.mainWindow())
        self.projectionsMenu.setObjectName("projectionsMenu")

        self.warp = QAction(QIcon(":/icons/warp.png"), QCoreApplication.translate("GdalTools", "Warp (Reproject)..."), self.iface.mainWindow())
        self.warp.setObjectName("warp")
        self.warp.setStatusTip(QCoreApplication.translate("GdalTools", "Warp an image into a new coordinate system"))
        self.warp.triggered.connect(self.doWarp)

        self.projection = QAction(QIcon(":icons/projection-add.png"), QCoreApplication.translate("GdalTools", "Assign Projection..."), self.iface.mainWindow())
        self.projection.setObjectName("projection")
        self.projection.setStatusTip(QCoreApplication.translate("GdalTools", "Add projection info to the raster"))
        self.projection.triggered.connect(self.doProjection)

        self.extractProj = QAction(QIcon(":icons/projection-export.png"), QCoreApplication.translate("GdalTools", "Extract Projection..."), self.iface.mainWindow())
        self.extractProj.setObjectName("extractProj")
        self.extractProj.setStatusTip(QCoreApplication.translate("GdalTools", "Extract projection information from raster(s)"))
        self.extractProj.triggered.connect(self.doExtractProj)

        self.projectionsMenu.addActions([self.warp, self.projection, self.extractProj])

        # conversion menu (Rasterize (Vector to raster), Polygonize (Raster to vector), Translate, RGB to PCT, PCT to RGB)
        self.conversionMenu = QMenu(QCoreApplication.translate("GdalTools", "Conversion"), self.iface.mainWindow())
        self.conversionMenu.setObjectName("conversionMenu")

        if self.GdalVersionNum >= 1300:
            self.rasterize = QAction(QIcon(":/icons/rasterize.png"), QCoreApplication.translate("GdalTools", "Rasterize (Vector to Raster)..."), self.iface.mainWindow())
            self.rasterize.setObjectName("rasterize")
            self.rasterize.setStatusTip(QCoreApplication.translate("GdalTools", "Burns vector geometries into a raster"))
            self.rasterize.triggered.connect(self.doRasterize)
            self.conversionMenu.addAction(self.rasterize)

        if self.GdalVersionNum >= 1600:
            self.polygonize = QAction(QIcon(":/icons/polygonize.png"), QCoreApplication.translate("GdalTools", "Polygonize (Raster to Vector)..."), self.iface.mainWindow())
            self.polygonize.setObjectName("polygonize")
            self.polygonize.setStatusTip(QCoreApplication.translate("GdalTools", "Produces a polygon feature layer from a raster"))
            self.polygonize.triggered.connect(self.doPolygonize)
            self.conversionMenu.addAction(self.polygonize)

        self.translate = QAction(QIcon(":/icons/translate.png"), QCoreApplication.translate("GdalTools", "Translate (Convert Format)..."), self.iface.mainWindow())
        self.translate.setObjectName("translate")
        self.translate.setStatusTip(QCoreApplication.translate("GdalTools", "Converts raster data between different formats"))
        self.translate.triggered.connect(self.doTranslate)

        self.paletted = QAction(QIcon(":icons/24-to-8-bits.png"), QCoreApplication.translate("GdalTools", "RGB to PCT..."), self.iface.mainWindow())
        self.paletted.setObjectName("paletted")
        self.paletted.setStatusTip(QCoreApplication.translate("GdalTools", "Convert a 24bit RGB image to 8bit paletted"))
        self.paletted.triggered.connect(self.doPaletted)

        self.rgb = QAction(QIcon(":icons/8-to-24-bits.png"), QCoreApplication.translate("GdalTools", "PCT to RGB..."), self.iface.mainWindow())
        self.rgb.setObjectName("rgb")
        self.rgb.setStatusTip(QCoreApplication.translate("GdalTools", "Convert an 8bit paletted image to 24bit RGB"))
        self.rgb.triggered.connect(self.doRGB)

        self.conversionMenu.addActions([self.translate, self.paletted, self.rgb])

        # extraction menu (Clipper, Contour)
        self.extractionMenu = QMenu(QCoreApplication.translate("GdalTools", "Extraction"), self.iface.mainWindow())
        self.extractionMenu.setObjectName("extractionMenu")

        if self.GdalVersionNum >= 1600:
            self.contour = QAction(QIcon(":/icons/contour.png"), QCoreApplication.translate("GdalTools", "Contour..."), self.iface.mainWindow())
            self.contour.setObjectName("contour")
            self.contour.setStatusTip(QCoreApplication.translate("GdalTools", "Builds vector contour lines from a DEM"))
            self.contour.triggered.connect(self.doContour)
            self.extractionMenu.addAction(self.contour)

        self.clipper = QAction(QIcon(":icons/raster-clip.png"), QCoreApplication.translate("GdalTools", "Clipper..."), self.iface.mainWindow())
        self.clipper.setObjectName("clipper")
        #self.clipper.setStatusTip( QCoreApplication.translate( "GdalTools", "Converts raster data between different formats") )
        self.clipper.triggered.connect(self.doClipper)

        self.extractionMenu.addActions([self.clipper])

        # analysis menu (DEM (Terrain model), Grid (Interpolation), Near black, Proximity (Raster distance), Sieve)
        self.analysisMenu = QMenu(QCoreApplication.translate("GdalTools", "Analysis"), self.iface.mainWindow())
        self.analysisMenu.setObjectName("analysisMenu")

        if self.GdalVersionNum >= 1600:
            self.sieve = QAction(QIcon(":/icons/sieve.png"), QCoreApplication.translate("GdalTools", "Sieve..."), self.iface.mainWindow())
            self.sieve.setObjectName("sieve")
            self.sieve.setStatusTip(QCoreApplication.translate("GdalTools", "Removes small raster polygons"))
            self.sieve.triggered.connect(self.doSieve)
            self.analysisMenu.addAction(self.sieve)

        if self.GdalVersionNum >= 1500:
            self.nearBlack = QAction(QIcon(":/icons/nearblack.png"), QCoreApplication.translate("GdalTools", "Near Black..."), self.iface.mainWindow())
            self.nearBlack.setObjectName("nearBlack")
            self.nearBlack.setStatusTip(QCoreApplication.translate("GdalTools", "Convert nearly black/white borders to exact value"))
            self.nearBlack.triggered.connect(self.doNearBlack)
            self.analysisMenu.addAction(self.nearBlack)

        if self.GdalVersionNum >= 1700:
            self.fillNodata = QAction(QIcon(":/icons/fillnodata.png"), QCoreApplication.translate("GdalTools", "Fill nodata..."), self.iface.mainWindow())
            self.fillNodata.setObjectName("fillNodata")
            self.fillNodata.setStatusTip(QCoreApplication.translate("GdalTools", "Fill raster regions by interpolation from edges"))
            self.fillNodata.triggered.connect(self.doFillNodata)
            self.analysisMenu.addAction(self.fillNodata)

        if self.GdalVersionNum >= 1600:
            self.proximity = QAction(QIcon(":/icons/proximity.png"), QCoreApplication.translate("GdalTools", "Proximity (Raster Distance)..."), self.iface.mainWindow())
            self.proximity.setObjectName("proximity")
            self.proximity.setStatusTip(QCoreApplication.translate("GdalTools", "Produces a raster proximity map"))
            self.proximity.triggered.connect(self.doProximity)
            self.analysisMenu.addAction(self.proximity)

        if self.GdalVersionNum >= 1500:
            self.grid = QAction(QIcon(":/icons/grid.png"), QCoreApplication.translate("GdalTools", "Grid (Interpolation)..."), self.iface.mainWindow())
            self.grid.setObjectName("grid")
            self.grid.setStatusTip(QCoreApplication.translate("GdalTools", "Create raster from the scattered data"))
            self.grid.triggered.connect(self.doGrid)
            self.analysisMenu.addAction(self.grid)

        if self.GdalVersionNum >= 1700:
            self.dem = QAction(QIcon(":icons/dem.png"), QCoreApplication.translate("GdalTools", "DEM (Terrain Models)..."), self.iface.mainWindow())
            self.dem.setObjectName("dem")
            self.dem.setStatusTip(QCoreApplication.translate("GdalTools", "Tool to analyze and visualize DEMs"))
            self.dem.triggered.connect(self.doDEM)
            self.analysisMenu.addAction(self.dem)

        #self.analysisMenu.addActions( [  ] )

        # miscellaneous menu (Build overviews (Pyramids), Tile index, Information, Merge, Build Virtual Raster (Catalog))
        self.miscellaneousMenu = QMenu(QCoreApplication.translate("GdalTools", "Miscellaneous"), self.iface.mainWindow())
        self.miscellaneousMenu.setObjectName("miscellaneousMenu")

        if self.GdalVersionNum >= 1600:
            self.buildVRT = QAction(QIcon(":/icons/vrt.png"), QCoreApplication.translate("GdalTools", "Build Virtual Raster (Catalog)..."), self.iface.mainWindow())
            self.buildVRT.setObjectName("buildVRT")
            self.buildVRT.setStatusTip(QCoreApplication.translate("GdalTools", "Builds a VRT from a list of datasets"))
            self.buildVRT.triggered.connect(self.doBuildVRT)
            self.miscellaneousMenu.addAction(self.buildVRT)

        self.merge = QAction(QIcon(":/icons/merge.png"), QCoreApplication.translate("GdalTools", "Merge..."), self.iface.mainWindow())
        self.merge.setObjectName("merge")
        self.merge.setStatusTip(QCoreApplication.translate("GdalTools", "Build a quick mosaic from a set of images"))
        self.merge.triggered.connect(self.doMerge)

        self.info = QAction(QIcon(":/icons/raster-info.png"), QCoreApplication.translate("GdalTools", "Information..."), self.iface.mainWindow())
        self.info.setObjectName("info")
        self.info.setStatusTip(QCoreApplication.translate("GdalTools", "Lists information about raster dataset"))
        self.info.triggered.connect(self.doInfo)

        self.overview = QAction(QIcon(":icons/raster-overview.png"), QCoreApplication.translate("GdalTools", "Build Overviews (Pyramids)..."), self.iface.mainWindow())
        self.overview.setObjectName("overview")
        self.overview.setStatusTip(QCoreApplication.translate("GdalTools", "Builds or rebuilds overview images"))
        self.overview.triggered.connect(self.doOverview)

        self.tileindex = QAction(QIcon(":icons/tiles.png"), QCoreApplication.translate("GdalTools", "Tile Index..."), self.iface.mainWindow())
        self.tileindex.setObjectName("tileindex")
        self.tileindex.setStatusTip(QCoreApplication.translate("GdalTools", "Build a shapefile as a raster tileindex"))
        self.tileindex.triggered.connect(self.doTileIndex)

        self.miscellaneousMenu.addActions([self.merge, self.info, self.overview, self.tileindex])

        self._menuActions.append(self.menu.addMenu(self.projectionsMenu))
        self._menuActions.append(self.menu.addMenu(self.conversionMenu))
        self._menuActions.append(self.menu.addMenu(self.extractionMenu))

        if not self.analysisMenu.isEmpty():
            self._menuActions.append(self.menu.addMenu(self.analysisMenu))

        self._menuActions.append(self.menu.addMenu(self.miscellaneousMenu))

        self.settings = QAction(QCoreApplication.translate("GdalTools", "GdalTools Settings..."), self.iface.mainWindow())
        self.settings.setObjectName("settings")
        self.settings.setStatusTip(QCoreApplication.translate("GdalTools", "Various settings for Gdal Tools"))
        self.settings.triggered.connect(self.doSettings)
        self.menu.addAction(self.settings)
        self._menuActions.append(self.settings)

    def unload(self):
        if not valid:
            return
        for a in self._menuActions:
            self.menu.removeAction(a)

    def doBuildVRT(self):
        from .tools.doBuildVRT import GdalToolsDialog as BuildVRT
        d = BuildVRT(self.iface)
        self.runToolDialog(d)

    def doContour(self):
        from .tools.doContour import GdalToolsDialog as Contour
        d = Contour(self.iface)
        self.runToolDialog(d)

    def doRasterize(self):
        from .tools.doRasterize import GdalToolsDialog as Rasterize
        d = Rasterize(self.iface)
        self.runToolDialog(d)

    def doPolygonize(self):
        from .tools.doPolygonize import GdalToolsDialog as Polygonize
        d = Polygonize(self.iface)
        self.runToolDialog(d)

    def doMerge(self):
        from .tools.doMerge import GdalToolsDialog as Merge
        d = Merge(self.iface)
        self.runToolDialog(d)

    def doSieve(self):
        from .tools.doSieve import GdalToolsDialog as Sieve
        d = Sieve(self.iface)
        self.runToolDialog(d)

    def doProximity(self):
        from .tools.doProximity import GdalToolsDialog as Proximity
        d = Proximity(self.iface)
        self.runToolDialog(d)

    def doNearBlack(self):
        from .tools.doNearBlack import GdalToolsDialog as NearBlack
        d = NearBlack(self.iface)
        self.runToolDialog(d)

    def doFillNodata(self):
        from .tools.doFillNodata import GdalToolsDialog as FillNodata
        d = FillNodata(self.iface)
        self.runToolDialog(d)

    def doWarp(self):
        from .tools.doWarp import GdalToolsDialog as Warp
        d = Warp(self.iface)
        self.runToolDialog(d)

    def doGrid(self):
        from .tools.doGrid import GdalToolsDialog as Grid
        d = Grid(self.iface)
        self.runToolDialog(d)

    def doTranslate(self):
        from .tools.doTranslate import GdalToolsDialog as Translate
        d = Translate(self.iface)
        self.runToolDialog(d)

    def doInfo(self):
        from .tools.doInfo import GdalToolsDialog as Info
        d = Info(self.iface)
        self.runToolDialog(d)

    def doProjection(self):
        from .tools.doProjection import GdalToolsDialog as Projection
        d = Projection(self.iface)
        self.runToolDialog(d)

    def doOverview(self):
        from .tools.doOverview import GdalToolsDialog as Overview
        d = Overview(self.iface)
        self.runToolDialog(d)

    def doClipper(self):
        from .tools.doClipper import GdalToolsDialog as Clipper
        d = Clipper(self.iface)
        self.runToolDialog(d)

    def doPaletted(self):
        from .tools.doRgbPct import GdalToolsDialog as RgbPct
        d = RgbPct(self.iface)
        self.runToolDialog(d)

    def doRGB(self):
        from .tools.doPctRgb import GdalToolsDialog as PctRgb
        d = PctRgb(self.iface)
        self.runToolDialog(d)

    def doTileIndex(self):
        from .tools.doTileIndex import GdalToolsDialog as TileIndex
        d = TileIndex(self.iface)
        self.runToolDialog(d)

    def doExtractProj(self):
        from .tools.doExtractProj import GdalToolsDialog as ExtractProj
        d = ExtractProj(self.iface)
        d.exec_()

    def doDEM(self):
        from .tools.doDEM import GdalToolsDialog as DEM
        d = DEM(self.iface)
        self.runToolDialog(d)

    def runToolDialog(self, dlg):
        dlg.show_()
        dlg.exec_()
        del dlg

    def doSettings(self):
        from .tools.doSettings import GdalToolsSettingsDialog as Settings
        d = Settings(self.iface)
        d.exec_()
class PostNAS_Search:

    def __init__(self, iface):
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.plugin_dir,'i18n','PostNAS_Search_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Create the dialog (after translation) and keep reference
        self.dlg = PostNAS_SearchDialog(iface=self.iface)
        self.conf = PostNAS_ConfDialog(iface=self.iface)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&PostNAS_Search')

        self.searchDockWidget = None
        self.searchDockWidgetArea = Qt.LeftDockWidgetArea

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('PostNAS_Search', message)

    def initGui(self):
        # Create Conf-Action and Menuentry
        self.confAction = QAction("Einstellungen", self.iface.mainWindow())
        self.confAction.setWhatsThis("Konfiguration der PostNAS-Suche")
        self.confAction.setStatusTip("Konfiguration der PostNAS-Suche")
        self.confAction.triggered.connect(self.showConf)

        if hasattr(self.iface, "addPluginToDatabaseMenu"):
            self.iface.addPluginToDatabaseMenu("&PostNAS-Suche", self.confAction)
        else:
            self.iface.addPluginToMenu("&PostNAS-Suche", self.confAction)

        self.toggleSearchAction = QAction(u"Flurstücksuche", self.iface.mainWindow())
        self.toggleSearchAction.setWhatsThis(u"Starten/Schliessen der Flurstücksuche")
        self.toggleSearchAction.setStatusTip(u"Starten/Schliessen der Flurstücksuche")
        self.toggleSearchAction.triggered.connect(self.toggleWidget)

        if hasattr(self.iface, "addPluginToDatabaseMenu"):
            self.iface.addPluginToDatabaseMenu("&PostNAS-Suche", self.toggleSearchAction)
        else:
            self.iface.addPluginToMenu("&PostNAS-Suche", self.toggleSearchAction)

        self.fulltextindex = QAction(u"Volltextindex erstellen", self.iface.mainWindow())
        self.fulltextindex.setWhatsThis(u"Erzeugt einen Volltextindex in der Datenbank um die Suche zu beschleunigen")
        self.fulltextindex.setStatusTip(u"Erzeugt einen Volltextindex in der Datenbank um die Suche zu beschleunigen")
        self.fulltextindex.triggered.connect(self.createFulltextindex)

        if hasattr(self.iface, "addPluginToDatabaseMenu"):
            self.iface.addPluginToDatabaseMenu("&PostNAS-Suche", self.fulltextindex)
        else:
            self.iface.addPluginToMenu("&PostNAS-Suche", self.fulltextindex)

        # Create action that will start plugin configuration
        self.action = QAction(QIcon(":/plugins/PostNAS_Search/search_24x24.png"),u"Flurstücksuche", self.iface.mainWindow())
        self.action.setCheckable(True)
        # connect the action to the run method
        self.action.triggered.connect(self.toggleWidget)

        # Add toolbar button and menu item
        self.iface.addToolBarIcon(self.action)

    def toggleWidget(self, event):
        if self.searchDockWidget == None:
            self.searchDockWidget = QDockWidget(self.iface.mainWindow())
            self.searchDockWidget.setWindowTitle(self.tr(u'Suche'))
            self.searchDockWidget.setWidget(self.dlg)
            self.searchDockWidget.closeEvent = self.toggleWidget
            self.iface.addDockWidget(self.searchDockWidgetArea, self.searchDockWidget)
            self.action.setChecked(True)
        else:
            self.searchDockWidgetArea = self.iface.mainWindow().dockWidgetArea(self.searchDockWidget)
            self.iface.removeDockWidget(self.searchDockWidget)
            self.searchDockWidget = None
            self.action.setChecked(False)

    def showConf(self):
        dlg = PostNAS_ConfDialog(self)
        dlg.exec_()

    def createFulltextindex(self):
        dlg = PostNAS_CreateFulltextindex(self)
        dlg.exec_()

    def unload(self):
        # Remove the Toolbar Icon
        self.iface.removeToolBarIcon(self.action)
        # Remove DockWidget
        if self.searchDockWidget != None:
            self.iface.removeDockWidget(self.searchDockWidget)

        if hasattr(self.iface, "removePluginDatabaseMenu"):
            self.iface.removePluginDatabaseMenu("&PostNAS-Suche", self.confAction)
            self.iface.removePluginDatabaseMenu("&PostNAS-Suche", self.toggleSearchAction)
            self.iface.removePluginDatabaseMenu("&PostNAS-Suche", self.fulltextindex)
        else:
            self.iface.removePluginMenu("&PostNAS-Suche", self.confAction)
            self.iface.removePluginMenu("&PostNAS-Suche", self.toggleSearchAction)
            self.iface.removePluginMenu("&PostNAS-Suche", self.fulltextindex)

        if self.confAction:
            self.confAction.deleteLater()
            self.confAction = None

        if self.toggleSearchAction:
            self.toggleSearchAction.deleteLater()
            self.toggleSearchAction = None

        if self.fulltextindex:
            self.fulltextindex.deleteLater()
            self.fulltextindex = None
class QgisMapBiomasAPI:
    """Plugin implementation
    """
    def __init__(self, iface):
        """
        Constructor

        Args:
            iface (qgis.gui.QgisInterface): a reference to the QGIS GUI interface
        """
        self.iface = iface
        self.plugin_dir = os.path.dirname(__file__)
        self.locale = QSettings().value('locale/userLocale')[0:2]
        self.dockwidget = None
        self.action = None
        self.name = 'MapBiomas API'
        self.about = 'MapBiomas API for QGIS'
        self.token = None
        self.biome = None
        self.state = None

        # initialize locale
        locale_path = os.path.join(
            self.plugin_dir, 'i18n', '{}_{}.qm'.format('mapbiomas-api',
                                                       self.locale))
        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

    def tr(self, msg):
        return QCoreApplication.translate("QgisMapBiomasAPI", msg)

    def initProcessing(self):
        self.provider = Provider()
        QgsApplication.processingRegistry().addProvider(self.provider)

    def initGui(self):
        """
        This method is called by QGIS when the main GUI starts up or 
        when the plugin is enabled in the Plugin Manager.
        Only want to register the menu items and toolbar buttons here,
        and connects action with run method.
        """

        self.initProcessing()

        icon = QIcon(os.path.join(self.plugin_dir, 'icon.png'))
        self.action = QAction(icon, self.name, self.iface.mainWindow())
        self.action.setWhatsThis(self.about)
        self.action.setStatusTip(self.about)
        self.action.setCheckable(True)
        self.action.triggered.connect(self.run)

        # for plugin menu/toolbar
        self.iface.addToolBarIcon(self.action)
        self.iface.addPluginToMenu(self.name, self.action)

    def unload(self):
        """
        Will be executed when the plugin is disabled. 
        Either in the Plugin Manager or when QGIS shuts down.
        It removes the previously defined QAction object from
        the menu and remove the plugin icon from the toolbar.
        """

        # for plugin menu/toolbar
        self.iface.removeToolBarIcon(self.action)
        self.iface.removePluginMenu(self.name, self.action)

        if self.dockwidget is not None:
            # disconnect triggers here
            self.dockwidget.pushButton.clicked.disconnect(self.do_something)
            self.dockwidget.checkBoxStartDetected.toggled[bool].disconnect(
                self.dockwidget.startDetectedAt.setEnabled)
            self.dockwidget.checkBoxEndDetected.toggled[bool].disconnect(
                self.dockwidget.endDetectedAt.setEnabled)
            self.dockwidget.checkBoxStartPublished.toggled[bool].disconnect(
                self.dockwidget.startPublishedAt.setEnabled)
            self.dockwidget.checkBoxEndPublished.toggled[bool].disconnect(
                self.dockwidget.endPublishedAt.setEnabled)
            self.dockwidget.close()
            self.dockwidget.visibilityChanged.disconnect(
                self.visibility_changed)
            self.iface.removeDockWidget(self.dockwidget)
            del self.dockwidget

        # remove processing provider
        QgsApplication.processingRegistry().removeProvider(self.provider)

    def run(self):
        """
        Executes the custom plugin functionality.
        This method is called by the previously defined QAction object (callback), 
        which went into the toolbar icon and the menu entry.
        """
        if self.dockwidget is None:
            self.dockwidget = DockMapBiomasApi()
            self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dockwidget)
            self.dockwidget.visibilityChanged.connect(self.visibility_changed)
            self.init()
            self.dockwidget.show()
        else:
            if self.dockwidget.isVisible():
                self.dockwidget.hide()
            else:
                self.dockwidget.show()

    def visibility_changed(self, change):
        """
        Change icon checked status with dockwidget visibility
        """
        self.action.setChecked(change)

    def info(self, msg, level=Qgis.Info, duration=5):
        """
        docstring
        """
        self.iface.messageBar().pushMessage(msg, level, duration)

    def log(self, msg, level=Qgis.Info):
        """
        docstring
        """
        QgsMessageLog.logMessage(msg, self.name, level)

    def sign_in(self):
        """
        Creation of an access token in the JWT standard 
        to be used in the Authorization header and
        populate BIOME and STATE if empty
        """

        err = None

        email = self.dockwidget.email.text()
        password = self.dockwidget.password.text()

        if password and email:
            try:
                self.token, err = MapbiomasApi.token({
                    "email": email,
                    "password": password
                })
            except Exception as e:
                err = str(e)
            if self.token is not None:
                if self.biome is None:
                    self.biome, err = territories.get(self.token,
                                                      {"category": "BIOME"})
                if self.state is None:
                    self.state, err = territories.get(self.token,
                                                      {"category": "STATE"})
        else:
            err = self.tr('Email and password are required.')

        return err

    def get_alerts(self):

        filters = {}
        territories = []

        if self.dockwidget.BIOME.count:
            territories.extend(
                [self.biome[k] for k in self.dockwidget.BIOME.checkedItems()])

        if self.dockwidget.STATE.count:
            territories.extend(
                [self.state[k] for k in self.dockwidget.STATE.checkedItems()])

        if territories:
            filters["territoryIds"] = territories

        if self.dockwidget.checkBoxStartDetected.isChecked():
            filters["startDetectedAt"] = self.dockwidget.startDetectedAt.date(
            ).toString('yyyy-MM-dd')

        if self.dockwidget.checkBoxEndDetected.isChecked():
            filters["endDetectedAt"] = self.dockwidget.endDetectedAt.date(
            ).toString('yyyy-MM-dd')

        if self.dockwidget.checkBoxStartPublished.isChecked():
            filters[
                "startPublishedAt"] = self.dockwidget.startPublishedAt.date(
                ).toString('yyyy-MM-dd')

        if self.dockwidget.checkBoxEndPublished.isChecked():
            filters["endPublishedAt"] = self.dockwidget.endPublishedAt.date(
            ).toString('yyyy-MM-dd')

        try:
            data, err = publishedAlerts.get(self.token, filters)
        except Exception as e:
            err = str(e)

        if err is None:
            with NamedTemporaryFile("w+t",
                                    prefix=self.tr("alerts_"),
                                    suffix=".geojson",
                                    delete=False) as outfile:
                json.dump(data, outfile)
                fn = outfile.name
            if not "territoryIds" in filters and (len(data["features"])
                                                  == publishedAlerts.LIMIT):
                self.info(
                    self.tr('The number of alerts was limited to {}').format(
                        publishedAlerts.LIMIT))
            # add vector layer
            layer = QgsVectorLayer(fn, 'MapBiomasAPI', 'ogr')
            if layer.isValid():
                locale_style = os.path.join(
                    self.plugin_dir, 'i18n',
                    'mapbiomas-api_{}.qml'.format(self.locale))
                if os.path.exists(locale_style):
                    layer.loadNamedStyle(locale_style)
                else:
                    layer.loadNamedStyle(
                        os.path.join(self.plugin_dir, 'mapbiomas-api.qml'))
                QgsProject.instance().addMapLayer(layer)
            else:
                self.log("Invalid layer file: {}".format(fn))
                self.info(
                    self.tr("Unknown error. Invalid layer. Try it again."),
                    level=Qgis.Critical)
        elif err == 'Você não tem permissão para realizar esta ação':
            self.dockwidget.pushButton.setText(self.tr('SIGN IN'))
            self.dockwidget.options.setDisabled(True)
            self.dockwidget.mGroupBox.setCollapsed(False)
            self.info(self.tr("The session has expired. Sign in again."),
                      level=Qgis.Critical)
        else:
            self.info(err, level=Qgis.Critical)

    def do_something(self):
        """Sign in or get alerts
        """
        if self.dockwidget.pushButton.text() == self.tr('SIGN IN'):
            err = self.sign_in()
            if err is None:
                self.dockwidget.BIOME.addItems(self.biome.keys())
                self.dockwidget.STATE.addItems(self.state.keys())
                self.dockwidget.pushButton.setText(self.tr('GET ALERTS'))
                self.dockwidget.options.setEnabled(True)
                self.dockwidget.mGroupBox.setCollapsed(True)
            else:
                self.info(err, level=Qgis.Critical)
        else:
            self.get_alerts()

    def init(self):
        """
        Fill options, default values and connect triggers of the dockwidget
        """

        # connect triggers
        self.dockwidget.pushButton.clicked.connect(self.do_something)
        self.dockwidget.checkBoxStartDetected.toggled[bool].connect(
            self.dockwidget.startDetectedAt.setEnabled)
        self.dockwidget.checkBoxEndDetected.toggled[bool].connect(
            self.dockwidget.endDetectedAt.setEnabled)
        self.dockwidget.checkBoxStartPublished.toggled[bool].connect(
            self.dockwidget.startPublishedAt.setEnabled)
        self.dockwidget.checkBoxEndPublished.toggled[bool].connect(
            self.dockwidget.endPublishedAt.setEnabled)

        # set defaults
        self.dockwidget.endDetectedAt.setDate(QDate.currentDate())
        self.dockwidget.endPublishedAt.setDate(QDate.currentDate())
Example #30
0
class MetaSearchPlugin(object):

    """base plugin"""

    def __init__(self, iface):
        """init"""

        self.iface = iface
        self.context = StaticContext()
        self.action_run = None
        self.action_help = None
        self.dialog = None
        self.web_menu = '&MetaSearch'

    def initGui(self):
        """startup"""

        # run
        run_icon = QIcon('%s/%s' % (self.context.ppath,
                                    'images/MetaSearch.png'))
        self.action_run = QAction(run_icon, 'MetaSearch',
                                  self.iface.mainWindow())
        self.action_run.setWhatsThis(QCoreApplication.translate('MetaSearch',
                                                                'MetaSearch plugin'))
        self.action_run.setStatusTip(QCoreApplication.translate('MetaSearch',
                                                                'Search Metadata Catalogs'))

        self.action_run.triggered.connect(self.run)

        self.iface.addWebToolBarIcon(self.action_run)
        self.iface.addPluginToWebMenu(self.web_menu, self.action_run)

        # help
        help_icon = QgsApplication.getThemeIcon('/mActionHelpContents.svg')
        self.action_help = QAction(help_icon, 'Help', self.iface.mainWindow())
        self.action_help.setWhatsThis(QCoreApplication.translate('MetaSearch',
                                                                 'MetaSearch plugin help'))
        self.action_help.setStatusTip(QCoreApplication.translate('MetaSearch',
                                                                 'Get Help on MetaSearch'))
        self.action_help.triggered.connect(self.help)

        self.iface.addPluginToWebMenu(self.web_menu, self.action_help)

        # prefab the dialog but not open it yet
        self.dialog = MetaSearchDialog(self.iface)

    def unload(self):
        """teardown"""

        # remove the plugin menu item and icon
        self.iface.removePluginWebMenu(self.web_menu, self.action_run)
        self.iface.removePluginWebMenu(self.web_menu, self.action_help)
        self.iface.removeWebToolBarIcon(self.action_run)

    def run(self):
        """open MetaSearch"""

        self.dialog.exec_()

    def help(self):
        """open help in user's default web browser"""

        open_url(get_help_url())
Example #31
0
class QRAVE:
    """QGIS Plugin Implementation."""
    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        self.tm = QgsApplication.taskManager()
        self.qproject = QgsProject.instance()

        self.pluginIsActive = False

        self.dockwidget = None
        self.metawidget = None

        # Populated on load from a URL
        self.acknowledgements = None

        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   'QRAVE_{}.qm'.format(locale))
        self.settings = Settings(iface=self.iface)

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&Riverscapes Plugin (QRAVE)')

        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'QRAVE')
        self.toolbar.setObjectName(u'QRAVE')

    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('QRAVE', message)

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""
        self.qproject.readProject.connect(self.onProjectLoad)

        self.openAction = QAction(
            QIcon(':/plugins/qrave_toolbar/RaveAddIn_16px.png'),
            self.tr(u'Riverscapes Plugin (QRAVE)'), self.iface.mainWindow())
        self.openAction.triggered.connect(self.toggle_widget)

        self.openAction.setStatusTip('Toggle the project viewer')
        self.openAction.setWhatsThis('Toggle the project viewer')

        self.openProjectAction = QAction(
            QIcon(':/plugins/qrave_toolbar/OpenProject.png'),
            self.tr(u'Open Riverscapes Project'), self.iface.mainWindow())
        self.openProjectAction.triggered.connect(self.projectBrowserDlg)

        self.openProjectAction.setStatusTip('Open QRAVE project')
        self.openProjectAction.setWhatsThis('Open QRAVE project')

        self.helpButton = QToolButton()
        self.helpButton.setToolButtonStyle(Qt.ToolButtonTextOnly)
        self.helpButton.setMenu(QMenu())
        self.helpButton.setPopupMode(QToolButton.MenuButtonPopup)

        m = self.helpButton.menu()

        # TODO: get the local help working
        # self.helpAction = QAction(
        #     QIcon(':/plugins/qrave_toolbar/Help.png'),
        #     self.tr('Help'),
        #     self.iface.mainWindow()
        # )
        # self.helpAction.triggered.connect(partial(showPluginHelp, None, filename=':/plugins/qrave_toolbar/help/build/html/index'))
        # self.websiteAction = QAction(
        #     QIcon(':/plugins/qrave_toolbar/RaveAddIn_16px.png'),
        #     self.tr('Website'),
        #     self.iface.mainWindow()
        # )
        # self.websiteAction.triggered.connect(lambda: QDesktopServices.openUrl(QUrl("http://rave.riverscapes.xyz")))

        self.helpAction = QAction(QIcon(':/plugins/qrave_toolbar/Help.png'),
                                  self.tr('Help'), self.iface.mainWindow())
        self.helpAction.triggered.connect(lambda: QDesktopServices.openUrl(
            QUrl("http://rave.riverscapes.xyz")))

        self.raveOptionsAction = QAction(
            QIcon(':/plugins/qrave_toolbar/Options.png'), self.tr('Settings'),
            self.iface.mainWindow())
        self.raveOptionsAction.triggered.connect(self.options_load)

        self.net_sync_action = QAction(
            QIcon(':/plugins/qrave_toolbar/refresh.png'),
            self.tr('Update resources'), self.iface.mainWindow())
        self.net_sync_action.triggered.connect(
            lambda: self.net_sync_load(force=True))

        self.find_resources_action = QAction(
            QIcon(':/plugins/qrave_toolbar/BrowseFolder.png'),
            self.tr('Find Resources folder'), self.iface.mainWindow())
        self.find_resources_action.triggered.connect(self.locateResources)

        self.about_action = QAction(
            QIcon(':/plugins/qrave_toolbar/RaveAddIn_16px.png'),
            self.tr('About QRAVE'), self.iface.mainWindow())
        self.about_action.triggered.connect(self.about_load)

        m.addAction(self.helpAction)
        # m.addAction(self.websiteAction)
        m.addAction(self.raveOptionsAction)
        m.addAction(self.net_sync_action)
        m.addSeparator()
        m.addAction(self.find_resources_action)
        m.addAction(self.about_action)
        self.helpButton.setDefaultAction(self.helpAction)

        self.toolbar.addAction(self.openAction)
        self.toolbar.addAction(self.openProjectAction)
        self.toolbar.addWidget(self.helpButton)

        # Do a check to see if the stored version is different than the current version
        lastVersion = self.settings.getValue('pluginVersion')

        # This does a lazy netsync (i.e. it will run it if it feels like it)
        versionChange = lastVersion != __version__
        self.net_sync_load(force=versionChange)

        if versionChange:
            QgsMessageLog.logMessage(
                "Version change detected: {} ==> {}".format(
                    lastVersion, __version__),
                'QRAVE',
                level=Qgis.Info)
            self.settings.setValue('pluginVersion', __version__)

    def onProjectLoad(self, doc):
        # If the project has the plugin enabled then restore it.
        qrave_enabled, type_conversion_ok = self.qproject.readEntry(
            CONSTANTS['settingsCategory'], 'enabled')
        if type_conversion_ok and qrave_enabled == '1':
            self.toggle_widget(forceOn=True)
            if self.dockwidget is not None:
                self.dockwidget.reload_tree()

    def onClosePlugin(self):
        """Cleanup necessary items here when plugin dockwidget is closed"""
        if self.metawidget is not None:
            self.metawidget.hide()
        if self.dockwidget is not None:
            self.dockwidget.hide()

        # disconnects
        self.dockwidget.closingPlugin.disconnect(self.onClosePlugin)
        self.qproject.readProject.disconnect(self.onProjectLoad)
        # remove this statement if dockwidget is to remain
        # for reuse if plugin is reopened
        self.dockwidget = None

        self.pluginIsActive = False

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        if self.metawidget is not None:
            self.metawidget.hide()
        if self.dockwidget is not None:
            self.dockwidget.hide()

        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&Riverscapes Plugin (QRAVE)'), action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    def toggle_widget(self, forceOn=False):
        """Toggle the widget open and closed when clicking the toolbar"""
        if not self.pluginIsActive:
            self.pluginIsActive = True

            # dockwidget may not exist if:
            #    first run of plugin
            #    removed on close (see self.onClosePlugin method)
            if self.dockwidget is None:
                # Create the dockwidget (after translation) and keep reference
                self.dockwidget = QRAVEDockWidget()
                self.metawidget = QRAVEMetaWidget()
                # Hook metadata changes up to the metawidget
                self.dockwidget.metaChange.connect(self.metawidget.load)

                # Run a network sync operation to get the latest stuff. Don't force it.
                #  This is just a quick check
                self.net_sync_load()

            # connect to provide cleanup on closing of dockwidget
            self.dockwidget.closingPlugin.connect(self.onClosePlugin)

            # show the dockwidget
            self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dockwidget)
            self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.metawidget)
            self.dockwidget.show()

        else:
            if self.dockwidget is not None:
                if self.dockwidget.isHidden():
                    self.dockwidget.show()
                elif forceOn is False:
                    self.dockwidget.hide()

        # The metawidget always starts hidden
        if self.metawidget is not None:
            self.metawidget.hide()

        if self.dockwidget is not None and not self.dockwidget.isHidden():
            self.qproject.writeEntry(CONSTANTS['settingsCategory'], 'enabled',
                                     True)
        else:
            self.qproject.removeEntry(CONSTANTS['settingsCategory'], 'enabled')

    def net_sync_load(self, force=False):
        """
        Periodically check for new files
        """

        lastDigestSync = self.settings.getValue('lastDigestSync')
        lastVersion = self.settings.getValue('pluginVersion')

        currTime = int(time())  # timestamp in seconds
        plugin_init = self.settings.getValue('initialized')
        autoUpdate = self.settings.getValue('autoUpdate')

        self.netsync = NetSync('Sync QRAVE resource files')

        perform_sync = False

        # setting the force flag overrides everything else
        if force:
            perform_sync = True

        # Otherwise you only get a sync if 'autoUpdate' is turned on
        elif autoUpdate:
            # If this is an old version or the plugin is uninitialized
            if not plugin_init or lastVersion != __version__ or self.netsync.need_sync:
                perform_sync = True
            # If we haven't checked for more than `digestSyncFreqHours` hours
            elif isinstance(lastDigestSync, int) \
                    and ((currTime - lastDigestSync) / 3600) > CONSTANTS['digestSyncFreqHours']:
                perform_sync = True

        if perform_sync is False:
            self.netsync = None
            return

        # Trigger the dockwidget to repaint after the netsync
        if self.dockwidget is not None:
            self.netsync.taskCompleted.connect(self.dockwidget.reload_tree)

        # FOR DEBUGGING ONLY. NEVER IN PRODUCTION
        # self.netsync.run()

        # COMMENT THIS OUT AND USE THE LINE ABOVE FOR SYNCHRONOUS DEBUGGING
        self.tm.addTask(self.netsync)

    def projectBrowserDlg(self):
        """
        Browse for a project directory
        :return:
        """
        last_browse_path = self.settings.getValue('lastBrowsePath')
        last_dir = os.path.dirname(
            last_browse_path) if last_browse_path is not None else None

        dialog_return = QFileDialog.getOpenFileName(
            self.dockwidget, "Open a Riverscapes project", last_dir,
            self.tr("Riverscapes Project files (project.rs.xml)"))
        if dialog_return is not None and dialog_return[
                0] != "" and os.path.isfile(dialog_return[0]):
            # We set the proect path in the project settings. This way it will be saved with the QgsProject file
            if self.dockwidget is None or self.dockwidget.isHidden() is True:
                self.toggle_widget(forceOn=True)
            self.dockwidget.add_project(dialog_return[0])

    def locateResources(self):
        """This the OS-agnostic "show in Finder" or "show in explorer" equivalent
        It should open the folder of the item in question

        Args:
            fpath (str): [description]
        """
        qurl = QUrl.fromLocalFile(RESOURCES_DIR)
        QDesktopServices.openUrl(qurl)

    def options_load(self):
        """
        Open the options/settings dialog
        """
        dialog = OptionsDialog()
        if self.dockwidget:
            dialog.dataChange.connect(self.dockwidget.dataChange)
        dialog.exec_()

    def about_load(self):
        """
        Open the About dialog
        """
        dialog = AboutDialog()
        if self.acknowledgements is None:
            self.acknowledgements = requests.get(
                'http://rave.riverscapes.xyz/dotnetack.html').text

        dialog.acknowledgements.setText(self.acknowledgements)
        dialog.exec_()