diff --git a/aequilibrae/paths/all_or_nothing.py b/aequilibrae/paths/all_or_nothing.py index 387b073e9..bbd4de3a9 100644 --- a/aequilibrae/paths/all_or_nothing.py +++ b/aequilibrae/paths/all_or_nothing.py @@ -46,6 +46,8 @@ def _build_signal(self): if self.assignment is None: self.assignment = SIGNAL(object) self.assignment.emit(["start", self.matrix.zones, self.class_name]) + else: + self.assignment.emit(["set_text", f"All-or-Nothing: {self.class_name}"]) def doWork(self): self.execute() diff --git a/aequilibrae/paths/linear_approximation.py b/aequilibrae/paths/linear_approximation.py index 3c7f56f39..2861291b0 100644 --- a/aequilibrae/paths/linear_approximation.py +++ b/aequilibrae/paths/linear_approximation.py @@ -20,16 +20,16 @@ from aequilibrae.paths.traffic_assignment import TrafficAssignment from aequilibrae.utils.signal import SIGNAL +from aequilibrae.utils.interface.worker_thread import WorkerThread from aequilibrae.utils.python_signal import PythonSignal -class LinearApproximation: +class LinearApproximation(WorkerThread): def __init__(self, assig_spec, algorithm, project=None) -> None: + WorkerThread.__init__(self, None) self.equilibration = SIGNAL(object) self.assignment = SIGNAL(object) - if isinstance(self.assignment, PythonSignal): - self.assignment.pos = 1 - + self.assignment.emit(["set_position", 1]) self.logger = project.logger if project else logging.getLogger("aequilibrae") self.project_path = project.project_base_path if project else gettempdir() @@ -471,6 +471,7 @@ def execute(self): # noqa: C901 self.equilibration.emit(["start", self.max_iter, "Equilibrium Assignment"]) self.logger.info(f"{self.algorithm} Assignment STATS") self.logger.info("Iteration, RelativeGap, stepsize") + self.assignment.emit(["start", c.matrix.zones, "All-or-Nothing"]) for self.iter in range(1, self.max_iter + 1): # noqa: B020 self.iteration_issue = [] self.equilibration.emit(["key_value", "rgap", self.rgap]) @@ -481,7 +482,6 @@ def execute(self): # noqa: C901 self.__maybe_create_path_file_directories() for c in self.traffic_classes: # type: TrafficClass - self.assignment.emit(["start", c.matrix.zones, "All-or-Nothing"]) # cost = c.fixed_cost / c.vot + self.congested_time # now only once cost = c.fixed_cost + self.congested_time aggregate_link_costs(cost, c.graph.compact_cost, c.results.crosswalk) diff --git a/aequilibrae/utils/geo_index.py b/aequilibrae/utils/geo_index.py index 048799478..27c2a8769 100644 --- a/aequilibrae/utils/geo_index.py +++ b/aequilibrae/utils/geo_index.py @@ -1,24 +1,15 @@ -import importlib.util as iutil import warnings from typing import Union, List from shapely.geometry import Point, Polygon, LineString, MultiPoint, MultiPolygon, MultiLineString from shapely.wkb import loads -rtree_avail = iutil.find_spec("rtree") is not None -qgis = iutil.find_spec("qgis") is not None -if qgis: +from aequilibrae.utils.qgis_utils import inside_qgis, rtree_avail + +if inside_qgis: from qgis.core import QgsSpatialIndex as Index from qgis.core import QgsGeometry, QgsFeature - env = "QGIS" -elif rtree_avail: - from rtree.index import Index as Index - - env = "Python" -else: - env = "NOT AVAILABLE" - class GeoIndex: """Implements a generic GeoIndex class that uses the QGIS index when using the GUI and RTree otherwise""" @@ -28,7 +19,7 @@ def __init__(self): self.built = False def build_from_layer(self, layer) -> dict: - if env != "QGIS": + if inside_qgis: warnings.warn("This method works inside QGIS only") self.built = True self.idx = Index(layer.getFeatures()) @@ -44,14 +35,14 @@ def insert( **geo** (:obj:`Shapely.geometry`): Any valid shapely geometry """ self.built = True - if env == "QGIS": + if inside_qgis: g = QgsGeometry() g.fromWkb(geometry.wkb) feature = QgsFeature() feature.setGeometry(g) feature.setId(feature_id) self.idx.addFeature(feature) - elif env == "Python": + elif rtree_avail: self.idx.insert(feature_id, geometry.bounds) else: warnings.warn("You need RTREE to build a spatial index") @@ -67,24 +58,24 @@ def nearest(self, geo: Union[Point, Polygon, LineString, MultiPoint, MultiPolygo :Returns: **neighbors** (:obj:`List[int]`): List of IDs of the closest neighbors in the index """ - if env == "QGIS": + if inside_qgis: g = QgsGeometry() g.fromWkb(geo.wkb) return self.idx.nearestNeighbor(g, num_results) - elif env == "Python": + elif rtree_avail: return self.idx.nearest(geo.bounds, num_results) else: warnings.warn("You need RTREE to build a spatial index") def delete(self, feature_id, geometry: Union[Point, Polygon, LineString, MultiPoint, MultiPolygon]): - if env == "QGIS": + if inside_qgis: g = QgsGeometry() g.fromWkb(geometry.wkb) feature = QgsFeature() feature.setGeometry(g) feature.setId(feature_id) self.idx.deleteFeature(feature) - elif env == "Python": + elif rtree_avail: self.idx.delete(feature_id, geometry.bounds) else: warnings.warn("You need RTREE to build a spatial index") diff --git a/aequilibrae/utils/interface/__init__.py b/aequilibrae/utils/interface/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/aequilibrae/utils/interface/worker_thread.py b/aequilibrae/utils/interface/worker_thread.py new file mode 100644 index 000000000..f537886bf --- /dev/null +++ b/aequilibrae/utils/interface/worker_thread.py @@ -0,0 +1,27 @@ +from aequilibrae.utils.qgis_utils import inside_qgis + +if inside_qgis: + from PyQt5.QtCore import QThread + from PyQt5.QtCore import pyqtSignal + + class WorkerThread(QThread): + if inside_qgis: + jobFinished = pyqtSignal(object) + + def __init__(self, parentThread): + QThread.__init__(self, parentThread) + + def run(self): + self.running = True + success = self.doWork() + if inside_qgis: + self.jobFinished.emit(success) + + def stop(self): + self.running = False + +else: + + class WorkerThread: # type: ignore + def __init__(self, *arg): + pass diff --git a/aequilibrae/utils/python_signal.py b/aequilibrae/utils/python_signal.py index 25b7b7da0..abc90d793 100644 --- a/aequilibrae/utils/python_signal.py +++ b/aequilibrae/utils/python_signal.py @@ -3,6 +3,8 @@ import warnings from random import choice +from aequilibrae.utils.qgis_utils import inside_qgis + missing_tqdm = iutil.find_spec("tqdm") is None if not missing_tqdm: @@ -12,8 +14,6 @@ else: from tqdm import tqdm # type: ignore -qgis = iutil.find_spec("qgis") is not None - show_status = os.environ.get("AEQ_SHOW_PROGRESS", "FALSE") == "TRUE" @@ -64,7 +64,7 @@ def emit(self, val): self.keydata[val[1]] = val[2] elif val[0] == "start": - if missing_tqdm and not qgis: + if missing_tqdm and not inside_qgis: self.deactivate = True warnings.warn("No progress bars will be shown. Please install tqdm to see them") if self.pbar is not None: diff --git a/aequilibrae/utils/qgis_utils.py b/aequilibrae/utils/qgis_utils.py index b87a159ea..d066031a2 100644 --- a/aequilibrae/utils/qgis_utils.py +++ b/aequilibrae/utils/qgis_utils.py @@ -2,3 +2,4 @@ # If we can find the qgis module to import ... we are running inside qgis inside_qgis = iutil.find_spec("qgis") is not None +rtree_avail = iutil.find_spec("rtree") is not None diff --git a/aequilibrae/utils/signal.py b/aequilibrae/utils/signal.py index a5a467030..c8356bd3d 100644 --- a/aequilibrae/utils/signal.py +++ b/aequilibrae/utils/signal.py @@ -1,11 +1,11 @@ -import importlib.util as iutil +from aequilibrae.utils.qgis_utils import inside_qgis def noop(_): pass -if iutil.find_spec("qgis") is not None: +if inside_qgis: from PyQt5.QtCore import pyqtSignal as SIGNAL # type: ignore noop(SIGNAL.__class__) # This should be no-op but it stops PyCharm from "optimising" the above import diff --git a/tests/aequilibrae/project/test_osm_downloader.py b/tests/aequilibrae/project/test_osm_downloader.py index 7c5fe3bb7..e0d0289b0 100644 --- a/tests/aequilibrae/project/test_osm_downloader.py +++ b/tests/aequilibrae/project/test_osm_downloader.py @@ -1,4 +1,3 @@ -import importlib.util as iutil import os from random import random from tempfile import gettempdir @@ -8,9 +7,6 @@ from aequilibrae.project.network.osm.osm_downloader import OSMDownloader -spec = iutil.find_spec("PyQt5") -pyqt = spec is not None - class TestOSMDownloader(TestCase): def setUp(self) -> None: