Skip to content

Commit 84e6d94

Browse files
committed
feat: parse and load layer tree (wms layers)
1 parent 46af22c commit 84e6d94

3 files changed

Lines changed: 61 additions & 166 deletions

File tree

plugin_code/models/Layer.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,6 @@ def from_dict(cls, data: Dict[str, Any]) -> 'Layer':
4747
features=data.get('features'),
4848
layerType=data.get('type')
4949
)
50+
51+
def get_id(self):
52+
return self._id

plugin_code/qgis_shogun_editor.py

Lines changed: 56 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,15 @@
3131
QgsNetworkAccessManager,
3232
QgsPointXY,
3333
QgsProject,
34-
QgsRectangle,
35-
QgsSettings,
3634
QgsRasterLayer,
37-
QgsProject,
38-
QgsMapLayer
35+
QgsSettings,
3936
)
4037

4138
# some things for doing http requests
4239
from qgis.PyQt.QtCore import QCoreApplication, QEventLoop, QSettings, Qt, QTranslator, QUrl
4340
from qgis.PyQt.QtGui import QDesktopServices, QIcon, QPixmap
4441
from qgis.PyQt.QtNetwork import QNetworkRequest, QSslSocket
45-
from qgis.PyQt.QtWidgets import QAction, QTreeWidgetItem, QMessageBox
42+
from qgis.PyQt.QtWidgets import QAction, QMessageBox, QTreeWidgetItem
4643

4744
from .models.Application import Application
4845
from .qgis_shogun_editor_dialog import QgisShogunEditorDialog
@@ -219,7 +216,7 @@ def run(self):
219216
"""Run method that performs all the real work"""
220217

221218
# Create the dialog with elements (after translation) and keep reference
222-
# Only create GUI ONCE in callback, so that it will only load when thie plugin is started
219+
# Only create GUI ONCE in callback, so that it will only load when the plugin is started
223220
if self.first_start:
224221
self.first_start = False
225222
self.dlg = QgisShogunEditorDialog()
@@ -228,7 +225,7 @@ def run(self):
228225

229226
self.dlg.loadButton.clicked.connect(lambda: self.initialize_graphql_and_load_app_list())
230227

231-
self.dlg.loadButton.clicked.connect(lambda: self._handle_double_click)
228+
self.dlg.applicationsList.itemDoubleClicked.connect(self._handle_double_click)
232229

233230
# add logo
234231
logo_path = os.path.join(os.path.dirname(__file__), "shogun_logo.png")
@@ -252,6 +249,18 @@ def run(self):
252249
# substitute with your code.
253250
pass
254251

252+
def find_all_layer_ids(self, layer_tree_json):
253+
layer_ids = []
254+
255+
if "layerId" in layer_tree_json:
256+
layer_ids.append(layer_tree_json["layerId"])
257+
258+
if "children" in layer_tree_json:
259+
for child in layer_tree_json["children"]:
260+
layer_ids.extend(self.find_all_layer_ids(child))
261+
262+
return layer_ids
263+
255264
def _handle_double_click(self, item):
256265
QgsMessageLog.logMessage(
257266
f"You selected: {item.text()} - application id is: {item.application_id}", 'QgisShogunEditor',
@@ -264,10 +273,21 @@ def _handle_double_click(self, item):
264273
if application.layer_tree is not None:
265274
try:
266275
QgsMessageLog.logMessage(
267-
f"Found layer tree {application.layer_tree}:",
276+
f"Found layer tree: {application.layer_tree}",
268277
'QgisShogunEditor',
269278
level=Qgis.Info
270279
)
280+
QgsMessageLog.logMessage(
281+
"Loading layertree to Qgis now.",
282+
'QgisShogunEditor',
283+
level=Qgis.Info
284+
)
285+
286+
root = QgsProject.instance().layerTreeRoot()
287+
root.clear()
288+
layer_ids = self.find_all_layer_ids(application.layer_tree)
289+
layers_content = self.layer_service.get_layers_by_ids(layer_ids)
290+
self.buildLayerTree(application.layer_tree, layers_content, root)
271291
except json.JSONDecodeError as e:
272292
QgsMessageLog.logMessage(
273293
f"Could not decode layer tree json: {e}", 'QgisShogunEditor',
@@ -301,64 +321,36 @@ def open_project_link(self, event):
301321
if event.button() == Qt.LeftButton:
302322
QDesktopServices.openUrl(QUrl("https://www.terrestris.de/de/"))
303323

304-
def check_url_for_applications(self, input_url):
305-
# check url and prepare for applications request
306-
# TODO: check for http and /
307-
input_url = str(input_url)
308-
if len(input_url) == 0:
309-
print('Please enter a valid url.')
310-
return
311-
else:
312-
if input_url.endswith('applications'):
313-
return input_url
314-
elif input_url.endswith('application'):
315-
return input_url + 's'
316-
elif input_url.endswith('/'):
317-
return input_url + 'applications'
318-
319-
def check_url_for_layers(self, input_url):
320-
# check url and prepare for layers request
321-
# TODO: check for http and /
322-
input_url = str(input_url)
323-
if len(input_url) == 0:
324-
print('Please enter a valid url.')
325-
return
326-
else:
327-
if input_url.endswith('layers'):
328-
return input_url
329-
elif input_url.endswith('layer'):
330-
return input_url + 's'
331-
elif input_url.endswith('/'):
332-
return input_url + 'layers'
333-
334-
def createWmsLayerFromShogun(self, layer_src_conf, url, epsg):
324+
def createWmsLayerFromShogun(self, layer_src_conf):
335325
layerNames = layer_src_conf['layerNames']
326+
layer_url = layer_src_conf['url']
327+
if str(layer_url).startswith('/'):
328+
layer_url = self.dlg.entryUrl.text() + layer_url
336329

337330
params = {
338331
'layers': layerNames,
339332
'styles': '',
340333
'format': 'image/png',
341-
'crs': epsg,
342-
'url': url
334+
'crs': 'EPSG:' + str(QgsProject.instance().crs().srsid()),
335+
'url': layer_url
343336
}
344337

345338
# set transparency if present
346339
try:
347340
params['transparent'] = layer_src_conf['requestParams']['TRANSPARENT']
348341
print('set transparent')
349-
except KeyError:
342+
except KeyError:
350343
print('no transparency')
351344

352345
uri = '&'.join([f"{k}={v}" for k, v in params.items()])
353346
layer = QgsRasterLayer(uri, layerNames, 'wms')
354-
print('createWmsLayerFromShogun', uri)
355347

356348
if layer.isValid():
357349
return layer
358350
else:
359351
return False
360352

361-
def createLayer(self, layer_src_conf, epsg, request_url):
353+
def createLayer(self, layer_src_conf):
362354
# layerurl = request_url
363355

364356
# dataType = layerItem.datatype
@@ -375,9 +367,9 @@ def createLayer(self, layer_src_conf, epsg, request_url):
375367
# elif dataType == 'WMS':
376368
# if layerurl == '/shogun2-webapp/geoserver.action':
377369
# url = layerItem.ressource.baseurl.rstrip('/shogun2-webapp/') + layerurl + '?'
378-
return self.createWmsLayerFromShogun(layer_src_conf, request_url, epsg)
379-
# else:
380-
# return createWmsLayer(layerItem, layerurl, epsg)
370+
return self.createWmsLayerFromShogun(layer_src_conf)
371+
# else:
372+
# return createWmsLayer(layerItem, layerurl, epsg)
381373

382374
# if for any reason the parameter 'dataType' is not set correctly, we check the url
383375
# of the layer to determine if it's a WFS/WCS from the shogun-geoserver
@@ -408,103 +400,34 @@ def createLayer(self, layer_src_conf, epsg, request_url):
408400

409401
# else:
410402
# info = 'Layer source '+ layerurl + ' could not be loaded'
411-
# QMessageBox.warning(None, 'Warning', info, QMessageBox.Ok)
412-
413-
414-
def addQgsLayer(self, currentCrs, layer_src_conf, request_url, layer_group):
403+
# QMessageBox.warning(None, 'Warning', info, QMessageBox.Ok)
404+
405+
def addQgsLayer(self, layer_src_conf, layer_group):
415406
self.qgisLayers = []
416407
# layerutils
417-
layer = self.createLayer(layer_src_conf, currentCrs, request_url)
408+
layer = self.createLayer(layer_src_conf)
418409
if not layer:
419-
print("Fehler beim Laden des WMS-Layers")
410+
QgsMessageLog.logMessage(
411+
f"Could not create layer: {layer_src_conf}", 'QgisShogunEditor',
412+
level=Qgis.Critical
413+
)
420414
return
421-
422-
QgsProject.instance().addMapLayer(layer, False) # implicit addition
423-
layer_group.addLayer(layer) # eplicit addition
424-
425-
print("WMS Layer erfolgreich geladen")
426-
427-
# self.iface.addRasterLayer(layer, 'test')
428-
429-
# qgisLayerItem = QgisLayerItem(layer, self)
430-
# self.qgisLayers.append(qgisLayerItem)
431-
# qgisLayerItem.addChild(qgisLayerItem)
432-
# qgisLayerItem.setExpanded(True)
433-
434-
# for an odd reason there appears a warning below the layer if it is a
435-
# wms layer from shogun - workaround to hide this:
436-
# if layer.dataProvider().name() == 'wms' and self.source['url'].startswith('/shogun2-webapp/'):
437-
#
438-
# root = QgsProject.instance().layerTreeRoot()
439-
# layerNode = root.findLayer(layer.id())
440-
# layerNode.setExpanded(False)
441-
442-
def check_url_for_geoserver(self, input_url, layer_src_conf, applications_client_config, layer_group):
443-
# check url and prepare for geoserver request
444-
geoserver_url = layer_src_conf['url']
445-
if str(geoserver_url).__contains__('https'):
446-
request_url = geoserver_url
447-
else:
448-
if str(input_url).endswith('/'):
449-
input_url = input_url[:-1]
450-
request_url = input_url + geoserver_url
451-
request_url = str(request_url).replace('http', 'https')
452-
request_url = str(request_url).replace('8080', '443')
453-
# request_url = str(request_url).replace('ows', 'wms')
454-
self.addQgsLayer(applications_client_config['mapView']['projection'], layer_src_conf, request_url, layer_group)
455-
return
456-
457-
def request_public_entity(self, url):
458-
self.request.setUrl(QUrl(url))
459-
460-
# no certificate
461-
ssl_config = self.request.sslConfiguration()
462-
ssl_config.setPeerVerifyMode(QSslSocket.VerifyNone)
463-
self.request.setSslConfiguration(ssl_config)
464-
465-
# request public application
466-
self.reply = self.na_manager.get(self.request)
467-
event_loop = QEventLoop()
468-
self.reply.finished.connect(event_loop.quit)
469-
event_loop.exec_() # blocs until finished
470-
471-
if self.reply.error() == self.reply.NoError:
472-
self.response = self.reply.readAll().data().decode("utf-8")
473-
# print("Response:", self.response)
474-
else:
475-
self.response = None
476-
print("Error:", self.reply.errorString())
477-
self.reply.deleteLater()
478415

479-
return self.response
416+
QgsProject.instance().addMapLayer(layer, False) # implicit addition
417+
layer_group.addLayer(layer) # eplicit addition
480418

481-
def find_all_layer_ids(self, layer_tree_json):
482-
layer_ids = []
483-
484-
if "layerId" in layer_tree_json:
485-
print('Found layerId', layer_tree_json["layerId"])
486-
layer_ids.append(layer_tree_json["layerId"])
487-
488-
if "children" in layer_tree_json:
489-
for child in layer_tree_json["children"]:
490-
layer_ids.extend(self.find_all_layer_ids(child))
491-
492-
return layer_ids
493-
494-
def buildLayerTree(self, applications_layertree, applications_client_config, root, input_url, layers_content):
495-
# print('LayerTree', applications_layertree)
419+
def buildLayerTree(self, applications_layertree, layers_content, root):
496420
if 'layerId' not in applications_layertree:
497-
new_group = root.addGroup(applications_layertree['title']) # option: take the name of the application
421+
new_group = root.addGroup(applications_layertree['title']) # option: take the name of the application
498422

499423
if 'layerId' in applications_layertree:
500-
result = [layer for layer in layers_content if layer['id'] == applications_layertree['layerId']]
501-
layer_src_conf = result[0]['sourceConfig']
502-
self.check_url_for_geoserver(input_url, layer_src_conf, applications_client_config, root)
503-
424+
result = [layer for layer in layers_content if layer.get_id() == applications_layertree['layerId']]
425+
layer_src_conf = result[0].source_config
426+
self.addQgsLayer(layer_src_conf, root)
427+
504428
if 'children' in applications_layertree:
505429
for child in applications_layertree['children']:
506-
self.buildLayerTree(child, applications_client_config, new_group, input_url, layers_content)
507-
430+
self.buildLayerTree(child, layers_content, new_group)
508431

509432
def sanitize_shogun_url(self, shogun_url):
510433
if shogun_url.endswith('/graphql'):
@@ -530,34 +453,3 @@ def initialize_graphql_and_load_app_list(self):
530453
self.dlg.applicationsList.clear()
531454
for app in applications:
532455
self.dlg.applicationsList.addItem(app.get_qt_list_item())
533-
534-
def load_applications(self):
535-
inputUrl = self.dlg.entryUrl.text()
536-
# check url
537-
applications_url = self.check_url_for_applications(inputUrl)
538-
layers_url = self.check_url_for_layers(inputUrl)
539-
if (len(applications_url) > 0 and len(layers_url) > 0):
540-
# request (all public) applications
541-
applications_response = self.request_public_entity(applications_url)
542-
applications_json = json.loads(applications_response)
543-
applicatons_content = applications_json['content'][0]
544-
545-
# print('Applications', applicatons_content)
546-
# self.layer_ids = self.find_all_layer_ids(applications_layertree)
547-
548-
# request (all public) layers
549-
layers_response = self.request_public_entity(layers_url)
550-
layers_josn = json.loads(layers_response)
551-
layers_content = layers_josn['content']
552-
553-
# load all layers
554-
# for layer in layers_content:
555-
# if layer['id'] in self.layer_ids:
556-
# layer_source_config = layer['sourceConfig']
557-
# print('Load layer with id', layer['id'])
558-
# self.check_url_for_geoserver(inputUrl, layer_source_config, applications_client_config['mapView']['projection'], applications_client_config)
559-
560-
# build LayerTree and load layers
561-
root = QgsProject.instance().layerTreeRoot()
562-
self.buildLayerTree(applicatons_content['layerTree'], applicatons_content['clientConfig'], root, inputUrl, layers_content)
563-

plugin_code/service/LayerService.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ def get_all_layers(self) -> List[Layer]:
5151

5252
def get_layers_by_ids(self, layer_ids: List[int]) -> List[Layer]:
5353
query = """
54-
query GetLayers($id: Int) {
55-
allLayersByIds(id: $id) {
54+
query GetLayers($ids: [Int]) {
55+
allLayersByIds(ids: $ids) {
5656
id
5757
created
5858
modified

0 commit comments

Comments
 (0)