diff --git a/Searcher/searchAddress.py b/Searcher/searchAddress.py
index 8ea2acc..1d61e05 100644
--- a/Searcher/searchAddress.py
+++ b/Searcher/searchAddress.py
@@ -1,16 +1,18 @@
import json
import os
from http.client import IncompleteRead
+from json import JSONDecodeError
+from typing import Union, Tuple
from urllib.error import HTTPError, URLError
from urllib.parse import quote
from urllib.request import urlopen
from qgis.PyQt.QtCore import QVariant
from qgis.core import QgsGeometry, QgsFeature, QgsField, QgsFields, \
- QgsProject, QgsVectorLayer, QgsMessageLog, Qgis
+ QgsVectorLayer, QgsMessageLog, Qgis
from qgis.utils import iface
-from ..utils import tr, add_map_layer_to_group, search_group_name
+from ..utils import tr, add_map_layer_to_group, search_group_name, project
class SearchAddress:
@@ -35,7 +37,7 @@ def __init__(self):
QgsField("y", QVariant.Double, "double", 10, 4),
]
- def fetch_address(self, address):
+ def fetch_address(self, address: str) -> None:
self.address = address
uug = 'https://services.gugik.gov.pl/uug?request=GetAddress&address='
url = uug + quote(self.address)
@@ -54,7 +56,7 @@ def fetch_address(self, address):
return
self.res.decode()
- def get_layer(self):
+ def get_layer(self) -> Union[bool, Tuple[str]]:
req_type = self.jres['type']
if req_type in ['city', 'address']:
org = 'MultiPoint?crs=epsg:2180&index=yes'
@@ -73,8 +75,9 @@ def get_layer(self):
return False
return org, obj_type, qml
- def get_layer_data(self, org, obj_type, qml):
- lyr = QgsProject.instance().mapLayersByName(obj_type)
+ def get_layer_data(self, org: str, obj_type: str,
+ qml: str) -> QgsVectorLayer:
+ lyr = project.mapLayersByName(obj_type)
if lyr:
return lyr[0]
@@ -94,10 +97,11 @@ def get_layer_data(self, org, obj_type, qml):
lyr.loadNamedStyle(os.path.join(direc, qml))
return lyr
- def process_results(self):
+ def process_results(self) -> Union[
+ Tuple[bool, str], Tuple[bool, QgsFeature]]:
try:
self.jres = json.loads(self.res)
- except Exception:
+ except JSONDecodeError:
return False, tr('Cannot parse results.')
if 'found objects' in self.jres:
@@ -145,14 +149,14 @@ def process_results(self):
return True, feats
- def zoom_to_feature(self, layer):
- layer = QgsProject.instance().mapLayersByName(layer)[0]
+ def zoom_to_feature(self, layer: str) -> None:
+ layer = project.mapLayersByName(layer)[0]
iface.mapCanvas().zoomScale(500)
layer.selectByIds([len(layer)])
iface.mapCanvas().zoomToSelected(layer)
layer.removeSelection()
- def add_feats(self, feats):
+ def add_feats(self, feats: QgsFeature) -> Union[bool, None]:
if isinstance(feats, str):
pass
else:
diff --git a/Searcher/searchParcel.py b/Searcher/searchParcel.py
index 9ec2107..385c16a 100644
--- a/Searcher/searchParcel.py
+++ b/Searcher/searchParcel.py
@@ -2,15 +2,16 @@
import re
import socket
from http.client import IncompleteRead
+from typing import List
from urllib.error import HTTPError, URLError
from urllib.request import urlopen
from qgis.core import QgsGeometry, QgsFeature, \
- QgsProject, QgsVectorLayer, Qgis
+ QgsVectorLayer, Qgis
from qgis.utils import iface
from ..utils import tr, CustomMessageBox, search_group_name, \
- add_map_layer_to_group
+ add_map_layer_to_group, project
class FetchULDK:
@@ -18,7 +19,7 @@ def __init__(self, params=None):
self.params = params
self.responce = []
- def fetch_list(self, area, teryt):
+ def fetch_list(self, area: str, teryt: str) -> bool:
map(str, teryt)
if area not in {'powiat', 'gmina', 'obreb'}:
self.responce = []
@@ -26,11 +27,11 @@ def fetch_list(self, area, teryt):
self.params = f'obiekt={area}&wynik=nazwa%2Cteryt&teryt={teryt}&'
return self.fetch()
- def fetch_voivodeships(self):
+ def fetch_voivodeships(self) -> bool:
self.params = 'obiekt=wojewodztwo&wynik=nazwa%2Cteryt&teryt=&'
return self.fetch()
- def fetch_parcel(self, teryt):
+ def fetch_parcel(self, teryt: str) -> bool:
self.responce = []
if not isinstance(teryt, str):
return False
@@ -38,19 +39,15 @@ def fetch_parcel(self, teryt):
'geom_wkt,teryt,voivodeship,county,region,commune,parcel'
return self.fetch()
- def fetch_in_point(self, coords):
- # TODO: Dodac pobieranie działki w pkt po kliknieciu
- pass
-
- def fetch(self):
+ def fetch(self) -> bool:
if '- gmina' in self.params or '- miasto' in self.params:
flag = self.params.find('-')
self.params = self.params[0:flag]
url = f'https://uldk.gugik.gov.pl/?{self.params}'
self.responce = []
try:
- with urlopen(url, timeout=19) as r:
- content = r.read()
+ with urlopen(url, timeout=19) as url_handler:
+ content = url_handler.read()
except IncompleteRead:
CustomMessageBox(None,
f"{tr('Error!')} {tr('Service returned incomplete responce.')}").button_ok()
@@ -75,10 +72,11 @@ def fetch(self):
f"{tr('Service did not find any matches, wrong plot number.')}").button_ok()
return False
- self.responce = self.natural_sort([x for x in res[1:] if x != ''])
+ self.responce = self.natural_sort(
+ [ter for ter in res[1:] if ter != ''])
return True
- def natural_sort(self, list):
+ def natural_sort(self, list: List[str]) -> List[str]:
convert = lambda text: int(text) if text.isdigit() else text.lower()
alpha_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
return sorted(list, key=alpha_key)
@@ -97,8 +95,8 @@ def __init__(self):
self.lyr_name, 'memory'
)
- def get_layer(self):
- lyr = QgsProject.instance().mapLayersByName(self.lyr_name)
+ def get_layer(self) -> None:
+ lyr = project.mapLayersByName(self.lyr_name)
if len(lyr) > 0:
self.lyr = lyr[0]
return
@@ -108,7 +106,7 @@ def get_layer(self):
self.lyr.loadNamedStyle(
os.path.join(direc, 'layer_style', 'dzialki.qml'))
- def parse_responce(self, resp):
+ def parse_responce(self, resp: List[str]) -> None:
feats = []
for row in resp:
ft = self._create_feature(row)
@@ -129,14 +127,14 @@ def parse_responce(self, resp):
Qgis.Warning
)
- def zoom_to_feature(self, layer):
- layer = QgsProject.instance().mapLayersByName(layer)[0]
+ def zoom_to_feature(self, layer: str) -> None:
+ layer = project.mapLayersByName(layer)[0]
iface.mapCanvas().zoomScale(500)
layer.selectByIds([len(layer)])
iface.mapCanvas().zoomToSelected(layer)
layer.removeSelection()
- def _create_feature(self, row):
+ def _create_feature(self, row: str) -> QgsFeature:
if row[:4].upper() == 'SRID':
row = row[row.index(';') + 1:]
feat = QgsFeature()
diff --git a/Searcher/searchTool.py b/Searcher/searchTool.py
index 22bd049..35a56a8 100644
--- a/Searcher/searchTool.py
+++ b/Searcher/searchTool.py
@@ -1,18 +1,22 @@
import json
+from json import JSONDecodeError
from urllib.parse import quote
from urllib.request import urlopen
import requests
from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QFont, QFontMetrics
+from qgis.PyQt.QtGui import QFont, QFontMetrics
from qgis.PyQt.QtCore import QStringListModel
from qgis.PyQt.QtCore import QTimer
from qgis.PyQt.QtWidgets import QCompleter
+from qgis.core import QgsVectorLayer
from qgis.utils import iface
+from typing import Union, Dict, List
from .searchAddress import SearchAddress
from .searchParcel import FetchULDK, ParseResponce
-from ..utils import tr, CustomMessageBox
+from ..utils import tr, CustomMessageBox, add_map_layer_to_group, \
+ ProgressDialog, identify_layer_in_group, root, WFS_PRG
class SearcherTool:
@@ -53,10 +57,10 @@ def __init__(self, dock, iface):
self.font = QFont('Agency FB')
self.fontm = QFontMetrics(self.font)
- def textChanged(self):
+ def textChanged(self) -> None:
self.typing_timer.start(300)
- def tips(self):
+ def tips(self) -> None:
address = self.dock.lineEdit_address.displayText()
url_pref = 'http://services.gugik.gov.pl/uug/?request=GetAddress&address='
quo_adr = quote(address)
@@ -79,32 +83,32 @@ def tips(self):
self.getStreets(obj['1']['simc'], obj['1']['city'])
if obj_type == 'address':
self.names.setStringList([
- f"{obj['1']['city']}, {obj['1']['street']} {obj['1']['number']}"])
- if limit == 0:
+ f"{obj['1']['city']}, {obj['1']['street']} {obj['1']['number']}"])
+ if not limit:
return
self.completer.setCompletionPrefix(f"{address.split(',')[0]}, ")
self.completer.complete()
- except Exception:
+ except (JSONDecodeError, TypeError):
return
- def getStreets(self, simc, city):
+ def getStreets(self, simc: str, city: str) -> None:
try:
data = json.loads(urlopen(
'https://services.gugik.gov.pl/uug/?request=GetStreet&simc=' + simc).read().decode())
obj = data['results']
self.names.setStringList(
[f"{city}, {obj[element]['street']}" for element in obj])
- except Exception:
+ except TypeError:
self.names.setStringList([])
- def validateCity(self, obj):
+ def validateCity(self, obj: Dict[str, Dict[str, int]]) -> None:
city = obj['1']['city']
self.names.setStringList(
[f"{city}, {obj[element]['simc']} {obj[element]['county']}" for
element in obj])
self.completer.popup().pressed.connect(lambda: self.userPick())
- def userPick(self):
+ def userPick(self) -> None:
line = self.dock.lineEdit_address.text().split()
self.completer.popup().pressed.disconnect()
if len(line) == 3:
@@ -116,7 +120,7 @@ def userPick(self):
else:
return
- def search_address(self):
+ def search_address(self) -> None:
validate_address = self.validate_lineedit()
if validate_address:
lineedit = self.dock.lineEdit_address.text().split(',')
@@ -141,19 +145,65 @@ def change_scale():
self.timer.timeout.connect(change_scale)
self.timer.start(10)
- def validate_lineedit(self):
+ def validate_lineedit(self) -> bool:
if self.dock.lineEdit_address.text():
return True
else:
CustomMessageBox(None,
f" {tr('Invalid')} {tr('Empty address field')}").button_ok()
- def widthforview(self, result):
+ def add_chosen_border(self, mess: str) -> None:
+ lay_data = {'Obręby_ewidencyjne': ["A06_Granice_obrebow_ewidencyjnych",
+ self.dock.comboBox_obr],
+ 'Gminy': ["A03_Granice_gmin", self.dock.comboBox_gmina],
+ 'Powiaty': ["A02_Granice_powiatow",
+ self.dock.comboBox_pow],
+ 'Województwa': ["A01_Granice_wojewodztw",
+ self.dock.comboBox_woj]}
+ for lay_key in lay_data:
+ if lay_data[lay_key][1].currentIndex():
+ _, jpt_kod_je = lay_data[lay_key][1].currentText().split("|")
+ if lay_key == "Gminy":
+ jpt_kod_je = jpt_kod_je.replace("_", "")
+ adres = lay_data[lay_key][0]
+ lay_name = lay_key
+ break
+ if 'jpt_kod_je' not in locals() or 'adres' not in locals()\
+ or 'lay_name' not in locals():
+ CustomMessageBox(self.iface.mainWindow(),
+ mess).button_ok()
+ return
+ prg_dlg = ProgressDialog(self.iface.mainWindow())
+ prg_dlg.start_steped(tr("Adding layers..."))
+ prg_dlg.start()
+ url = f"{WFS_PRG}?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAME=ms:{adres}&TYPENAMES=ms:{adres}"
+ vlayer = QgsVectorLayer(url, "wfs_lay", "WFS")
+ vlayer.setSubsetString(f"""SELECT * FROM {adres}
+ WHERE JPT_KOD_JE = '{jpt_kod_je}'""")
+ group_name = "GRANICE"
+ granice_group = root.findGroup(group_name)
+ if not granice_group:
+ root.addGroup(group_name)
+ lay = identify_layer_in_group(group_name, lay_name)
+ if not lay:
+ lay = QgsVectorLayer("Polygon", lay_name, "memory")
+ attr = vlayer.dataProvider().fields().toList()
+ lay.dataProvider().addAttributes(attr)
+ lay.updateFields()
+ add_map_layer_to_group(lay, "GRANICE")
+ vlayer.selectAll()
+ feat = vlayer.selectedFeatures()[0]
+ lay.dataProvider().addFeature(feat)
+ lay.updateExtents()
+ self.searchaddress_call.zoom_to_feature(lay.name())
+ prg_dlg.stop()
+
+ def widthforview(self, result: List[str]) -> int:
longest = max(result, key=len)
width = 2 * self.fontm.width(longest)
return width
- def fetch_voivodeship(self):
+ def fetch_voivodeship(self) -> None:
"""Fetching voivodeship list from GUGiK"""
voi_list = [
'Dolnośląskie|02',
@@ -178,7 +228,7 @@ def fetch_voivodeship(self):
self.dock.comboBox_woj.blockSignals(False)
self.clear_comboBoxes('voi')
- def woj_changed(self):
+ def woj_changed(self) -> None:
voi = self._get_voi_code()
if not voi:
return
@@ -191,7 +241,7 @@ def woj_changed(self):
self.dock.comboBox_pow.view().setFixedWidth(self.widthforview(result))
self.dock.comboBox_pow.blockSignals(False)
- def pow_changed(self):
+ def pow_changed(self) -> None:
dis = self._get_dis_code()
if not dis:
return
@@ -200,7 +250,7 @@ def pow_changed(self):
fe.fetch_list('gmina', dis)
self.dock.comboBox_gmina.blockSignals(True)
result, communities = fe.responce, []
- multiples = [e.split('|')[0] for e in result]
+ multiples = [powiat.split('|')[0] for powiat in result]
for district in result:
end = district[-2:]
if multiples.count(district.split('|')[0]) > 1:
@@ -217,7 +267,7 @@ def pow_changed(self):
self.widthforview(communities))
self.dock.comboBox_gmina.blockSignals(False)
- def gmi_changed(self):
+ def gmi_changed(self) -> None:
mun = self._get_mun_code()
self.clear_comboBoxes('mun')
if not mun:
@@ -230,7 +280,7 @@ def gmi_changed(self):
self.dock.comboBox_obr.view().setFixedWidth(self.widthforview(result))
self.dock.comboBox_obr.blockSignals(False)
- def clear_comboBoxes(self, level=None):
+ def clear_comboBoxes(self, level: str = None) -> None:
"""Clear comboboxes to level where user change something"""
self.dock.comboBox_obr.blockSignals(True)
self.dock.comboBox_obr.clear()
@@ -251,34 +301,37 @@ def clear_comboBoxes(self, level=None):
self.dock.comboBox_pow.addItem(tr('District'))
self.dock.comboBox_pow.blockSignals(False)
- def _get_voi_code(self):
+ def _get_voi_code(self) -> Union[str, bool]:
voi_txt = self.dock.comboBox_woj.currentText()
if '|' not in voi_txt:
self.clear_comboBoxes()
return False
return voi_txt.split('|')[1]
- def _get_dis_code(self):
+ def _get_dis_code(self) -> Union[str, bool]:
dis_txt = self.dock.comboBox_pow.currentText()
if '|' not in dis_txt:
self.clear_comboBoxes('dis')
return False
return dis_txt.split('|')[1]
- def _get_mun_code(self):
+ def _get_mun_code(self) -> Union[str, bool]:
mun_txt = self.dock.comboBox_gmina.currentText()
if '|' not in mun_txt:
self.clear_comboBoxes('mun')
return False
return mun_txt.split('|')[1]
- def search_parcel(self):
- adr = '' # ful address of parcel
+ def search_parcel(self) -> None:
parc = self.dock.lineEdit_parcel.text()
if '.' in parc and '_' in parc: # user input whole address in parcel
adr = parc
else:
comm = self.dock.comboBox_obr.currentText()
+ if not parc:
+ self.add_chosen_border(
+ f"{tr('Address of parcel is not valid.')}")
+ return
if '|' not in comm:
CustomMessageBox(None,
f"{tr('Address of parcel is not valid.')}").button_ok()
@@ -286,9 +339,9 @@ def search_parcel(self):
comm = comm.split('|')[1]
adr = f'{comm}.{parc}'
- f = FetchULDK()
- if not f.fetch_parcel(adr):
+ feULDK = FetchULDK()
+ if not feULDK.fetch_parcel(adr):
return
pr = ParseResponce()
pr.get_layer()
- pr.parse_responce(f.responce)
+ pr.parse_responce(feULDK.responce)
diff --git a/i18n/giap_pl.ts b/i18n/giap_pl.ts
index 139688e..7cabbdd 100644
--- a/i18n/giap_pl.ts
+++ b/i18n/giap_pl.ts
@@ -647,6 +647,11 @@ Proszę czekać...
Puste pole adresu
+
+
+
+ Dodawanie warstwy...
+
diff --git a/utils.py b/utils.py
index 94f5906..593f277 100644
--- a/utils.py
+++ b/utils.py
@@ -14,6 +14,7 @@
from qgis.utils import iface
project = QgsProject.instance()
+root = project.layerTreeRoot()
class CustomMessageBox(QMessageBox):
@@ -124,6 +125,7 @@ def set_size(self, value: int) -> None:
self.stylesheet = f'*{{font: {value}pt;}} {self.stylesheet}'
self.setStyleSheet(self.stylesheet)
+
class SingletonModel:
__instance = None
@@ -136,6 +138,56 @@ def __new__(cls, *args):
return SingletonModel.__instance
+class ProgressDialog(QProgressDialog, SingletonModel):
+
+ def __init__(self, parent=None, title='GIAP-PolaMap(lite)'):
+ super(ProgressDialog, self).__init__(parent)
+ self.setWindowTitle(title)
+ self.setWindowIcon(icon_manager(['window_icon'])['window_icon'])
+ self.setLabelText('Proszę czekać...')
+ self.setFixedWidth(300)
+ self.setFixedHeight(100)
+ self.setMaximum(100)
+ self.setCancelButton(None)
+ self.setWindowFlags(Qt.Dialog | Qt.WindowCloseButtonHint)
+ self.rejected.connect(self.stop)
+ self.setWindowModality(Qt.WindowModal)
+
+ def make_percent_step(self, step=100, new_text=None):
+ self.setStyleSheet(self.stylesheet)
+ if new_text:
+ self.setLabelText(new_text)
+ if "wczytywanie" in new_text:
+ for pos in range(100 - self.value()):
+ QApplication.processEvents()
+ self.setValue(self.value() + 1)
+ return
+ for pos in range(step):
+ QApplication.processEvents()
+ self.setValue(self.value() + 1)
+ QApplication.sendPostedEvents()
+ QApplication.processEvents()
+
+ def start_steped(self, title='Trwa ładowanie danych.\n Proszę czekać...'):
+ self.setLabelText(title)
+ self.setValue(1)
+ self.show()
+ QApplication.sendPostedEvents()
+ QApplication.processEvents()
+
+ def start(self):
+ self.setFixedWidth(250)
+ self.setMaximum(0)
+ self.setCancelButton(None)
+ self.show()
+ QApplication.sendPostedEvents()
+ QApplication.processEvents()
+
+ def stop(self):
+ self.setValue(100)
+ self.close()
+
+
def identify_layer(ls, layer_to_find):
for layer in list(ls.values()):
if layer.name() == layer_to_find:
@@ -257,6 +309,7 @@ def paint(self, painter, option, index):
GIAP_NEWS_WEB_PAGE = 'https://www.giap.pl/aktualnosci/'
+WFS_PRG = "https://mapy.geoportal.gov.pl/wss/service/PZGIK/PRG/WFS/AdministrativeBoundaries"
# oba poniższe słowniki powinny być spójne
WMS_SERVERS = {
@@ -1197,6 +1250,7 @@ def paint(self, painter, option, index):
max_ele_nazwy = 4
+
def icon_manager(tool_list: List[str], main_qgs_widget: QObject = None) -> \
Dict[str, Union[Optional[QIcon], Any]]:
dirnm = normalize_path(os.path.join(os.path.dirname(__file__), 'icons'))
@@ -1244,14 +1298,13 @@ def add_map_layer_to_group(
f'Warstwa nieprawidłowa {layer.name()}. Wymagana interwencja.',
"GIAP - PolaMap Lite",
Qgis.Info)
- root = project.layerTreeRoot()
if main_group_name and root.findGroup(main_group_name):
group = root.findGroup(main_group_name).findGroup(group_name)
else:
group = root.findGroup(group_name)
if not group:
if force_create:
- group = project.layerTreeRoot().addGroup(group_name)
+ group = root.addGroup(group_name)
else:
project.addMapLayer(layer)
return
@@ -1259,6 +1312,7 @@ def add_map_layer_to_group(
if group_name:
group.insertLayer(position, layer)
+
def find_widget_with_menu_in_toolbar(toolbar: QToolBar) -> List[QToolButton]:
lista_widgets = toolbar.children()
qmenu_list = []
@@ -1268,6 +1322,7 @@ def find_widget_with_menu_in_toolbar(toolbar: QToolBar) -> List[QToolButton]:
qmenu_list.append(widget)
return qmenu_list
+
def get_action_from_toolbar(toolbar: QToolBar) -> List[QAction]:
lista_widgets = toolbar.children()
act_list = []
@@ -1277,9 +1332,10 @@ def get_action_from_toolbar(toolbar: QToolBar) -> List[QAction]:
act_list.append(widget.actions()[0])
return act_list
+
def add_action_from_toolbar(iface: iface, sec, btn: list) -> None:
if iface.mainWindow().findChild(QToolBar, btn[0].split('_')[0]):
- dlu = len(btn[0].split('_'))
+ dlu = len(btn[0].split('_'))
if dlu == max_ele_nazwy:
objname_toolbar, ind, typ, ind_menu = btn[0].split('_')
else:
@@ -1298,22 +1354,23 @@ def add_action_from_toolbar(iface: iface, sec, btn: list) -> None:
if dlu == max_ele_nazwy:
if widg.menu():
- #Wyciąganie i dodawanie pojdeynczej akcji z menu
+ # Wyciąganie i dodawanie pojdeynczej akcji z menu
sel_act_from_menu = widg.menu().actions()[int(ind_menu)]
objname = sel_act_from_menu.objectName()
sel_act_from_menu.setObjectName(btn[0])
sec.add_action(sel_act_from_menu, btn[1], btn[2])
sel_act_from_menu.setObjectName(objname)
else:
- #Dodawanie wybranej akcji
+ # Dodawanie wybranej akcji
sel_act = widg.actions()[int(ind_menu)]
objname = sel_act.objectName()
sel_act.setObjectName(btn[0])
sec.add_action(sel_act, btn[1], btn[2])
sel_act.setObjectName(objname)
else:
- #Dodawanie menu i domyślnej akcji
+ # Dodawanie menu i domyślnej akcji
objname = widg.defaultAction().objectName()
widg.defaultAction().setObjectName(btn[0])
- sec.add_action(widg.defaultAction(), btn[1], btn[2], widg.menu())
- widg.defaultAction().setObjectName(objname)
\ No newline at end of file
+ sec.add_action(widg.defaultAction(), btn[1], btn[2],
+ widg.menu())
+ widg.defaultAction().setObjectName(objname)