Skip to content

Commit

Permalink
Merge pull request #26 from Riverscapes/dev
Browse files Browse the repository at this point in the history
0.5.0 Release
  • Loading branch information
MattReimer authored Jun 1, 2021
2 parents fd6b28a + 4703ebc commit 7d719f8
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 131 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
## 0.5.0 ***(June 1, 2021)***

### New Features

* [Update to support more basemap Types (XYZ and TMS)](https://github.com/Riverscapes/QRAVEPlugin/issues/21)
* [Adding the ability to opt-out of automatic updates](https://github.com/Riverscapes/QRAVEPlugin/issues/23)

### Bug Fixes

* [Failing gracefully when the project XML doesn't conform to the latest version](https://github.com/Riverscapes/QRAVEPlugin/issues/28)
* [Empty tree branches don't cause crashes anymore](https://github.com/Riverscapes/QRAVEPlugin/issues/15)
* [Raster transparencies now work correctly](https://github.com/Riverscapes/QRAVEPlugin/issues/22)
* [...lots of little annoying bugs](https://github.com/Riverscapes/QRAVEPlugin/issues/18)


## 0.4.0 ***(May 13, 2021)***

* [Fixed a blocking bug that prevented working on QGIS Versions less than 3.18](https://github.com/Riverscapes/QRAVEPlugin/issues/16)
Expand Down
2 changes: 1 addition & 1 deletion __version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.4.0"
__version__ = "0.5.0"
2 changes: 2 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
"initialized": false,
"loadDefaultView": true,
"basemapsInclude": true,
"autoUpdate": true,
"lastDigestSync": null,
"lastResourceSync": null,
"lastBrowsePath": null,
"pluginVersion": null,
"basemapRegion": "United States"
},
"constants": {
Expand Down
106 changes: 67 additions & 39 deletions src/classes/basemaps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations
import os
import requests
import urllib.parse
from typing import Dict

import lxml.etree
Expand All @@ -22,47 +23,28 @@

class QRaveBaseMap():

def __init__(self, parent: QStandardItem, layer_url: str, meta: Dict[str, str]):
def __init__(self, parent: QStandardItem, layer_url: str, tile_type: str, meta: Dict[str, str]):
self.parent = parent
self.meta = meta
self.loaded = False
self.tile_type = tile_type
self.settings = Settings()
self.layer_url = layer_url.replace('?', '')

self.tm = QgsApplication.taskManager()
self.reset()

def reset(self):
self.parent.removeRows(0, self.parent.rowCount())
loading_layer = QStandardItem('loading...')
f = loading_layer.font()
f.setItalic(True)
loading_layer.setFont(f)
loading_layer.setEnabled(False)
self.parent.appendRow(loading_layer)

def _load_layers_done(self, exception, result=None):
"""This is called when doSomething is finished.
Exception is not None if doSomething raises an exception.
result is the return value of doSomething."""
if exception is None:
if result is None:
self.settings.log('Completed with no exception and no result ', Qgis.Warning)
else:
try:
self.parent.removeRows(0, self.parent.rowCount())
for lyr in result.findall('Capability/Layer'):
self.parse_layer(lyr, self.parent)
if self.tile_type == 'wms':
self.parent.removeRows(0, self.parent.rowCount())
loading_layer = QStandardItem('loading...')
f = loading_layer.font()
f.setItalic(True)
loading_layer.setFont(f)
loading_layer.setEnabled(False)
self.parent.appendRow(loading_layer)

except Exception as e:
self.settings.log(str(e), Qgis.Critical)

else:
self.settings.log("Exception: {}".format(exception),
Qgis.Critical)
raise exception

def parse_layer(self, root_el, parent: QStandardItem):
def _parse_wms_layer(self, root_el, parent: QStandardItem):

try:
title = root_el.find('Title').text
Expand All @@ -76,6 +58,7 @@ def parse_layer(self, root_el, parent: QStandardItem):
abstract = abstract_fnd.text if abstract_fnd is not None else "No abstract provided"

urlWithParams = "crs={}&format={}&layers={}&styles&url={}".format(srs, lyr_format, name, self.layer_url)

lyr_item = QStandardItem(QIcon(':/plugins/qrave_toolbar/layers/Raster.png'), title)

extra_meta = {
Expand All @@ -87,7 +70,7 @@ def parse_layer(self, root_el, parent: QStandardItem):
ProjectTreeData(
QRaveTreeTypes.LEAF,
None,
QRaveMapLayer(title, QRaveMapLayer.LayerTypes.WMS, urlWithParams, extra_meta)
QRaveMapLayer(title, QRaveMapLayer.LayerTypes.WEBTILE, tile_type=self.tile_type, layer_uri=urlWithParams, meta=extra_meta)
),
Qt.UserRole)
lyr_item.setToolTip(wrap_by_word(abstract, 20))
Expand All @@ -100,24 +83,46 @@ def parse_layer(self, root_el, parent: QStandardItem):
Qgis.Warning
)
lyr_item = QStandardItem(QIcon(':/plugins/qrave_toolbar/BrowseFolder.png'), root_el.find('Title').text)
lyr_item.setData(ProjectTreeData(QRaveTreeTypes.BASEMAP_SUB_FOLDER), Qt.UserRole),
lyr_item.setData(ProjectTreeData(QRaveTreeTypes.BASEMAP_SUB_FOLDER), Qt.UserRole)

parent.appendRow(lyr_item)

for sublyr in root_el.findall('Layer'):
self.parse_layer(sublyr, lyr_item)
self._parse_wms_layer(sublyr, lyr_item)

def _wms_fetch_done(self, exception, result=None):
"""This is called when doSomething is finished.
Exception is not None if doSomething raises an exception.
result is the return value of doSomething."""
if exception is None:
if result is None:
self.settings.log('Completed with no exception and no result ', Qgis.Warning)
else:
try:
self.parent.removeRows(0, self.parent.rowCount())
for lyr in result.findall('Capability/Layer'):
self._parse_wms_layer(lyr, self.parent)

except Exception as e:
self.settings.log(str(e), Qgis.Critical)

else:
self.settings.log("Exception: {}".format(exception),
Qgis.Critical)
raise exception

def load_layers(self, force=False):
if self.loaded is True and force is False:
return

def _layer_fetch(task):
def _wms_fetch(task):
result = requestFetch(self.layer_url + REQUEST_ARGS)
return lxml.etree.fromstring(result)

self.settings.log('Fetching WMS Capabilities: {}'.format(self.layer_url), Qgis.Info)
ns_task = QgsTask.fromFunction('Loading WMS Data', _layer_fetch, on_finished=self._load_layers_done)
self.tm.addTask(ns_task)
if self.tile_type == 'wms':
self.settings.log('Fetching WMS Capabilities: {}'.format(self.layer_url), Qgis.Info)
ns_task = QgsTask.fromFunction('Loading WMS Data', _wms_fetch, on_finished=self._wms_fetch_done)
self.tm.addTask(ns_task)


class BaseMaps(Borg):
Expand Down Expand Up @@ -151,13 +156,36 @@ def load(self):

for layer in group_layer.findall('Layer'):
layer_label = layer.attrib['name']
# TODO: Need to go a little backward compatible. We can remove this logic after July 1, 2021
tile_type = layer.attrib['type'] if 'type' in layer.attrib else 'wms'
layer_url = layer.attrib['url']

q_layer = QStandardItem(QIcon(':/plugins/qrave_toolbar/RaveAddIn_16px.png'), layer_label)

meta = {meta.attrib['name']: meta.text for meta in layer.findall('Metadata/Meta')}
# We set the data to be Basemaps to help us load this stuff later
q_layer.setData(ProjectTreeData(QRaveTreeTypes.LEAF, None, QRaveBaseMap(q_layer, layer_url, meta)), Qt.UserRole)

basemap_obj = QRaveBaseMap(q_layer, layer_url, tile_type, meta)

if tile_type == 'wms':
pt_data = basemap_obj
else:
encoded_url = urllib.parse.quote_plus(layer_url)
url_with_params = 'type=xyz&url={}'.format(encoded_url)
pt_data = QRaveMapLayer(
layer_label,
QRaveMapLayer.LayerTypes.WEBTILE,
tile_type=tile_type,
layer_uri=url_with_params,
meta=meta
)

# WMS is complicated because it needs a lookup
q_layer.setData(
ProjectTreeData(QRaveTreeTypes.LEAF, None, pt_data),
Qt.UserRole
)

# We set the data to be Basemaps to help us load this stuff later
q_group_layer.appendRow(q_layer)
except Exception as e:
settings = Settings()
Expand Down
4 changes: 2 additions & 2 deletions src/classes/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def _build_views(self):

for view in self.business_logic.findall('Views/View'):
name = view.attrib['name']
view_id = view.attrib['id']
view_id = view.attrib['id'] if 'id' in view.attrib else None

if name is None or view_id is None:
continue
Expand Down Expand Up @@ -182,7 +182,7 @@ def _recurse_tree(self, bl_el=None, proj_el=None, parent: QStandardItem = None):
# Repeaters are a separate case
elif child_node.tag == 'Repeater':
qrepeater = QStandardItem(QIcon(':/plugins/qrave_toolbar/BrowseFolder.png'), child_node.attrib['label'])
qrepeater.setData(ProjectTreeData(QRaveTreeTypes.PROJECT_REPEATER_FOLDER, project=self, data=children_container.attrib), Qt.UserRole),
qrepeater.setData(ProjectTreeData(QRaveTreeTypes.PROJECT_REPEATER_FOLDER, project=self, data=dict(children_container.attrib)), Qt.UserRole),
curr_item.appendRow(qrepeater)
repeat_xpath = child_node.attrib['xpath']
repeat_node = child_node.find('Node')
Expand Down
18 changes: 10 additions & 8 deletions src/classes/qrave_map_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,33 +44,36 @@ class LayerTypes():
POINT = 'point'
RASTER = 'raster'
FILE = 'file'
WMS = 'WMS'
# Tile Types
WEBTILE = 'WEBTILE'

def __init__(self,
label: str,
layer_type: str,
layer_uri: str,
bl_attr: Dict[str, str] = None,
meta: Dict[str, str] = None,
layer_name: str = None
layer_name: str = None,
tile_type: str = None
):
self.label = label
self.layer_uri = layer_uri

if isinstance(layer_uri, str) and len(layer_uri) > 0 and layer_type != QRaveMapLayer.LayerTypes.WMS:
if isinstance(layer_uri, str) and len(layer_uri) > 0 and layer_type != QRaveMapLayer.LayerTypes.WEBTILE:
self.layer_uri = os.path.abspath(layer_uri)

self.bl_attr = bl_attr
self.meta = meta
self.transparency = 0
self.layer_name = layer_name
self.tile_type = tile_type

if layer_type not in QRaveMapLayer.LayerTypes.__dict__.values():
settings = Settings()
settings.log('Layer type "{}" is not valid'.format(layer_type), Qgis.Critical)
self.layer_type = layer_type

self.exists = self.layer_type == QRaveMapLayer.LayerTypes.WMS or os.path.isfile(layer_uri)
self.exists = self.layer_type == QRaveMapLayer.LayerTypes.WEBTILE or os.path.isfile(layer_uri)

@staticmethod
def _addgrouptomap(sGroupName, sGroupOrder, parentGroup):
Expand Down Expand Up @@ -146,19 +149,18 @@ def add_layer_to_map(item: QStandardItem):
layer_uri = map_layer.layer_uri
rOutput = None
# This might be a basemap
if map_layer.layer_type == QRaveMapLayer.LayerTypes.WMS:
if map_layer.layer_type == QRaveMapLayer.LayerTypes.WEBTILE:
rOutput = QgsRasterLayer(layer_uri, map_layer.label, 'wms')

elif map_layer.layer_type in [QRaveMapLayer.LayerTypes.LINE, QRaveMapLayer.LayerTypes.POLYGON, QRaveMapLayer.LayerTypes.POINT]:
if map_layer.layer_name is not None:
layer_uri += "|layername={}".format(map_layer.layer_name)
rOutput = QgsVectorLayer(layer_uri, map_layer.label, "ogr")
rOutput = QgsVectorLayer(layer_uri, map_layer.label, "ogr")

elif map_layer.layer_type == QRaveMapLayer.LayerTypes.RASTER:
# Raster
rOutput = QgsRasterLayer(layer_uri, map_layer.label)


if rOutput is not None:
##########################################
# Symbology
Expand Down Expand Up @@ -219,7 +221,7 @@ def add_layer_to_map(item: QStandardItem):
# rOutput.triggerRepaint()
except Exception as e:
settings.log('Error deriving transparency from layer: {}'.format(e))

QgsProject.instance().addMapLayer(rOutput, False)
parentGroup.insertLayer(item.row(), rOutput)

Expand Down
18 changes: 13 additions & 5 deletions src/dock_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,13 @@ def default_tree_action(self, idx: QModelIndex):
else:
QRaveMapLayer.add_layer_to_map(item)

# Expand is the default option for wms because we might need to load the layers
elif isinstance(item_data.data, QRaveBaseMap):
# Expand is the default option because we might need to load the layers
pass
if item_data.data.tile_type == 'wms':
pass
# All the XYZ layers can be added normally.
else:
QRaveMapLayer.add_layer_to_map(item)

elif item_data.type in [QRaveTreeTypes.PROJECT_ROOT]:
self.change_meta(item, item_data, True)
Expand Down Expand Up @@ -476,16 +480,20 @@ def open_menu(self, position):

# This is the layer context menu
if isinstance(data, QRaveMapLayer):
if data.layer_type == QRaveMapLayer.LayerTypes.WMS:
if data.layer_type == QRaveMapLayer.LayerTypes.WEBTILE:
self.basemap_context_menu(idx, item, project_tree_data)
elif data.layer_type == QRaveMapLayer.LayerTypes.FILE:
self.file_layer_context_menu(idx, item, project_tree_data)
else:
self.map_layer_context_menu(idx, item, project_tree_data)

# A QARaveBaseMap is just a container for layers
elif isinstance(data, QRaveBaseMap):
self.folder_dumb_context_menu(idx, item, project_tree_data)
# A WMS QARaveBaseMap is just a container for layers
if data.tile_type == 'wms':
self.folder_dumb_context_menu(idx, item, project_tree_data)
# Every other kind of basemap is an add-able layer
else:
self.basemap_context_menu(idx, item, project_tree_data)

elif project_tree_data.type == QRaveTreeTypes.PROJECT_ROOT:
self.project_context_menu(idx, item, project_tree_data)
Expand Down
2 changes: 2 additions & 0 deletions src/options_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def __init__(self, parent=None):
def setValues(self):
self.basemapsInclude.setChecked(self.settings.getValue('basemapsInclude'))
self.loadDefaultView.setChecked(self.settings.getValue('loadDefaultView'))
self.autoUpdate.setChecked(self.settings.getValue('autoUpdate'))

# Set the combo box
self.basemapRegion.clear()
Expand All @@ -52,6 +53,7 @@ def commit_settings(self, btn):
self.settings.setValue('basemapsInclude', self.basemapsInclude.isChecked())
self.settings.setValue('loadDefaultView', self.loadDefaultView.isChecked())
self.settings.setValue('basemapRegion', self.basemapRegion.currentText())
self.settings.setValue('autoUpdate', self.autoUpdate.isChecked())

elif role == QDialogButtonBox.ResetRole:
self.settings.resetAllSettings()
Expand Down
Loading

0 comments on commit 7d719f8

Please sign in to comment.