class ConflictDialog(WIDGET, BASE):

    LOCAL, REMOTE, DELETE = 1,2, 3

    def __init__(self, conflicts):
        super(ConflictDialog, self).__init__(None, Qt.WindowSystemMenuHint | Qt.WindowTitleHint)
        self.solved = False
        self.resolvedConflicts = {}
        self.conflicts = conflicts
        self.setupUi(self)

        self.setWindowFlags(self.windowFlags() |
                              Qt.WindowSystemMenuHint |
                              Qt.WindowMinMaxButtonsHint)

        self.zoomButton.clicked.connect(self.zoomToFullExtent)
        self.solveButton.clicked.connect(self.solve)
        self.conflictsTree.itemClicked.connect(self.treeItemClicked)
        self.attributesTable.cellClicked.connect(self.cellClicked)
        self.solveAllLocalButton.clicked.connect(self.solveAllLocal)
        self.solveAllRemoteButton.clicked.connect(self.solveAllRemote)
        self.solveLocalButton.clicked.connect(self.solveLocal)
        self.solveRemoteButton.clicked.connect(self.solveRemote)

        self.showRemoteCheck.stateChanged.connect(self.showGeoms)
        self.showLocalCheck.stateChanged.connect(self.showGeoms)

        self.lastSelectedItem = None
        self.currentPath = None
        self.currentConflict = None
        self.theirsLayer = None
        self.oursLayer = None

        settings = QSettings()
        horizontalLayout = QHBoxLayout()
        horizontalLayout.setSpacing(0)
        horizontalLayout.setMargin(0)
        self.mapCanvas = QgsMapCanvas()
        self.mapCanvas.setCanvasColor(Qt.white)
        self.mapCanvas.enableAntiAliasing(settings.value("/qgis/enable_anti_aliasing", False, type = bool))
        self.mapCanvas.useImageToRender(settings.value("/qgis/use_qimage_to_render", False, type = bool))
        self.mapCanvas.mapRenderer().setProjectionsEnabled(True)
        action = settings.value("/qgis/wheel_action", 0, type = float)
        zoomFactor = settings.value("/qgis/zoom_factor", 2, type = float)
        self.mapCanvas.setWheelAction(QgsMapCanvas.WheelAction(action), zoomFactor)
        horizontalLayout.addWidget(self.mapCanvas)
        self.canvasWidget.setLayout(horizontalLayout)
        self.panTool = QgsMapToolPan(self.mapCanvas)
        self.mapCanvas.setMapTool(self.panTool)

        self.solveButton.setEnabled(False)
        self.solveLocalButton.setEnabled(False)
        self.solveRemoteButton.setEnabled(False)

        self.fillConflictsTree()

    def fillConflictsTree(self):
        topTreeItems = {}
        for c in self.conflicts:
            path = os.path.dirname(c.path)
            if path in topTreeItems:
                topItem = topTreeItems[path]
            else:
                topItem = QTreeWidgetItem()
                topItem.setText(0, path)
                topItem.setIcon(0, layerIcon)
                topTreeItems[path] = topItem
            conflictItem = ConflictItem(c)
            topItem.addChild(conflictItem)
        for item in list(topTreeItems.values()):
            self.conflictsTree.addTopLevelItem(item)

    def cellClicked(self, row, col):
        if col > 2:
            return
        value = self.attributesTable.item(row, col).value
        geoms = (self.oursgeom, self.theirsgeom)
        self.attributesTable.setItem(row, 4, ValueItem(value, False, geoms));
        self.attributesTable.item(row, 0).setBackgroundColor(Qt.white);
        self.attributesTable.item(row, 1).setBackgroundColor(Qt.white);
        self.attributesTable.item(row, 2).setBackgroundColor(Qt.white);
        attrib = self.attributesTable.item(row, 3).text()
        if attrib in self.conflicted:
            self.conflicted.remove(attrib)
        self.updateSolveButton()

        self.showGeoms()

    def treeItemClicked(self):
        item = self.conflictsTree.selectedItems()[0]
        if self.lastSelectedItem == item:
            return
        if isinstance(item, ConflictItem):
            self.lastSelectedItem = item
            self.currentPath = item.conflict.path
            self.updateCurrentPath()
            self.solveLocalButton.setEnabled(True)
            self.solveRemoteButton.setEnabled(True)
            self.solveButton.setEnabled(False)

    def updateCurrentPath(self):
        self.solveButton.setEnabled(False)
        self.solveLocalButton.setEnabled(False)
        self.solveRemoteButton.setEnabled(False)
        self.cleanCanvas()
        self.showFeatureAttributes()
        self.createLayers()
        self.showGeoms()
        self.zoomToFullExtent()

    def zoomToFullExtent(self):
        layers = [lay.extent() for lay in self.mapCanvas.layers() if lay.type() == lay.VectorLayer]
        if layers:
            ext = layers[0]
            for layer in  layers[1:]:
                ext.combineExtentWith(layer)
            self.mapCanvas.setExtent(ext)
            self.mapCanvas.refresh()

    def cleanCanvas(self):
        self.mapCanvas.setLayerSet([])
        layers = [self.oursLayer, self.theirsLayer]
        for layer in layers:
            if layer is not None:
                QgsMapLayerRegistry.instance().removeMapLayer(layer.id())
        self.oursLayer = None
        self.theirsLayer = None

    def solveAllRemote(self):
        ret = QMessageBox.warning(self, "Solve conflict",
                                "Are you sure you want to solve all conflicts using the 'To merge' version?",
                                QMessageBox.Yes | QMessageBox.No,
                                QMessageBox.Yes);
        if ret == QMessageBox.Yes:
            self.solved = True
            self.resolvedConflicts = {c.path:self.REMOTE for c in self.conflicts}
            self.close()

    def solveAllLocal(self):
        ret = QMessageBox.warning(self, "Solve conflict",
            "Are you sure you want to solve all conflict using the 'Local' version?",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.Yes);
        if ret == QMessageBox.Yes:
            self.solved = True
            self.resolvedConflicts = {c.path:self.LOCAL for c in self.conflicts}
            self.close()


    def _afterSolve(self, remove = True):
        if remove:
            parent = self.lastSelectedItem.parent()
            parent.removeChild(self.lastSelectedItem)
            self.lastSelectedItem = None
            if parent.childCount() == 0:
                self.conflictsTree.invisibleRootItem().removeChild(parent)
                if self.conflictsTree.topLevelItemCount() == 0:
                    self.solved = True
                    self.close()

        self.attributesTable.setRowCount(0)
        self.cleanCanvas()
        self.solveButton.setEnabled(False)
        self.solveLocalButton.setEnabled(False)
        self.solveRemoteButton.setEnabled(False)

    def solveLocal(self):
        self.resolvedConflicts[self.currentPath] = self.LOCAL
        self._afterSolve()

    def solveRemote(self):
        self.resolvedConflicts[self.currentPath] = self.REMOTE
        self._afterSolve()

    def solve(self):
        attribs = {}
        for i in range(self.attributesTable.rowCount()):
            value = self.attributesTable.item(i, 4).value
            name = str(self.attributesTable.item(i, 3).text())
            attribs[name] = value
        self.resolvedConflicts[self.currentPath] = attribs
        self._afterSolve()


    def updateSolveButton(self):
        self.solveButton.setEnabled(len(self.conflicted) == 0)

    def showFeatureAttributes(self):
        conflictItem = self.lastSelectedItem
        self.oursgeom = None
        self.theirsgeom = None
        geoms = (self.oursgeom, self.theirsgeom)
        self.currentConflictedAttributes = []
        attribs = list(conflictItem.origin.keys())
        self.attributesTable.setRowCount(len(attribs))

        self.conflicted = []
        for idx, name in enumerate(attribs):
            font = QFont()
            font.setBold(True)
            font.setWeight(75)
            item = QTableWidgetItem(name)
            item.setFont(font)
            self.attributesTable.setItem(idx, 3, item);

            self.attributesTable.setItem(idx, 4, ValueItem(None, False));

            try:
                values = (conflictItem.origin[name], conflictItem.local[name], conflictItem.remote[name])
            except Exception: #Local has been deleted
                self._afterSolve(False)
                self.solveModifyAndDelete(conflictItem.conflict.path, self.REMOTE)
                return
            except TypeError: #Remote has been deleted
                self._afterSolve(False)
                self.solveModifyAndDelete(conflictItem.conflict.path,self.LOCAL)
                return
            try:
                geom = QgsGeometry.fromWkt(values[0])
            except:
                geom = None
            if geom is not None:
                self.theirsgeom = QgsGeometry().fromWkt(values[1])
                self.oursgeom = QgsGeometry.fromWkt(values[2])
                geoms = (self.oursgeom, self.theirsgeom)

            ok = values[0] == values[1] or values[1] == values[2] or values[0] == values[2]

            for i, v in enumerate(values):
                self.attributesTable.setItem(idx, i, ValueItem(v, not ok, geoms));

            if not ok:
                self.conflicted.append(name)
            else:
                if values[0] == values[1]:
                    newvalue = values[2]
                else:
                    newvalue = values[1]
                self.attributesTable.setItem(idx, 4, ValueItem(newvalue, False, geoms));

        self.attributesTable.resizeRowsToContents()
        self.attributesTable.horizontalHeader().setMinimumSectionSize(150)
        self.attributesTable.horizontalHeader().setStretchLastSection(True)


    def solveModifyAndDelete(self, path, modified):
        msgBox = QMessageBox()
        msgBox.setText("The feature has been modified in one version and deleted in the other one.\n"
                       "How do you want to solve the conflict?")
        msgBox.addButton(QPushButton('Modify'), QMessageBox.YesRole)
        msgBox.addButton(QPushButton('Delete'), QMessageBox.NoRole)
        msgBox.addButton(QPushButton('Cancel'), QMessageBox.RejectRole)
        ret = msgBox.exec_()
        if ret == 0:
            self.resolvedConflicts[path] = modified
            self._afterSolve()
        elif ret == 1:
            self.resolvedConflicts[path] = self.DELETE
            self._afterSolve()
        else:
            pass

    def createLayers(self):
        types = [("Point", ptOursStyle, ptTheirsStyle),
                  ("LineString", lineOursStyle, lineTheirsStyle),
                  ("Polygon", polygonOursStyle, polygonTheirsStyle)]
        if self.oursgeom is not None:
            geomtype = types[int(self.oursgeom.type())][0]
            style = types[int(self.oursgeom.type())][1]
            self.oursLayer = loadLayerNoCrsDialog(geomtype + "?crs=EPSG:4326", "ours", "memory")
            pr = self.oursLayer.dataProvider()
            feat = QgsFeature()
            feat.setGeometry(self.oursgeom)
            pr.addFeatures([feat])
            self.oursLayer.loadNamedStyle(style)
            self.oursLayer.updateExtents()
            QgsMapLayerRegistry.instance().addMapLayer(self.oursLayer, False)
        else:
            self.oursLayer = None
        if self.theirsgeom is not None:
            geomtype = types[int(self.theirsgeom.type())][0]
            style = types[int(self.theirsgeom.type())][2]
            self.theirsLayer = loadLayerNoCrsDialog(geomtype + "?crs=EPSG:4326", "theirs", "memory")
            pr = self.theirsLayer.dataProvider()
            feat = QgsFeature()
            feat.setGeometry(self.theirsgeom)
            pr.addFeatures([feat])
            self.theirsLayer.loadNamedStyle(style)
            self.theirsLayer.updateExtents()
            QgsMapLayerRegistry.instance().addMapLayer(self.theirsLayer, False)
        else:
            self.theirsLayer = None

    def showGeoms(self):
        checks = [self.showRemoteCheck, self.showLocalCheck]
        layers = [self.oursLayer, self.theirsLayer]
        toShow = []
        for lay, chk in zip(layers, checks):
            if lay is not None and chk.isChecked():
                toShow.append(lay)
        self.mapCanvas.setRenderFlag(False)
        self.mapCanvas.setLayerSet([QgsMapCanvasLayer(layer) for layer in toShow])
        self.mapCanvas.setRenderFlag(True)


    def closeEvent(self, evnt):
        if not self.solved:
            ret = QMessageBox.warning(self, "Conflict resolution",
                                  "There are unsolved conflicts.\n"
                                  "Do you really want to exit and abort the sync operation?",
                                  QMessageBox.Yes | QMessageBox.No)
            if ret == QMessageBox.No:
                evnt.ignore()
                return

        self.cleanCanvas()
예제 #2
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.first_flag = True
        self.setWindowTitle('PyQGIS')
        # 调整窗口大小
        self.resize(800, 600)
        # 初始化图层树
        vl = QVBoxLayout(self.dockWidgetContents)
        self.layerTreeView = QgsLayerTreeView(self)
        vl.addWidget(self.layerTreeView)
        # 初始化地图画布
        self.mapCanvas = QgsMapCanvas(self)
        hl = QHBoxLayout(self.frame)
        hl.setContentsMargins(0, 0, 0, 0)
        hl.addWidget(self.mapCanvas)

        # 建立桥梁
        self.model = QgsLayerTreeModel(PROJECT.layerTreeRoot(), self)
        self.model.setFlag(QgsLayerTreeModel.AllowNodeRename)
        self.model.setFlag(QgsLayerTreeModel.AllowNodeReorder)
        self.model.setFlag(QgsLayerTreeModel.AllowNodeChangeVisibility)
        self.model.setFlag(QgsLayerTreeModel.ShowLegendAsTree)
        self.model.setAutoCollapseLegendNodes(10)
        self.layerTreeView.setModel(self.model)
        self.layerTreeBridge = QgsLayerTreeMapCanvasBridge(
            PROJECT.layerTreeRoot(), self.mapCanvas, self)
        # 显示经纬度
        self.mapCanvas.xyCoordinates.connect(self.showLngLat)

        # 打开工程
        self.actionOpen.triggered.connect(self.actionOpenTriggered)
        # 退出程序
        self.actionQuit.triggered.connect(self.close)

        # 地图工具
        # TODO:放大、缩小没有图标
        self.actionPanTriggered()
        self.actionPan.triggered.connect(self.actionPanTriggered)
        self.actionZoomin.triggered.connect(self.actionZoomInTriggered)
        self.actionZoomout.triggered.connect(self.actionZoomOutTriggered)
        self.actionIdentity.triggered.connect(self.actionIdentifyTriggered)

        # 图层
        self.actionShapefile.triggered.connect(self.actionShapefileTriggered)
        self.actionCsv.triggered.connect(self.actionCsvTriggered)
        self.actionPostGIS.triggered.connect(self.actionPostGISTriggered)
        self.actionWFS.triggered.connect(self.actionWFSTriggered)

        self.actionGeotiff.triggered.connect(self.actionGeotiffTriggered)
        self.actionXYZ.triggered.connect(self.actionXYZTriggered)

        # 绘图工具
        self.actionPoint.triggered.connect(self.actionPointTriggered)
        self.actionLine.triggered.connect(self.actionLineTriggered)
        self.actionRectangle.triggered.connect(self.actionRectangleTriggered)
        self.actionPolygon.triggered.connect(self.actionPolygonTriggered)

        # 关于Qt
        self.actionAboutQt.triggered.connect(
            lambda: QMessageBox.aboutQt(self, '关于Qt'))
        self.actionAbout.triggered.connect(
            lambda: QMessageBox.about(self, '关于', 'PyQGIS二次开发'))

        # self.actionPan.triggered.connect(self.actionPanTriggered)
        # self.actionIdentify.triggered.connect(self.actionIdentifyTriggered)

        # 图层右键菜单
        self.customMenuProvider = CustomMenuProvider(self.layerTreeView,
                                                     self.mapCanvas)
        self.layerTreeView.setMenuProvider(self.customMenuProvider)
        # self.layerTreeRegistryBridge = QgsLayerTreeRegistryBridge(PROJECT.layerTreeRoot(), PROJECT, self)

    def actionOpenTriggered(self):
        """打开工程"""
        data_file, ext = QFileDialog.getOpenFileName(self, '打开', '',
                                                     '工程文件(*.qgs , *.qgz)')
        if data_file:
            PROJECT.read(data_file)

    def actionPanTriggered(self):
        self.mapTool = QgsMapToolPan(self.mapCanvas)
        self.mapCanvas.setMapTool(self.mapTool)

    def actionZoomInTriggered(self):
        self.mapTool = QgsMapToolZoom(self.mapCanvas, False)
        self.mapCanvas.setMapTool(self.mapTool)

    def actionZoomOutTriggered(self):
        self.mapTool = QgsMapToolZoom(self.mapCanvas, True)
        self.mapCanvas.setMapTool(self.mapTool)

    def actionIdentifyTriggered(self):
        # 设置识别工具
        self.identifyTool = QgsMapToolIdentifyFeature(self.mapCanvas)
        self.identifyTool.featureIdentified.connect(self.showFeatures)
        self.mapCanvas.setMapTool(self.identifyTool)

        # 设置需要识别的图层
        layers = self.mapCanvas.layers()
        if layers:
            # 识别画布中第一个图层
            self.identifyTool.setLayer(layers[0])

    def showFeatures(self, feature):
        print(type(feature))

        QMessageBox.information(self, '信息', ''.join(feature.attributes()))

    def actionAddGroupTriggered(self):
        PROJECT.layerTreeRoot().addGroup('group1')

    def actionShapefileTriggered(self):
        """打开shp"""
        data_file, ext = QFileDialog.getOpenFileName(self, '打开', '', '*.shp')
        if data_file:
            layer = QgsVectorLayer(
                data_file,
                os.path.splitext(os.path.basename(data_file))[0], "ogr")
            self.addLayer(layer)

    def actionCsvTriggered(self):
        """加载csv数据"""
        data_file, ext = QFileDialog.getOpenFileName(self, '打开', '', '*.csv')
        if data_file:
            # 去掉盘符,否则图层无效
            data_file = os.path.splitdrive(data_file)[1]
            uri = f"file://{data_file}?delimiter=,&xField=x&yField=y"
            print(uri)
            layer = QgsVectorLayer(uri, "point", "delimitedtext")
            self.addLayer(layer)

    def actionPostGISTriggered(self):
        """加载postgis图层"""
        dialog = PostGISDialog(self)
        if dialog.exec_():
            uri = QgsDataSourceUri()
            uri.setConnection(dialog.lineEditHost.text(),
                              dialog.lineEditPort.text(),
                              dialog.lineEditDatabase.text(),
                              dialog.lineEditUsername.text(),
                              dialog.lineEditPassword.text())
            # lineEditGeometryColumn:根据实际情况,可能为:wkb_geometry、geometry、the_geom...
            uri.setDataSource("public", dialog.lineEditLayer.text(),
                              dialog.lineEditGeometryColumn.text())

            layer = QgsVectorLayer(uri.uri(False), dialog.lineEditLayer.text(),
                                   "postgres")
            self.addLayer(layer)

    def actionWFSTriggered(self):
        """加载天地图WFS图层"""
        uri = 'http://gisserver.tianditu.gov.cn/TDTService/wfs?' \
              'srsname=EPSG:4326&typename=TDTService:RESA&version=auto&request=GetFeature&service=WFS'
        layer = QgsVectorLayer(uri, "RESA", "WFS")
        self.addLayer(layer)

    def actionGeotiffTriggered(self):
        """加载geotiff"""
        data_file, ext = QFileDialog.getOpenFileName(self, '打开', '', '*.tif')
        if data_file:
            layer = QgsRasterLayer(data_file, os.path.basename(data_file))
            self.addLayer(layer)

    def actionXYZTriggered(self):
        uri = 'type=xyz&' \
              'url=https://www.google.cn/maps/vt?lyrs=s@804%26gl=cn%26x={x}%26y={y}%26z={z}&' \
              'zmax=19&' \
              'zmin=0&' \
              'crs=EPSG3857'
        layer = QgsRasterLayer(uri, 'google', 'wms')
        self.addLayer(layer)

    def addLayer(self, layer):
        if layer.isValid():
            if self.first_flag:
                self.mapCanvas.setDestinationCrs(layer.crs())
                self.mapCanvas.setExtent(layer.extent())
                self.first_flag = False
            PROJECT.addMapLayer(layer)
            layers = [layer
                      ] + [PROJECT.mapLayer(i) for i in PROJECT.mapLayers()]
            self.mapCanvas.setLayers(layers)
            self.mapCanvas.refresh()
        else:
            print('图层无效.')

    def actionPointTriggered(self):
        self.pointTool = PointMapTool(self.mapCanvas)
        self.mapCanvas.setMapTool(self.pointTool)

    def actionLineTriggered(self):
        self.lineTool = LineMapTool(self.mapCanvas)
        self.mapCanvas.setMapTool(self.lineTool)

    def actionRectangleTriggered(self):
        self.rectangleTool = RectangleMapTool(self.mapCanvas)
        self.mapCanvas.setMapTool(self.rectangleTool)

    def actionPolygonTriggered(self):
        self.polygonTool = PolygonMapTool(self.mapCanvas)
        self.mapCanvas.setMapTool(self.polygonTool)

    def showLngLat(self, point):
        x = point.x()
        y = point.y()
        self.statusbar.showMessage(f'经度:{x}, 纬度:{y}')
예제 #3
0
class ConflictDialog(WIDGET, BASE):

    LOCAL, REMOTE, DELETE = 1, 2, 3

    def __init__(self, conflicts):
        super(ConflictDialog,
              self).__init__(None,
                             Qt.WindowSystemMenuHint | Qt.WindowTitleHint)
        self.solved = False
        self.resolvedConflicts = {}
        self.conflicts = conflicts
        self.setupUi(self)

        self.setWindowFlags(self.windowFlags() | Qt.WindowSystemMenuHint
                            | Qt.WindowMinMaxButtonsHint)

        self.zoomButton.clicked.connect(self.zoomToFullExtent)
        self.solveButton.clicked.connect(self.solve)
        self.conflictsTree.itemClicked.connect(self.treeItemClicked)
        self.attributesTable.cellClicked.connect(self.cellClicked)
        self.solveAllLocalButton.clicked.connect(self.solveAllLocal)
        self.solveAllRemoteButton.clicked.connect(self.solveAllRemote)
        self.solveLocalButton.clicked.connect(self.solveLocal)
        self.solveRemoteButton.clicked.connect(self.solveRemote)

        self.showRemoteCheck.stateChanged.connect(self.showGeoms)
        self.showLocalCheck.stateChanged.connect(self.showGeoms)

        self.lastSelectedItem = None
        self.currentPath = None
        self.currentConflict = None
        self.theirsLayer = None
        self.oursLayer = None

        settings = QSettings()
        horizontalLayout = QHBoxLayout()
        horizontalLayout.setSpacing(0)
        horizontalLayout.setMargin(0)
        self.mapCanvas = QgsMapCanvas()
        self.mapCanvas.setCanvasColor(Qt.white)
        self.mapCanvas.enableAntiAliasing(
            settings.value("/qgis/enable_anti_aliasing", False, type=bool))
        self.mapCanvas.useImageToRender(
            settings.value("/qgis/use_qimage_to_render", False, type=bool))
        self.mapCanvas.mapRenderer().setProjectionsEnabled(True)
        action = settings.value("/qgis/wheel_action", 0, type=float)
        zoomFactor = settings.value("/qgis/zoom_factor", 2, type=float)
        self.mapCanvas.setWheelAction(QgsMapCanvas.WheelAction(action),
                                      zoomFactor)
        horizontalLayout.addWidget(self.mapCanvas)
        self.canvasWidget.setLayout(horizontalLayout)
        self.panTool = QgsMapToolPan(self.mapCanvas)
        self.mapCanvas.setMapTool(self.panTool)

        self.solveButton.setEnabled(False)
        self.solveLocalButton.setEnabled(False)
        self.solveRemoteButton.setEnabled(False)

        self.fillConflictsTree()

    def fillConflictsTree(self):
        topTreeItems = {}
        for c in self.conflicts:
            path = os.path.dirname(c.path)
            if path in topTreeItems:
                topItem = topTreeItems[path]
            else:
                topItem = QTreeWidgetItem()
                topItem.setText(0, path)
                topItem.setIcon(0, layerIcon)
                topTreeItems[path] = topItem
            conflictItem = ConflictItem(c)
            topItem.addChild(conflictItem)
        for item in list(topTreeItems.values()):
            self.conflictsTree.addTopLevelItem(item)

    def cellClicked(self, row, col):
        if col > 2:
            return
        value = self.attributesTable.item(row, col).value
        geoms = (self.oursgeom, self.theirsgeom)
        self.attributesTable.setItem(row, 4, ValueItem(value, False, geoms))
        self.attributesTable.item(row, 0).setBackgroundColor(Qt.white)
        self.attributesTable.item(row, 1).setBackgroundColor(Qt.white)
        self.attributesTable.item(row, 2).setBackgroundColor(Qt.white)
        attrib = self.attributesTable.item(row, 3).text()
        if attrib in self.conflicted:
            self.conflicted.remove(attrib)
        self.updateSolveButton()

        self.showGeoms()

    def treeItemClicked(self):
        item = self.conflictsTree.selectedItems()[0]
        if self.lastSelectedItem == item:
            return
        if isinstance(item, ConflictItem):
            self.lastSelectedItem = item
            self.currentPath = item.conflict.path
            self.updateCurrentPath()
            self.solveLocalButton.setEnabled(True)
            self.solveRemoteButton.setEnabled(True)
            self.solveButton.setEnabled(False)

    def updateCurrentPath(self):
        self.solveButton.setEnabled(False)
        self.solveLocalButton.setEnabled(False)
        self.solveRemoteButton.setEnabled(False)
        self.cleanCanvas()
        self.showFeatureAttributes()
        self.createLayers()
        self.showGeoms()
        self.zoomToFullExtent()

    def zoomToFullExtent(self):
        layers = [
            lay.extent() for lay in self.mapCanvas.layers()
            if lay.type() == lay.VectorLayer
        ]
        if layers:
            ext = layers[0]
            for layer in layers[1:]:
                ext.combineExtentWith(layer)
            self.mapCanvas.setExtent(ext)
            self.mapCanvas.refresh()

    def cleanCanvas(self):
        self.mapCanvas.setLayerSet([])
        layers = [self.oursLayer, self.theirsLayer]
        for layer in layers:
            if layer is not None:
                QgsMapLayerRegistry.instance().removeMapLayer(layer.id())
        self.oursLayer = None
        self.theirsLayer = None

    def solveAllRemote(self):
        ret = QMessageBox.warning(
            self, "Solve conflict",
            "Are you sure you want to solve all conflicts using the 'To merge' version?",
            QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
        if ret == QMessageBox.Yes:
            self.solved = True
            self.resolvedConflicts = {
                c.path: self.REMOTE
                for c in self.conflicts
            }
            self.close()

    def solveAllLocal(self):
        ret = QMessageBox.warning(
            self, "Solve conflict",
            "Are you sure you want to solve all conflict using the 'Local' version?",
            QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
        if ret == QMessageBox.Yes:
            self.solved = True
            self.resolvedConflicts = {
                c.path: self.LOCAL
                for c in self.conflicts
            }
            self.close()

    def _afterSolve(self, remove=True):
        if remove:
            parent = self.lastSelectedItem.parent()
            parent.removeChild(self.lastSelectedItem)
            self.lastSelectedItem = None
            if parent.childCount() == 0:
                self.conflictsTree.invisibleRootItem().removeChild(parent)
                if self.conflictsTree.topLevelItemCount() == 0:
                    self.solved = True
                    self.close()

        self.attributesTable.setRowCount(0)
        self.cleanCanvas()
        self.solveButton.setEnabled(False)
        self.solveLocalButton.setEnabled(False)
        self.solveRemoteButton.setEnabled(False)

    def solveLocal(self):
        self.resolvedConflicts[self.currentPath] = self.LOCAL
        self._afterSolve()

    def solveRemote(self):
        self.resolvedConflicts[self.currentPath] = self.REMOTE
        self._afterSolve()

    def solve(self):
        attribs = {}
        for i in range(self.attributesTable.rowCount()):
            value = self.attributesTable.item(i, 4).value
            name = str(self.attributesTable.item(i, 3).text())
            attribs[name] = value
        self.resolvedConflicts[self.currentPath] = attribs
        self._afterSolve()

    def updateSolveButton(self):
        self.solveButton.setEnabled(len(self.conflicted) == 0)

    def showFeatureAttributes(self):
        conflictItem = self.lastSelectedItem
        self.oursgeom = None
        self.theirsgeom = None
        geoms = (self.oursgeom, self.theirsgeom)
        self.currentConflictedAttributes = []
        attribs = list(conflictItem.origin.keys())
        self.attributesTable.setRowCount(len(attribs))

        self.conflicted = []
        for idx, name in enumerate(attribs):
            font = QFont()
            font.setBold(True)
            font.setWeight(75)
            item = QTableWidgetItem(name)
            item.setFont(font)
            self.attributesTable.setItem(idx, 3, item)

            self.attributesTable.setItem(idx, 4, ValueItem(None, False))

            try:
                values = (conflictItem.origin[name], conflictItem.local[name],
                          conflictItem.remote[name])
            except Exception:  #Local has been deleted
                self._afterSolve(False)
                self.solveModifyAndDelete(conflictItem.conflict.path,
                                          self.REMOTE)
                return
            except TypeError:  #Remote has been deleted
                self._afterSolve(False)
                self.solveModifyAndDelete(conflictItem.conflict.path,
                                          self.LOCAL)
                return
            try:
                geom = QgsGeometry.fromWkt(values[0])
            except:
                geom = None
            if geom is not None:
                self.theirsgeom = QgsGeometry().fromWkt(values[1])
                self.oursgeom = QgsGeometry.fromWkt(values[2])
                geoms = (self.oursgeom, self.theirsgeom)

            ok = values[0] == values[1] or values[1] == values[2] or values[
                0] == values[2]

            for i, v in enumerate(values):
                self.attributesTable.setItem(idx, i,
                                             ValueItem(v, not ok, geoms))

            if not ok:
                self.conflicted.append(name)
            else:
                if values[0] == values[1]:
                    newvalue = values[2]
                else:
                    newvalue = values[1]
                self.attributesTable.setItem(idx, 4,
                                             ValueItem(newvalue, False, geoms))

        self.attributesTable.resizeRowsToContents()
        self.attributesTable.horizontalHeader().setMinimumSectionSize(150)
        self.attributesTable.horizontalHeader().setStretchLastSection(True)

    def solveModifyAndDelete(self, path, modified):
        msgBox = QMessageBox()
        msgBox.setText(
            "The feature has been modified in one version and deleted in the other one.\n"
            "How do you want to solve the conflict?")
        msgBox.addButton(QPushButton('Modify'), QMessageBox.YesRole)
        msgBox.addButton(QPushButton('Delete'), QMessageBox.NoRole)
        msgBox.addButton(QPushButton('Cancel'), QMessageBox.RejectRole)
        ret = msgBox.exec_()
        if ret == 0:
            self.resolvedConflicts[path] = modified
            self._afterSolve()
        elif ret == 1:
            self.resolvedConflicts[path] = self.DELETE
            self._afterSolve()
        else:
            pass

    def createLayers(self):
        types = [("Point", ptOursStyle, ptTheirsStyle),
                 ("LineString", lineOursStyle, lineTheirsStyle),
                 ("Polygon", polygonOursStyle, polygonTheirsStyle)]
        if self.oursgeom is not None:
            geomtype = types[int(self.oursgeom.type())][0]
            style = types[int(self.oursgeom.type())][1]
            self.oursLayer = loadLayerNoCrsDialog(geomtype + "?crs=EPSG:4326",
                                                  "ours", "memory")
            pr = self.oursLayer.dataProvider()
            feat = QgsFeature()
            feat.setGeometry(self.oursgeom)
            pr.addFeatures([feat])
            self.oursLayer.loadNamedStyle(style)
            self.oursLayer.updateExtents()
            QgsMapLayerRegistry.instance().addMapLayer(self.oursLayer, False)
        else:
            self.oursLayer = None
        if self.theirsgeom is not None:
            geomtype = types[int(self.theirsgeom.type())][0]
            style = types[int(self.theirsgeom.type())][2]
            self.theirsLayer = loadLayerNoCrsDialog(
                geomtype + "?crs=EPSG:4326", "theirs", "memory")
            pr = self.theirsLayer.dataProvider()
            feat = QgsFeature()
            feat.setGeometry(self.theirsgeom)
            pr.addFeatures([feat])
            self.theirsLayer.loadNamedStyle(style)
            self.theirsLayer.updateExtents()
            QgsMapLayerRegistry.instance().addMapLayer(self.theirsLayer, False)
        else:
            self.theirsLayer = None

    def showGeoms(self):
        checks = [self.showRemoteCheck, self.showLocalCheck]
        layers = [self.oursLayer, self.theirsLayer]
        toShow = []
        for lay, chk in zip(layers, checks):
            if lay is not None and chk.isChecked():
                toShow.append(lay)
        self.mapCanvas.setRenderFlag(False)
        self.mapCanvas.setLayerSet(
            [QgsMapCanvasLayer(layer) for layer in toShow])
        self.mapCanvas.setRenderFlag(True)

    def closeEvent(self, evnt):
        if not self.solved:
            ret = QMessageBox.warning(
                self, "Conflict resolution", "There are unsolved conflicts.\n"
                "Do you really want to exit and abort the sync operation?",
                QMessageBox.Yes | QMessageBox.No)
            if ret == QMessageBox.No:
                evnt.ignore()
                return

        self.cleanCanvas()
예제 #4
0
class MirrorMap(QWidget):
    def __init__(self, parent=None, iface=None):
        QWidget.__init__(self, parent)
        # self.setAttribute(Qt.WA_DeleteOnClose)

        self.iface = iface
        self.layerId2canvasLayer = {}
        self.canvasLayers = []

        self.setupUi()

    def closeEvent(self, event):
        self.scaleFactor.valueChanged.disconnect(self.onExtentsChanged)

        if not self.iface is None:
            self.iface.mapCanvas().extentsChanged.discconnect(
                self.onExtentsChanged)
            self.iface.mapCanvas().mapRenderer(
            ).destinationCrsChanged.disconnect(self.onCrsChanged)
            self.iface.mapCanvas().mapRenderer().mapUnitsChanged.disconnect(
                self.onCrsChanged)
            self.iface.mapCanvas().mapRenderer(
            ).hasCrsTransformEnabled.disconnect(self.onCrsTransformEnabled)
            QgsProject.instance().layerWillBeRemoved.disconnect(self.delLayer)
            self.iface.currentLayerChanged.disconnect(self.refreshLayerButtons)

        self.closed.emit()

        return QWidget.closeEvent(self, event)

    def setupUi(self):
        self.setObjectName("mirrormap")

        gridLayout = QGridLayout(self)
        gridLayout.setContentsMargins(0, 0, gridLayout.verticalSpacing(),
                                      gridLayout.verticalSpacing())

        self.canvas = QgsMapCanvas(self)
        self.canvas.setCanvasColor(QColor(255, 255, 255))
        settings = QSettings()
        gridLayout.addWidget(self.canvas, 0, 0, 1, 5)

        self.addLayerBtn = QToolButton(self)
        # self.addLayerBtn.setToolButtonStyle( Qt.ToolButtonTextBesideIcon )
        # self.addLayerBtn.setText("Add current layer")
        self.addLayerBtn.setIcon(GuiUtils.get_icon("add.png"))

        self.addLayerBtn.clicked.connect(self.tool_add_layer)
        gridLayout.addWidget(self.addLayerBtn, 1, 0, 1, 1)

        self.delLayerBtn = QToolButton(self)
        # self.delLayerBtn.setToolButtonStyle( Qt.ToolButtonTextBesideIcon )
        # self.delLayerBtn.setText("Remove current layer")
        self.delLayerBtn.setIcon(GuiUtils.get_icon("remove.png"))
        self.delLayerBtn.clicked.connect(self.tool_remove_layer)
        gridLayout.addWidget(self.delLayerBtn, 1, 1, 1, 1)

        self.renderCheck = QCheckBox("Render", self)
        self.renderCheck.toggled.connect(self.toggleRender)
        self.renderCheck.setChecked(True)
        gridLayout.addWidget(self.renderCheck, 1, 2, 1, 1)

        self.scaleFactorLabel = QLabel(self)
        self.scaleFactorLabel.setText("Scale factor:")
        self.scaleFactorLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        gridLayout.addWidget(self.scaleFactorLabel, 1, 3, 1, 1)
        self.scaleFactor = QDoubleSpinBox(self)
        self.scaleFactor.setMinimum(0.0)
        self.scaleFactor.setMaximum(1000.0)
        self.scaleFactor.setDecimals(3)
        self.scaleFactor.setValue(1)
        self.scaleFactor.setObjectName("scaleFactor")
        self.scaleFactor.setSingleStep(.05)
        gridLayout.addWidget(self.scaleFactor, 1, 4, 1, 1)
        self.scaleFactor.valueChanged.connect(self.onExtentsChanged)

        # Add a default pan tool
        self.toolPan = QgsMapToolPan(self.canvas)
        self.canvas.setMapTool(self.toolPan)

        self.scaleFactor.valueChanged.connect(self.onExtentsChanged)
        self.set_iface(self.iface)

    def toggleRender(self, enabled):
        self.canvas.setRenderFlag(enabled)

    def extent(self):
        """
        :return: Current extents of the map canvas view.
        """
        return self.canvas.extent()

    def canvas_layers(self):
        """
        :return: Layers currently in the canvas.
        :rtype: list
        """
        return self.canvasLayers

    def on_canvas_refreshed(self):
        """
        """
        self.refresh_layers()

    def tool_add_layer(self):
        self.addLayer()

    def tool_remove_layer(self):
        self.delLayer()

    def set_iface(self, iface):
        if iface is None:
            return

        self.iface = iface
        self.iface.mapCanvas().extentsChanged.connect(self.onExtentsChanged)
        # self.iface.mapCanvas().mapCanvasRefreshed.connect(self.on_canvas_refreshed)
        self.iface.mapCanvas().destinationCrsChanged.connect(self.onCrsChanged)
        QgsProject.instance().layerWillBeRemoved.connect(self.delLayer)
        self.iface.currentLayerChanged.connect(self.refreshLayerButtons)

        self.refreshLayerButtons()

        self.onExtentsChanged()
        self.onCrsChanged()

    def refresh_layers(self):
        """
        Checks if the layers in the canvas list have already been added.
        If not, then add to the property viewer canvas.
        """
        for ly in self.iface.mapCanvas().layers():
            layer_id = self._layerId(ly)
            if layer_id not in self.layerId2canvasLayer:
                self.addLayer(layer_id)
        # QCoreApplication.processEvents(QEventLoop.ExcludeSocketNotifiers|QEventLoop.ExcludeUserInputEvents)

    def onExtentsChanged(self):
        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        self.canvas.setExtent(self.iface.mapCanvas().extent())
        self.canvas.zoomByFactor(self.scaleFactor.value())
        # self.canvas.refresh()

        self.canvas.setRenderFlag(prevFlag)

    def onCrsChanged(self):
        self.canvas.setDestinationCrs(
            self.iface.mapCanvas().mapSettings().destinationCrs())

    def refreshLayerButtons(self):
        layer = self.iface.activeLayer()

        isLayerSelected = layer != None
        hasLayer = False
        for l in self.canvas.layers():
            if l == layer:
                hasLayer = True
                break

        self.addLayerBtn.setEnabled(isLayerSelected and not hasLayer)
        self.delLayerBtn.setEnabled(isLayerSelected and hasLayer)

    def getLayerSet(self):
        return [self._layerId(x.layer()) for x in self.canvasLayers]

    def setLayerSet(self, layerIds=None):
        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        if layerIds == None:
            self.layerId2canvasLayer = {}
            self.canvasLayers = []
            self.canvas.setLayers([])

        else:
            for lid in layerIds:
                self.addLayer(lid)

        self.refreshLayerButtons()
        self.onExtentsChanged()
        self.canvas.setRenderFlag(prevFlag)

    def addLayer(self, layerId=None):
        if layerId == None:
            layer = self.iface.activeLayer()
        else:
            layer = QgsProject.instance().mapLayer(layerId)

        if layer == None:
            return

        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        # add the layer to the map canvas layer set
        self.canvasLayers = []
        id2cl_dict = {}
        for l in self.iface.mapCanvas().layers():
            lid = self._layerId(l)
            if lid in self.layerId2canvasLayer:  # previously added
                cl = self.layerId2canvasLayer[lid]
            elif l == layer:  # Selected layer
                cl = layer
            else:
                continue

            id2cl_dict[lid] = cl
            self.canvasLayers.append(cl)

        self.layerId2canvasLayer = id2cl_dict
        self.canvas.setLayers(self.canvasLayers)

        self.refreshLayerButtons()
        self.onExtentsChanged()
        self.canvas.setRenderFlag(prevFlag)

    def delLayer(self, layerId=None):
        if layerId == None:
            layer = self.iface.activeLayer()
            if layer == None:
                return
            layerId = self._layerId(layer)

        # remove the layer from the map canvas layer set
        if layerId not in self.layerId2canvasLayer:
            return

        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        cl = self.layerId2canvasLayer[layerId]
        del self.layerId2canvasLayer[layerId]
        self.canvasLayers.remove(cl)
        self.canvas.setLayers(self.canvasLayers)

        self.refreshLayerButtons()
        self.onExtentsChanged()
        self.canvas.setRenderFlag(prevFlag)

    def _layerId(self, layer):
        if hasattr(layer, 'id'):
            return layer.id()

        return layer.getLayerID()
# raster layer
world_wmts_url = "contextualWMSLegend=0&crs=EPSG:4326&dpiMode=7&"\
                 "featureCount=10&format=image/jpeg&layers=opengeo:countries&"\
                 "styles=&tileMatrixSet=EPSG:4326&"\
                 "url=http://suite.opengeo.org/geoserver/gwc/service/wmts?request%3DGetCapabilities"
world_lyr = QgsRasterLayer(world_wmts_url, "Countries", 'wms')
print(world_lyr.isValid())

# vector layer
geojson_contributors = path.join(
                               path.dirname(QgsApplication.developersMapFilePath()),
                                            'contributors.json')

layer = QgsVectorLayer(geojson_contributors, "QGIS Contributors", "ogr")
layer.setLayerTransparency(35)
print(layer.isValid())

# managing map canvas
map_canvas = QgsMapCanvas(new_dialog)
map_canvas.setMinimumSize(800, 600)
map_canvas.mapRenderer().setDestinationCrs(crs_wgs84)
QgsMapLayerRegistry.instance().addMapLayers([layer, world_lyr], 0)
map_canvas_layer_list = [QgsMapCanvasLayer(layer), QgsMapCanvasLayer(world_lyr)]
map_canvas.setExtent(layer.extent())
map_canvas.setLayerSet(map_canvas_layer_list)

new_dialog.show()

print(dir(map_canvas.map))
print(map_canvas.layers())