Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development #27

Merged
merged 13 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 31 additions & 9 deletions .github/workflows/test_plugin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,48 @@ jobs:

Tests-plugin-LoS-Tools:

runs-on: ubuntu-latest
runs-on: ubuntu-24.04

steps:

- name: GIS Sources
- name: Install
run: |
sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable
sudo apt-get install python3-pytest python3-pytest-cov

- name: Install QGIS
- name: Prepare QGIS
run: |
sudo wget -qO /etc/apt/keyrings/qgis-archive-keyring.gpg https://download.qgis.org/downloads/qgis-archive-keyring.gpg
sudo sh -c 'echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/qgis-archive-keyring.gpg] https://qgis.org/ubuntugis `lsb_release -c -s` main" > /etc/apt/sources.list.d/qgis.list'
sudo apt-get update
sudo apt-get install -y qgis
sudo gpg -k && \
KEYRING=/usr/share/keyrings/qgis-archive-keyring.gpg && \
wget -O $KEYRING https://download.qgis.org/downloads/qgis-archive-keyring.gpg && \
sudo touch /etc/apt/sources.list.d/qgis.sources && \
echo 'Types: deb deb-src' | sudo tee -a /etc/apt/sources.list.d/qgis.sources && \
echo 'URIs: https://qgis.org/ubuntugis' | sudo tee -a /etc/apt/sources.list.d/qgis.sources && \
echo 'Suites: '$(lsb_release -c -s) | sudo tee -a /etc/apt/sources.list.d/qgis.sources && \
echo 'Architectures: '$(dpkg --print-architecture) | sudo tee -a /etc/apt/sources.list.d/qgis.sources && \
echo 'Components: main' | sudo tee -a /etc/apt/sources.list.d/qgis.sources && \
echo 'Signed-By: '$KEYRING | sudo tee -a /etc/apt/sources.list.d/qgis.sources && \
LASTSUPPORTED=focal && \
KEYRING=/usr/share/keyrings/ubuntugis-archive-keyring.gpg && \
sudo gpg --no-default-keyring --keyring $KEYRING --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 6B827C12C2D425E227EDCA75089EBE08314DF160 && \
sudo touch /etc/apt/sources.list.d/ubuntugis-unstable.sources && \
echo 'Types: deb deb-src' | sudo tee -a /etc/apt/sources.list.d/ubuntugis-unstable.sources && \
echo 'URIs:https://ppa.launchpadcontent.net/ubuntugis/ubuntugis-unstable/ubuntu' | sudo tee -a /etc/apt/sources.list.d/ubuntugis-unstable.sources && \
echo 'Suites: '$(lsb_release -c -s)| sudo tee -a /etc/apt/sources.list.d/ubuntugis-unstable.sources && \
echo 'Architectures: '$(dpkg --print-architecture) | sudo tee -a /etc/apt/sources.list.d/ubuntugis-unstable.sources && \
echo 'Components: main' | sudo tee -a /etc/apt/sources.list.d/ubuntugis-unstable.sources && \
echo 'Signed-By: '$KEYRING | sudo tee -a /etc/apt/sources.list.d/ubuntugis-unstable.sources

- name: Install
run: |
sudo apt-get update && \
sudo apt-get -y -q install --no-install-recommends wget software-properties-common build-essential ca-certificates python3-pip dialog apt-utils && \
sudo apt -y -q install qgis qgis-dev qgis-plugin-grass

- name: QGIS Version
run: qgis --version

- name: Install Python packages
run: pip install pytest pytest-qgis pytest-cov
run: pip install pytest-qgis --break-system-packages

- name: Checkout
uses: actions/checkout@v4
Expand Down
46 changes: 46 additions & 0 deletions los_tools/classes/classes_los.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

import copy
import math
import typing
from typing import List, Optional, Union

from qgis.core import QgsFeature, QgsGeometry, QgsPoint
Expand Down Expand Up @@ -156,6 +158,8 @@ def is_visible_at_index(self, index: int, return_integer: bool = False) -> Union
return int(self.visible[index]) if return_integer else self.visible[index]

def get_geom_at_index(self, index: int) -> QgsPoint:
if index == -1:
index = 0
point = QgsPoint(
self.points[index][self.X],
self.points[index][self.Y],
Expand Down Expand Up @@ -505,6 +509,38 @@ def from_feature(
sampling_distance=sampling_distance,
)

@classmethod
def from_another(
cls,
other: LoSWithoutTarget,
distance_limit: typing.Optional[float] = None,
) -> LoSWithoutTarget:
obj = LoSWithoutTarget.__new__(LoSWithoutTarget)
obj.observer_offset = other.observer_offset
obj.use_curvature_corrections = other.use_curvature_corrections
obj.refraction_coefficient = other.refraction_coefficient
obj.is_global = False
obj.is_without_target = True
obj.target_offset = 0
obj.target_x = None
obj.target_y = None
obj.target_index = None
obj.global_horizon_index = None

if distance_limit is None:
obj.points = copy.deepcopy(other.points)
obj.previous_max_angle = copy.deepcopy(other.previous_max_angle)
obj.visible = copy.deepcopy(other.visible)
obj.horizon = copy.deepcopy(other.horizon)
else:
index_limit = other._get_distance_limit_index(distance_limit)
obj.points = copy.deepcopy(other.points[:index_limit])
obj.previous_max_angle = copy.deepcopy(other.previous_max_angle[:index_limit])
obj.visible = copy.deepcopy(other.visible[:index_limit])
obj.horizon = copy.deepcopy(other.horizon[:index_limit])

return obj

def get_horizontal_angle(self) -> float:
azimuth = QgsPoint(self.points[0][self.X], self.points[0][self.Y]).azimuth(
QgsPoint(self.points[-1][self.X], self.points[-1][self.Y])
Expand Down Expand Up @@ -579,3 +615,13 @@ def get_global_horizon_elevation_difference(self):

else:
return None

def _get_distance_limit_index(self, distance: float) -> int:
index = len(self.points) - 1

for i in range(1, len(self.points)):
if self.points[i][self.DISTANCE] > distance:
index = i
break

return index
2 changes: 2 additions & 0 deletions los_tools/constants/field_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,5 @@ class FieldNames:

ANGLE_STEP = "angle_step_between_los"
ANGLE_STEP_POINTS = "angle_step_between_points"

HORIZON_DISTANCE = "horizon_distance"
8 changes: 2 additions & 6 deletions los_tools/gui/create_los_tool/create_los_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,7 @@ def canvasReleaseEvent(self, e: QgsMapMouseEvent) -> None:
self._start_point = None

def canvasMoveEvent(self, event: QgsMapMouseEvent) -> None:
result = self._snapper.snapToMap(event.pos())
self.snap_marker.setMatch(result)
if result.type() == QgsPointLocator.Vertex:
self._snap_point = result.point()
else:
self._snap_point = event.mapPoint()
self._set_snap_point(event)

if self._start_point is not None:
self.draw_los(self._snap_point)
Expand Down Expand Up @@ -216,6 +211,7 @@ def add_los_to_layer(self) -> None:
task.taskFinishedTime.connect(self.task_finished_message)

self.task_manager.addTask(task)
self.clean()

def task_finished(self) -> None:
self.featuresAdded.emit()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from qgis.core import Qgis, QgsPointLocator
import typing

from qgis.core import Qgis, QgsPointLocator, QgsPointXY
from qgis.gui import QgisInterface, QgsMapMouseEvent, QgsMapToolAdvancedDigitizing, QgsSnapIndicator
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtGui import QKeyEvent
Expand All @@ -20,6 +22,8 @@ def __init__(self, iface: QgisInterface) -> None:

self._los_rubber_band = self.createRubberBand(Qgis.GeometryType.Line)

self._snap_point: typing.Optional[QgsPointXY] = None

def activate(self) -> None:
super().activate()

Expand Down Expand Up @@ -70,7 +74,7 @@ def keyPressEvent(self, e: QKeyEvent) -> None:
self._iface.mapCanvas().unsetMapTool(self)
return super().keyPressEvent(e)

def canvasMoveEvent(self, event: QgsMapMouseEvent) -> None:
def _set_snap_point(self, event: QgsMapMouseEvent) -> None:
result = self._snapper.snapToMap(event.pos())
self.snap_marker.setMatch(result)
if result.type() == QgsPointLocator.Vertex:
Expand Down
7 changes: 4 additions & 3 deletions los_tools/gui/dialog_los_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@ def init_gui(self) -> None:
self.maximal_los_length.setValue(100, QgsUnitTypes.DistanceKilometers)
self.maximal_los_length.valueChanged.connect(self.fill_distances)
self.maximal_los_length.setDisabled(True)

self.use_maximal_los_length = QCheckBox()
self.use_maximal_los_length.stateChanged.connect(self.fill_distances)
self.use_maximal_los_length.toggled.connect(self.fill_distances)

layout_group_box_los.addRow("Default Sampling Size", self.default_sampling_size)
layout_group_box_los.addRow("Use Maximal LoS Length", self.use_maximal_los_length)
Expand Down Expand Up @@ -209,7 +210,7 @@ def remove_distance(self) -> None:
self.fill_distances()

def fill_distances(self) -> None:
self.maximal_los_length.setEnabled(self.use_maximal_los_length.isChecked())
self.maximal_los_length.setDisabled(not self.use_maximal_los_length.isChecked())

self.treeView.clear()

Expand Down Expand Up @@ -246,7 +247,7 @@ def fill_distances(self) -> None:
item = QTreeWidgetItem()
item.setText(
0,
f"Over {self._distances[-1]} to {self.maximal_los_length.distance().inUnits(result_unit)}",
f"Over {self._distances[-1]} to {self.maximal_los_length.distance()}",
)
item.setText(1, str(size))

Expand Down
11 changes: 7 additions & 4 deletions los_tools/gui/dialog_raster_validations.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def __init__(self, iface=None) -> None:

self.init_gui()

self._prepare()

def init_gui(self):
self.setMinimumWidth(600)
self.setWindowTitle("Rasters Validation and Sampling")
Expand Down Expand Up @@ -152,16 +154,17 @@ def _default_tools(self) -> None:
self._prev_map_tool = self._canvas.mapTool()
self._prev_cursor = self._canvas.cursor()

def open(self) -> None:
def _prepare(self) -> None:
self._default_tools()
self._populate_raster_view()
self.validate()

def open(self) -> None:
self._prepare()
super().open()

def exec(self) -> int:
self._default_tools()
self._populate_raster_view()
self.validate()
self._prepare()
return super().exec()

def select_sample_point(self) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def canvasReleaseEvent(self, e: QgsMapMouseEvent) -> None:
if e.button() == Qt.RightButton:
self.clean()
if e.button() == Qt.LeftButton:
self._set_snap_point(e)
self.draw_los()

def draw_los(self):
Expand Down
6 changes: 4 additions & 2 deletions los_tools/metadata.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
name=LoS Tools
qgisMinimumVersion=3.30
description=This plugin creates and analyzes lines-of-sight (LoS) and also provides supporting tools.
version=1.1.2
version=1.2.0
author=Jan Caha
[email protected]

Expand All @@ -20,7 +20,9 @@ repository=https://github.com/JanCaha/qgis_los_tools

hasProcessingProvider=yes
# Uncomment the following line and add your changelog:
changelog=1.1.2
changelog=1.2.0
- new tool to extract horizon lines at specific distance
<p>1.1.2
- fix issue with interactive tools not opening properly after closing
<p>1.1.1
- simplify inner working of GUI tools
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"ALG_DESC": "This tool extracts horizon lines from LoS without a target. For other types of LoS, this operation is not applicable. Maximal horizon till specified distance is created. So horizon lines for several distances can be created at once.",
"ALG_CREATOR": "Jan Caha",
"LoSLayer": "LoS layer to analyze.",
"Distances": "Table of distance limits for which horizon lines should be extracted.",
"CurvatureCorrections": "Should curvature and refraction corrections be applied?",
"RefractionCoefficient": "Value of the refraction coefficient. Default value: 0.13.",
"OutputLayer": "Output layer containing the extracted horizon lines."
}
Loading