diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..915a258
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,26 @@
+name: Semantic Release
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ release:
+ runs-on: ubuntu-24.04
+ concurrency: release
+ permissions:
+ id-token: write
+ contents: write
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Python Semantic Release
+ uses: python-semantic-release/python-semantic-release@v9.8.2
+ with:
+ # Note: This token expires July 2025
+ # See: https://python-semantic-release.readthedocs.io/en/latest/index.html#index-creating-vcs-releases
+ github_token: ${{ secrets.GH_TOKEN_FOR_SEMANTIC_CI_RELEASE }}
\ No newline at end of file
diff --git a/.tool-versions b/.tool-versions
new file mode 100644
index 0000000..17b2499
--- /dev/null
+++ b/.tool-versions
@@ -0,0 +1,2 @@
+# Need to use system python to access QGIS libraries
+python system
diff --git a/__init__.py b/__init__.py
index 57a203b..4c650fa 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,3 +1,5 @@
+__version__ = "0.0.0"
+
import logging
from .plugin import OFDSDedupPlugin
diff --git a/dev_requirements.in b/dev_requirements.in
index ac46706..99c9a53 100644
--- a/dev_requirements.in
+++ b/dev_requirements.in
@@ -10,3 +10,6 @@ mypy>=1.8,<2
PyQt5-stubs
qgis-stubs
pip-tools
+python-semantic-release
+pyclean
+jinja2-cli
diff --git a/dev_requirements.txt b/dev_requirements.txt
index 0760bdb..d7f06d4 100644
--- a/dev_requirements.txt
+++ b/dev_requirements.txt
@@ -1,9 +1,11 @@
#
-# This file is autogenerated by pip-compile with Python 3.10
+# This file is autogenerated by pip-compile with Python 3.12
# by the following command:
#
# pip-compile dev_requirements.in
#
+annotated-types==0.7.0
+ # via pydantic
astor==0.8.1
# via flake8-qgis
attrs==23.2.0
@@ -12,14 +14,22 @@ black==24.3.0
# via -r dev_requirements.in
build==1.2.1
# via pip-tools
+certifi==2024.6.2
+ # via requests
+charset-normalizer==3.3.2
+ # via requests
click==8.1.7
# via
# black
+ # click-option-group
# pip-tools
+ # python-semantic-release
+click-option-group==0.5.6
+ # via python-semantic-release
coverage[toml]==7.4.4
# via pytest-cov
-exceptiongroup==1.2.0
- # via pytest
+dotty-dict==1.3.1
+ # via python-semantic-release
flake8==7.0.0
# via
# -r dev_requirements.in
@@ -29,12 +39,32 @@ flake8-bugbear==24.2.6
# via -r dev_requirements.in
flake8-qgis==1.0.0
# via -r dev_requirements.in
+gitdb==4.0.11
+ # via gitpython
+gitpython==3.1.43
+ # via python-semantic-release
+idna==3.7
+ # via requests
+importlib-resources==6.4.0
+ # via python-semantic-release
iniconfig==2.0.0
# via pytest
isort==5.13.2
# via -r dev_requirements.in
+jinja2==3.1.4
+ # via
+ # jinja2-cli
+ # python-semantic-release
+jinja2-cli==0.8.2
+ # via -r dev_requirements.in
+markdown-it-py==3.0.0
+ # via rich
+markupsafe==2.1.5
+ # via jinja2
mccabe==0.7.0
# via flake8
+mdurl==0.1.2
+ # via markdown-it-py
mypy==1.9.0
# via -r dev_requirements.in
mypy-extensions==1.0.0
@@ -54,10 +84,18 @@ platformdirs==4.2.0
# via black
pluggy==1.4.0
# via pytest
+pyclean==3.0.0
+ # via -r dev_requirements.in
pycodestyle==2.11.1
# via flake8
+pydantic==2.7.4
+ # via python-semantic-release
+pydantic-core==2.18.4
+ # via pydantic
pyflakes==3.2.0
# via flake8
+pygments==2.18.0
+ # via rich
pyproject-hooks==1.0.0
# via
# build
@@ -75,21 +113,34 @@ pytest-cov==5.0.0
# via -r dev_requirements.in
pytest-qgis==2.0.0
# via -r dev_requirements.in
+python-gitlab==4.6.0
+ # via python-semantic-release
+python-semantic-release==9.8.2
+ # via -r dev_requirements.in
qgis-stubs==0.2.0.post1
# via -r dev_requirements.in
-tomli==2.0.1
+requests==2.32.3
# via
- # black
- # build
- # coverage
- # mypy
- # pip-tools
- # pyproject-hooks
- # pytest
+ # python-gitlab
+ # python-semantic-release
+ # requests-toolbelt
+requests-toolbelt==1.0.0
+ # via python-gitlab
+rich==13.7.1
+ # via python-semantic-release
+shellingham==1.5.4
+ # via python-semantic-release
+smmap==5.0.1
+ # via gitdb
+tomlkit==0.12.5
+ # via python-semantic-release
typing-extensions==4.11.0
# via
- # black
# mypy
+ # pydantic
+ # pydantic-core
+urllib3==2.2.1
+ # via requests
wheel==0.43.0
# via pip-tools
diff --git a/gui_style.py b/gui_style.py
new file mode 100644
index 0000000..e6fad9b
--- /dev/null
+++ b/gui_style.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'gui_style.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.10
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_StyleOFDSDialog(object):
+ def setupUi(self, StyleOFDSDialog):
+ StyleOFDSDialog.setObjectName("StyleOFDSDialog")
+ StyleOFDSDialog.resize(457, 161)
+ self.gridLayout_2 = QtWidgets.QGridLayout(StyleOFDSDialog)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.gridLayout = QtWidgets.QGridLayout()
+ self.gridLayout.setObjectName("gridLayout")
+ self.spansComboBox = QtWidgets.QComboBox(StyleOFDSDialog)
+ self.spansComboBox.setObjectName("spansComboBox")
+ self.gridLayout.addWidget(self.spansComboBox, 3, 1, 1, 1)
+ self.line = QtWidgets.QFrame(StyleOFDSDialog)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.gridLayout.addWidget(self.line, 6, 2, 1, 1)
+ self.nodesLabel = QtWidgets.QLabel(StyleOFDSDialog)
+ self.nodesLabel.setMaximumSize(QtCore.QSize(100, 16777215))
+ self.nodesLabel.setObjectName("nodesLabel")
+ self.gridLayout.addWidget(self.nodesLabel, 2, 0, 1, 1)
+ self.nodesComboBox = QtWidgets.QComboBox(StyleOFDSDialog)
+ self.nodesComboBox.setObjectName("nodesComboBox")
+ self.gridLayout.addWidget(self.nodesComboBox, 2, 1, 1, 1)
+ self.closeButton = QtWidgets.QPushButton(StyleOFDSDialog)
+ self.closeButton.setObjectName("closeButton")
+ self.gridLayout.addWidget(self.closeButton, 7, 2, 1, 1)
+ self.spansApplyButton = QtWidgets.QPushButton(StyleOFDSDialog)
+ self.spansApplyButton.setMaximumSize(QtCore.QSize(100, 16777215))
+ self.spansApplyButton.setObjectName("spansApplyButton")
+ self.gridLayout.addWidget(self.spansApplyButton, 3, 2, 1, 1)
+ self.infoLabel = QtWidgets.QLabel(StyleOFDSDialog)
+ self.infoLabel.setObjectName("infoLabel")
+ self.gridLayout.addWidget(self.infoLabel, 0, 0, 1, 3)
+ self.nodesApplyButton = QtWidgets.QPushButton(StyleOFDSDialog)
+ self.nodesApplyButton.setMaximumSize(QtCore.QSize(100, 16777215))
+ self.nodesApplyButton.setObjectName("nodesApplyButton")
+ self.gridLayout.addWidget(self.nodesApplyButton, 2, 2, 1, 1)
+ self.spansLabel = QtWidgets.QLabel(StyleOFDSDialog)
+ self.spansLabel.setMaximumSize(QtCore.QSize(100, 16777215))
+ self.spansLabel.setObjectName("spansLabel")
+ self.gridLayout.addWidget(self.spansLabel, 3, 0, 1, 1)
+ self.refreshButton = QtWidgets.QPushButton(StyleOFDSDialog)
+ self.refreshButton.setObjectName("refreshButton")
+ self.gridLayout.addWidget(self.refreshButton, 7, 1, 1, 1)
+ self.gridLayout_2.addLayout(self.gridLayout, 1, 0, 1, 1)
+
+ self.retranslateUi(StyleOFDSDialog)
+ self.closeButton.pressed.connect(StyleOFDSDialog.close) # type: ignore
+ QtCore.QMetaObject.connectSlotsByName(StyleOFDSDialog)
+
+ def retranslateUi(self, StyleOFDSDialog):
+ _translate = QtCore.QCoreApplication.translate
+ StyleOFDSDialog.setWindowTitle(_translate("StyleOFDSDialog", "Style OFDS"))
+ self.nodesLabel.setText(_translate("StyleOFDSDialog", "Nodes Layer"))
+ self.closeButton.setText(_translate("StyleOFDSDialog", "Close"))
+ self.spansApplyButton.setText(_translate("StyleOFDSDialog", "Apply"))
+ self.infoLabel.setText(_translate("StyleOFDSDialog", "Apply styles to OFDS layers"))
+ self.nodesApplyButton.setText(_translate("StyleOFDSDialog", "Apply"))
+ self.spansLabel.setText(_translate("StyleOFDSDialog", "Spans Layer"))
+ self.refreshButton.setText(_translate("StyleOFDSDialog", "Refresh Layers"))
diff --git a/gui_style.ui b/gui_style.ui
new file mode 100644
index 0000000..b5bc594
--- /dev/null
+++ b/gui_style.ui
@@ -0,0 +1,128 @@
+
+
+ StyleOFDSDialog
+
+
+
+ 0
+ 0
+ 457
+ 161
+
+
+
+ Style OFDS
+
+
+ -
+
+
-
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 100
+ 16777215
+
+
+
+ Nodes Layer
+
+
+
+ -
+
+
+ -
+
+
+ Close
+
+
+
+ -
+
+
+
+ 100
+ 16777215
+
+
+
+ Apply
+
+
+
+ -
+
+
+ Apply styles to OFDS layers
+
+
+
+ -
+
+
+
+ 100
+ 16777215
+
+
+
+ Apply
+
+
+
+ -
+
+
+
+ 100
+ 16777215
+
+
+
+ Spans Layer
+
+
+
+ -
+
+
+ Refresh Layers
+
+
+
+
+
+
+
+
+
+
+ closeButton
+ pressed()
+ StyleOFDSDialog
+ close()
+
+
+ 399
+ 136
+
+
+ 228
+ 80
+
+
+
+
+
diff --git a/metadata.txt b/metadata.txt
index 7034962..5555c11 100644
--- a/metadata.txt
+++ b/metadata.txt
@@ -1,9 +1,9 @@
-[general]
-name=OFDS Consolidation Tool
-description=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard
-about=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard
-version=0.1
-qgisMinimumVersion=3.28
-author=ODSC
-email=code@opendataservices.coop
+[general]
+name=OFDS Consolidation Tool
+description=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard
+about=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard
+version=0.0.0
+qgisMinimumVersion=3.28
+author=ODSC
+email=code@opendataservices.coop
repository=https://github.com/Open-Telecoms-Data/ofds_consolidation_tool
\ No newline at end of file
diff --git a/plugin.py b/plugin.py
index 1e9d849..7337f80 100644
--- a/plugin.py
+++ b/plugin.py
@@ -4,28 +4,54 @@
from PyQt5.QtCore import PYQT_VERSION_STR, QT_VERSION_STR
from PyQt5.QtWidgets import QAction
from qgis.core import QgsProject
-from qgis.utils import iface # type: ignore
+from qgis.gui import QgisInterface
+from qgis.utils import iface
+
+iface: QgisInterface
from .tool.tool import OFDSDedupToolDialog
+from .tool.style import OFDSStyleToolDialog
+from . import __version__
logger = logging.getLogger(__name__)
class OFDSDedupPlugin:
def __init__(self):
- self.tool_dialog = OFDSDedupToolDialog()
+ self.consolidate_tool_dialog = OFDSDedupToolDialog()
+ self.style_tool_dialog = OFDSStyleToolDialog()
def initGui(self):
- self.action = QAction("Consolidate OFDS", iface.mainWindow())
- self.action.triggered.connect(self.run)
- iface.addToolBarIcon(self.action)
+ self.action_consolidate = QAction("Consolidate OFDS", iface.mainWindow())
+ self.action_consolidate.setObjectName("consolidateOfdsAction")
+ self.action_consolidate.triggered.connect(self.run_consolidate)
+ iface.addToolBarIcon(self.action_consolidate)
+ iface.addPluginToMenu("&OFDS", self.action_consolidate)
+
+ self.action_style = QAction("Style OFDS", iface.mainWindow())
+ self.action_style.setObjectName("styleOfdsAction")
+ self.action_style.triggered.connect(self.run_style)
+ iface.addToolBarIcon(self.action_style)
+ iface.addPluginToMenu("&OFDS", self.action_style)
def unload(self):
- iface.removeToolBarIcon(self.action)
- del self.action
+ iface.removePluginMenu("&OFDS", self.action_consolidate)
+ iface.removeToolBarIcon(self.action_consolidate)
+ del self.action_consolidate
+
+ iface.removePluginMenu("&OFDS", self.action_style)
+ iface.removeToolBarIcon(self.action_style)
+ del self.action_style
+
+ self.consolidate_tool_dialog.close()
+ del self.consolidate_tool_dialog
+
+ self.style_tool_dialog.close()
+ del self.style_tool_dialog
- def run(self):
- logger.debug("Running OFDS Consolidation plugin")
+ def run_consolidate(self):
+ logger.debug("Opening OFDS Consolidate Tool Dialog")
+ logger.debug(f"OFDS Consolidation Tool Plugin v{__version__}")
logger.debug(
f"Qt: v{QT_VERSION_STR} PyQt: v{PYQT_VERSION_STR} Python: {sys.version}"
)
@@ -34,5 +60,15 @@ def run(self):
if not project:
raise Exception
- self.tool_dialog.reset(project=project)
- self.tool_dialog.show()
+ self.consolidate_tool_dialog.reset(project=project)
+ self.consolidate_tool_dialog.show()
+
+ def run_style(self):
+ logger.debug("Opening OFDS Style Tool Dialog")
+ logger.debug(f"OFDS Consolidation Tool Plugin v{__version__}")
+ project = QgsProject.instance()
+ if not project:
+ raise Exception
+
+ self.style_tool_dialog.refresh_layers()
+ self.style_tool_dialog.show()
diff --git a/pyproject.toml b/pyproject.toml
index 64ce2f9..ac71535 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,29 +4,25 @@ version = "0.1.0"
requires-python = ">=3.8"
authors = [
- {name = "ODSC", email = "code@opendataservices.coop"}
+ { name = "ODSC", email = "code@opendataservices.coop" }
]
description = "A tool to consolidate multiple fibre network datasets formatted using the Open Fibre Data Standard"
readme = "README.md"
-license = {file = "LICENSE"}
+license = { file = "LICENSE" }
keywords = ["open", "fibre", "data", "standard", "ofds", "consolidation", "tool", "gis", "qgis"]
# See: https://pypi.org/classifiers/
classifiers = [
# Development Status
- "Development Status :: 2 - Pre-Alpha",
-
+ "Development Status :: 3 - Alpha",
# Environment
"Environment :: Plugins",
-
# Indicate who your project is intended for
"Intended Audience :: Telecommunications Industry",
"Topic :: Desktop Environment",
"Topic :: Scientific/Engineering :: GIS",
-
# License
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
-
# Specify the Python versions you support here.
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
@@ -46,7 +42,7 @@ testpaths = [
[tool.black]
line-length = 88
-force-exclude = "gui.py" # gui.py is generated by pyuic5, so don't format it
+force-exclude = "gui(_style)?.py" # gui.py is generated by pyuic5, so don't format it
[tool.isort]
# https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#isort
@@ -60,5 +56,63 @@ warn_unused_configs = true
check_untyped_defs = true
exclude = [
"gui.py",
+ "gui_style.py",
"_lib/jellyfish/_jellyfish.py",
-]
\ No newline at end of file
+]
+
+[tool.semantic_release]
+assets = []
+build_command = "/bin/bash ./scripts/build-package.sh"
+build_command_env = []
+commit_message = "{version}\n\nAutomatically generated by python-semantic-release"
+commit_parser = "angular"
+logging_use_named_masks = false
+major_on_zero = true
+allow_zero_version = true
+no_git_verify = false
+tag_format = "v{version}"
+
+[tool.semantic_release.branches.main]
+match = '(main|master|rg/release)'
+prerelease_token = "rc"
+prerelease = false
+
+[tool.semantic_release.changelog]
+template_dir = "templates"
+changelog_file = "CHANGELOG.md"
+exclude_commit_patterns = []
+
+[tool.semantic_release.changelog.environment]
+block_start_string = "{%"
+block_end_string = "%}"
+variable_start_string = "{{"
+variable_end_string = "}}"
+comment_start_string = "{#"
+comment_end_string = "#}"
+trim_blocks = false
+lstrip_blocks = false
+newline_sequence = "\n"
+keep_trailing_newline = false
+extensions = []
+autoescape = true
+
+[tool.semantic_release.commit_author]
+env = "GIT_COMMIT_AUTHOR"
+default = "semantic-release "
+
+[tool.semantic_release.commit_parser_options]
+allowed_tags = ["build", "chore", "ci", "docs", "feat", "fix", "perf", "style", "refactor", "test"]
+minor_tags = ["feat"]
+patch_tags = ["fix", "perf"]
+default_bump_level = 0
+
+[tool.semantic_release.remote]
+name = "origin"
+type = "github"
+ignore_token_for_push = false
+insecure = false
+
+[tool.semantic_release.publish]
+dist_glob_patterns = ["dist/*"]
+upload_to_vcs_release = true
+
diff --git a/resources.py b/resources.py
new file mode 100644
index 0000000..7764417
--- /dev/null
+++ b/resources.py
@@ -0,0 +1,10 @@
+from pathlib import Path
+
+# Paths to resources
+
+PLUGIN_DIR = Path(__file__).resolve().parent
+
+STYLESHEETS_DIR = PLUGIN_DIR / "tool" / "stylesheets"
+
+STYLESHEET_NODES = STYLESHEETS_DIR / "networkProviderNodesMultiplePlainOutput.qml"
+STYLESHEET_SPANS = STYLESHEETS_DIR / "networkProviderSpansMultiplePlainOutput.qml"
diff --git a/scripts/build-package.sh b/scripts/build-package.sh
new file mode 100755
index 0000000..239586b
--- /dev/null
+++ b/scripts/build-package.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+set -euo pipefail
+
+NEW_VERSION="${NEW_VERSION:?Error: NEW_VERSION not set}"
+
+# Update metadata.txt with the new version
+jinja2 --strict -D new_version="${NEW_VERSION}" templates/.metadata.txt.j2 > metadata.txt
+
+# Create a QGIS-compatible Plugin and
+# Copy our files into the plugin dir.
+# Note the directory name must not conflict with other plugins.
+
+ZIPDIR="dist/ofds_consolidation_tool/"
+
+# Check we won't accidentally overwrite stuff
+if [ -d "$ZIPDIR" ]; then
+ echo "Error: temporary directory already exists. Are you sure you're running this in project root?"
+ exit 1
+fi
+
+# Make our dist dir if it doesn't exist
+mkdir -p "$ZIPDIR"
+
+# Clean up __pycache__ and .pyc files
+pyclean . || true
+
+
+# Copy in relevant files
+cp -r \
+ "metadata.txt" \
+ "README.md" \
+ "LICENSE" \
+ "docs/" \
+ "pyproject.toml" \
+ "tests/" \
+ "dev_requirements.in" \
+ "dev_requirements.txt" \
+ "tool/" \
+ "gui.py" \
+ "gui_style.py" \
+ "helpers.py" \
+ "ofds.py" \
+ "plugin.py" \
+ "resources.py" \
+ "$ZIPDIR"
+
+pushd dist/
+# Create the Zip
+zip -r "OFDS_Consolidation_Tool_QGIS_Plugin_v${NEW_VERSION}.zip" ofds_consolidation_tool/
+
+# Clean up temporary dir
+rm -r ofds_consolidation_tool/
+popd
diff --git a/scripts/compile-gui.sh b/scripts/compile-gui.sh
index e8b9ec1..d65eaf1 100755
--- a/scripts/compile-gui.sh
+++ b/scripts/compile-gui.sh
@@ -2,3 +2,4 @@
set -euo pipefail
pyuic5 gui.ui -o gui.py
+pyuic5 gui_style.ui -o gui_style.py
diff --git a/templates/.metadata.txt.j2 b/templates/.metadata.txt.j2
new file mode 100644
index 0000000..bb32a3f
--- /dev/null
+++ b/templates/.metadata.txt.j2
@@ -0,0 +1,9 @@
+[general]
+name=OFDS Consolidation Tool
+description=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard
+about=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard
+version={{ new_version }}
+qgisMinimumVersion=3.28
+author=ODSC
+email=code@opendataservices.coop
+repository=https://github.com/Open-Telecoms-Data/ofds_consolidation_tool
\ No newline at end of file
diff --git a/tool/style.py b/tool/style.py
new file mode 100644
index 0000000..54912fe
--- /dev/null
+++ b/tool/style.py
@@ -0,0 +1,86 @@
+import logging
+from typing import List, cast
+
+from PyQt5.QtWidgets import QDialog
+from qgis.core import (
+ QgsProject,
+ QgsMapLayer,
+ QgsWkbTypes,
+ QgsVectorLayer,
+ QgsMapLayerType,
+)
+
+from ..resources import STYLESHEET_NODES, STYLESHEET_SPANS
+from ..gui_style import Ui_StyleOFDSDialog
+
+logger = logging.getLogger(__name__)
+
+
+class OFDSStyleToolDialog(QDialog):
+ """
+ The top-level GUI element, the Dialog box that holds all our other sub-elements.
+
+ Note that there is a singleton instance of this class, it isn't created/destroyed
+ each time we open the tool, but it remains constantly "open" in the backgroud even
+ when closed or hidden, and all state is still the same next time the tool is opened
+ from the toolbar. This means it's important to properly tidy up the GUI and any
+ created/opened resources when we're done.
+ """
+
+ ui: Ui_StyleOFDSDialog
+
+ def __init__(self):
+ super().__init__()
+
+ self.ui = Ui_StyleOFDSDialog()
+ self.ui.setupUi(self)
+
+ # Connect UI signals to slots on this class
+ self.ui.nodesApplyButton.pressed.connect(self.apply_nodes_style)
+ self.ui.spansApplyButton.pressed.connect(self.apply_spans_style)
+ self.ui.refreshButton.pressed.connect(self.refresh_layers)
+
+ def refresh_layers(self):
+ """Refresh and populate the lists of layers from the current Project"""
+
+ project = QgsProject.instance()
+
+ # Find all the layers the user has loaded
+ all_layers: List[QgsMapLayer] = project.mapLayers(validOnly=True).values()
+
+ # Filter the layers that the tool can accept
+ selectable_layers: List[QgsVectorLayer] = list()
+
+ for layer in all_layers:
+ # only vector layers can be valid OFDS layers
+ if layer.type() == QgsMapLayerType.VectorLayer:
+ selectable_layers.append(cast(QgsVectorLayer, layer))
+
+ ncb = self.ui.nodesComboBox
+ scb = self.ui.spansComboBox
+
+ ncb.clear()
+ for layer in selectable_layers:
+ if layer.geometryType() == QgsWkbTypes.GeometryType.PointGeometry:
+ ncb.addItem(layer.name(), layer)
+
+ scb.clear()
+ for layer in selectable_layers:
+ if layer.geometryType() == QgsWkbTypes.GeometryType.LineGeometry:
+ scb.addItem(layer.name(), layer)
+
+ def apply_nodes_style(self):
+ """Apply the Nodes style to the currently selected Nodes layer"""
+ nodes_layer = self.ui.nodesComboBox.currentData()
+ if isinstance(nodes_layer, QgsVectorLayer):
+ if nodes_layer.geometryType() == QgsWkbTypes.GeometryType.PointGeometry:
+ nodes_layer.loadNamedStyle(STYLESHEET_NODES.as_posix(), False)
+ nodes_layer.triggerRepaint()
+
+ def apply_spans_style(self):
+ """Apply the Spans style to the currently selected Spans layer"""
+ spans_layer = self.ui.spansComboBox.currentData()
+ if isinstance(spans_layer, QgsVectorLayer):
+ if spans_layer.geometryType() == QgsWkbTypes.GeometryType.LineGeometry:
+ spans_layer.loadNamedStyle(STYLESHEET_SPANS.as_posix(), False)
+ spans_layer.triggerRepaint()
diff --git a/tool/view.py b/tool/view.py
index 3bdc49b..3804614 100644
--- a/tool/view.py
+++ b/tool/view.py
@@ -1,6 +1,7 @@
import logging
-from typing import List, Optional, Tuple, Union
from dataclasses import dataclass
+from typing import List, Optional, Tuple, Union
+
from PyQt5.QtWidgets import (
QTextEdit,
QPushButton,
@@ -24,9 +25,6 @@
NodeComparison,
SpanComparison,
)
-from ..gui import Ui_OFDSDedupToolDialog
-
-from ..helpers import EPSG3857, getOpenStreetMapLayer
from .model.network import FeatureType
from .viewmodel.state import (
ToolLayerSelectState,
@@ -36,6 +34,9 @@
ToolState,
ToolStateEnum,
)
+from ..gui import Ui_OFDSDedupToolDialog
+from ..helpers import EPSG3857, getOpenStreetMapLayer
+from ..resources import STYLESHEET_NODES, STYLESHEET_SPANS
logger = logging.getLogger(__name__)
@@ -554,6 +555,9 @@ def __init__(self, project: QgsProject, mapCanvas: QgsMapCanvas):
def update(self, state: Optional[ToolState]):
if isinstance(state, ToolOutputState):
+ state.output_network.nodesLayer.loadNamedStyle(STYLESHEET_NODES.as_posix())
+ state.output_network.spansLayer.loadNamedStyle(STYLESHEET_SPANS.as_posix())
+
self.minimapview.update(
MiniMapView.State(
nodesLayer=state.output_network.nodesLayer,