Source code for svir.dialogs.download_layer_dialog

# -*- coding: utf-8 -*-
# /***************************************************************************
# Irmt
#                                 A QGIS plugin
# OpenQuake Integrated Risk Modelling Toolkit
#                              -------------------
#        begin                : 2013-10-24
#        copyright            : (C) 2014 by GEM Foundation
#        email                : devops@openquake.org
# ***************************************************************************/
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake.  If not, see <http://www.gnu.org/licenses/>.

import json
import os
import tempfile
import shutil
from xml.etree import ElementTree
from qgis.PyQt.QtCore import pyqtSlot, Qt
from qgis.PyQt.QtWidgets import (
                                 QDialog,
                                 QDialogButtonBox,
                                 QListWidgetItem,
                                 QMessageBox,
                                 )
from qgis.core import QgsVectorLayer,  QgsProject
from svir.thread_worker.abstract_worker import start_worker
from svir.thread_worker.download_platform_project_worker import (
    DownloadPlatformProjectWorker)

from svir.utilities.utils import (WaitCursorManager,
                                  SvNetworkError,
                                  ask_for_destination_full_path_name,
                                  files_exist_in_destination,
                                  confirm_overwrite,
                                  write_layer_suppl_info_to_qgs,
                                  get_ui_class,
                                  log_msg,
                                  )

NS_NET_OPENGIS_WFS = '{http://www.opengis.net/wfs}'

FORM_CLASS = get_ui_class('ui_download_layer.ui')


[docs]class DownloadLayerDialog(QDialog, FORM_CLASS): """ Modal dialog giving to the user the possibility to select and download one of the projects available on the OQ-Platform """ def __init__(self, iface, downloader): QDialog.__init__(self) # Set up the user interface from Designer. self.setupUi(self) self.iface = iface # login to platform, to be able to retrieve sv indices self.sv_downloader = downloader self.ok_button = self.buttonBox.button(QDialogButtonBox.Ok) self.layer_lbl.setText('Project definition') self.layer_id = None # needed after ok is pressed self.downloaded_layer_id = None # the id of the layer created self.extra_infos = {} self.set_ok_button() with WaitCursorManager(): self.get_capabilities() @pyqtSlot()
[docs] def on_layers_lst_itemSelectionChanged(self): layer_id = self.layers_lst.currentItem().data(Qt.ToolTipRole) if layer_id is not None: self.layer_id = layer_id self.layer_lbl.setText('Project details for "%s"' % layer_id) layer_infos = self.extra_infos[layer_id] bb = layer_infos['Bounding Box'] layer_infos_str = "Title:\n\t" + layer_infos['Title'] layer_infos_str += "\nAbstract:\n\t" + layer_infos['Abstract'] layer_infos_str += "\nBounding box:" layer_infos_str += "\n\tminx = " + bb['minx'] layer_infos_str += "\n\tminy = " + bb['miny'] layer_infos_str += "\n\tmaxx = " + bb['maxx'] layer_infos_str += "\n\tmaxy = " + bb['maxy'] layer_infos_str += "\nKeywords:\n\t" + layer_infos['Keywords'] self.layer_detail.setText(layer_infos_str) self.set_ok_button()
@pyqtSlot(QListWidgetItem)
[docs] def on_layers_lst_itemDoubleClicked(self, item): self.accept()
[docs] def set_ok_button(self): self.ok_button.setDisabled( len(self.layers_lst.selectedItems()) == 0)
[docs] def get_capabilities(self): wfs = '/geoserver/wfs?' params = ('SERVICE=WFS' '&VERSION=1.0.0' '&REQUEST=GetCapabilities' '&SRSNAME=EPSG:4326') url = '%s%s%s' % (self.sv_downloader.host, wfs, params) result = self.sv_downloader.sess.get(url) if result.status_code == 200: self.parse_get_capabilities(result.content.decode('utf8')) else: raise SvNetworkError( "Unable to download layers: %s" % result.error)
[docs] def parse_get_capabilities(self, xml): # this raises a IOError if the file doesn't exist root = ElementTree.fromstring(xml) layers = root.find('%sFeatureTypeList' % NS_NET_OPENGIS_WFS) for layer in layers: try: keywords = layer.find('%sKeywords' % NS_NET_OPENGIS_WFS).text if keywords is not None and ( 'IRMT_QGIS_Plugin' in keywords or # this is for backward compatibility 'SVIR_QGIS_Plugin' in keywords): title = layer.find('%sTitle' % NS_NET_OPENGIS_WFS).text layer_id = layer.find('%sName' % NS_NET_OPENGIS_WFS).text abstract = layer.find( '%sAbstract' % NS_NET_OPENGIS_WFS).text bbox = layer.find( '%sLatLongBoundingBox' % NS_NET_OPENGIS_WFS).attrib self.extra_infos[layer_id] = { 'Title': title, 'Abstract': abstract, 'Keywords': keywords, 'Bounding Box': bbox} # update combo box item = QListWidgetItem() item.setData(Qt.DisplayRole, title) item.setData(Qt.ToolTipRole, layer_id) self.layers_lst.addItem(item) except AttributeError: continue
[docs] def accept(self): dest_full_path_name = ask_for_destination_full_path_name(self) if not dest_full_path_name: return # ignoring file extension dest_file_stem_path, _dest_file_ext = os.path.splitext( dest_full_path_name) worker = DownloadPlatformProjectWorker(self.sv_downloader, self.layer_id) worker.successfully_finished.connect( lambda zip_file: self._import_layer( zip_file, self.sv_downloader, dest_file_stem_path, self)) start_worker(worker, self.iface.messageBar(), 'Downloading data from platform')
def _replace_file_names(self, source_files, dest_file_stem): # the name from the zip_file will be replaced with dest_file_stem dest_file_names = [] for source_file in source_files: _name, ext = os.path.splitext(source_file) dest_file_name = dest_file_stem + ext dest_file_names.append(dest_file_name) return dest_file_names def _import_layer( self, zip_file, sv_downloader, dest_file_stem_path, parent_dlg): files_in_zip = zip_file.namelist() shp_file_in_zip = next( filename for filename in files_in_zip if '.shp' in filename) dest_dir = os.path.dirname(dest_file_stem_path) files_to_create = self._replace_file_names(files_in_zip, dest_file_stem_path) existing_files_in_destination = files_exist_in_destination( dest_dir, files_to_create) if existing_files_in_destination: while confirm_overwrite( parent_dlg, existing_files_in_destination) == QMessageBox.No: dest_full_path_name = ask_for_destination_full_path_name( parent_dlg) if not dest_full_path_name: continue # ignoring file extension dest_file_stem_path, _dest_file_ext = os.path.splitext( dest_full_path_name) dest_dir = os.path.dirname(dest_file_stem_path) files_to_create = self._replace_file_names(files_in_zip, dest_file_stem_path) existing_files_in_destination = files_exist_in_destination( dest_dir, files_to_create) if not existing_files_in_destination: break temp_path = os.path.join(tempfile.gettempdir(), shp_file_in_zip[:-4]) if os.path.exists(temp_path): # clearing the temporary directory should be safe for the_file in os.listdir(temp_path): file_path = os.path.join(temp_path, the_file) if os.path.isfile(file_path): os.unlink(file_path) else: os.makedirs(temp_path) # it returns None if successful zip_file.extractall(temp_path) # copy extracted files to the destination directory chosen by the user, # substituting the file names with the name chosen by the user for the_file in os.listdir(temp_path): _name, ext = os.path.splitext(the_file) new_file_path = dest_file_stem_path + ext # the full file name of the shapefile will be used to create the # vector layer if ext == '.shp': new_shp_file_path = new_file_path shutil.move(os.path.join(temp_path, the_file), new_file_path) request_url = '%s/svir/get_supplemental_information?layer_name=%s' % ( sv_downloader.host, parent_dlg.layer_id) get_supplemental_information_resp = sv_downloader.sess.get(request_url) if not get_supplemental_information_resp.ok: msg = 'Unable to retrieve the project definitions for the layer' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return supplemental_information = json.loads( get_supplemental_information_resp.content.decode('utf8')) # attempt to convert supplemental information in old list format # or those that did not nest project definitions into a # project_definitions attribute if (isinstance(supplemental_information, list) or 'project_definitions' not in supplemental_information): supplemental_information = { 'project_definitions': supplemental_information} layer = QgsVectorLayer( new_shp_file_path, parent_dlg.extra_infos[parent_dlg.layer_id]['Title'], 'ogr') if layer.isValid(): QgsProject.instance().addMapLayer(layer) msg = 'Shapefile was imported to %s' % new_shp_file_path log_msg(msg, level='S', message_bar=self.iface.messageBar()) else: msg = 'Layer invalid' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return try: # dlg.layer_id has the format "oqplatform:layername" style_name = parent_dlg.layer_id.split(':')[1] + '.sld' request_url = '%s/gs/rest/styles/%s' % ( sv_downloader.host, style_name) get_style_resp = sv_downloader.sess.get(request_url) if not get_style_resp.ok: raise SvNetworkError(get_style_resp.reason) fd, sld_file = tempfile.mkstemp(suffix=".sld") os.close(fd) with open(sld_file, 'w') as f: f.write(get_style_resp.text) layer.loadSldStyle(sld_file) except Exception as e: error_msg = ('Unable to download and apply the' ' style layer descriptor: %s' % e) log_msg(error_msg, level='C', message_bar=self.iface.messageBar()) self.iface.setActiveLayer(layer) project_definitions = supplemental_information['project_definitions'] # ensure backwards compatibility with projects with a single # project definition if not isinstance(project_definitions, list): project_definitions = [project_definitions] supplemental_information['project_definitions'] = project_definitions supplemental_information['platform_layer_id'] = parent_dlg.layer_id if 'selected_project_definition_idx' not in supplemental_information: supplemental_information['selected_project_definition_idx'] = 0 self.downloaded_layer_id = layer.id() write_layer_suppl_info_to_qgs( self.downloaded_layer_id, supplemental_information) super(DownloadLayerDialog, self).accept()