1# -*- coding: utf-8 -*-
2
3"""
4***************************************************************************
5    dataobject.py
6    ---------------------
7    Date                 : August 2012
8    Copyright            : (C) 2012 by Victor Olaya
9    Email                : volayaf at gmail dot com
10***************************************************************************
11*                                                                         *
12*   This program is free software; you can redistribute it and/or modify  *
13*   it under the terms of the GNU General Public License as published by  *
14*   the Free Software Foundation; either version 2 of the License, or     *
15*   (at your option) any later version.                                   *
16*                                                                         *
17***************************************************************************
18"""
19
20__author__ = 'Victor Olaya'
21__date__ = 'August 2012'
22__copyright__ = '(C) 2012, Victor Olaya'
23
24import os
25import re
26
27from qgis.core import (QgsDataProvider,
28                       QgsRasterLayer,
29                       QgsWkbTypes,
30                       QgsVectorLayer,
31                       QgsProject,
32                       QgsSettings,
33                       QgsProcessingContext,
34                       QgsFeatureRequest,
35                       QgsExpressionContext,
36                       QgsExpressionContextUtils,
37                       QgsExpressionContextScope)
38from qgis.gui import QgsSublayersDialog
39from qgis.PyQt.QtCore import QCoreApplication
40from qgis.utils import iface
41
42from processing.core.ProcessingConfig import ProcessingConfig
43
44ALL_TYPES = [-1]
45
46TYPE_VECTOR_ANY = -1
47TYPE_VECTOR_POINT = 0
48TYPE_VECTOR_LINE = 1
49TYPE_VECTOR_POLYGON = 2
50TYPE_RASTER = 3
51TYPE_FILE = 4
52TYPE_TABLE = 5
53
54
55def createContext(feedback=None):
56    """
57    Creates a default processing context
58
59    :param feedback: Optional existing QgsProcessingFeedback object, or None to use a default feedback object
60    :type feedback: Optional[QgsProcessingFeedback]
61
62    :returns: New QgsProcessingContext object
63    :rtype: QgsProcessingContext
64    """
65    context = QgsProcessingContext()
66    context.setProject(QgsProject.instance())
67    context.setFeedback(feedback)
68
69    invalid_features_method = ProcessingConfig.getSetting(ProcessingConfig.FILTER_INVALID_GEOMETRIES)
70    if invalid_features_method is None:
71        invalid_features_method = QgsFeatureRequest.GeometryAbortOnInvalid
72    context.setInvalidGeometryCheck(invalid_features_method)
73
74    settings = QgsSettings()
75    context.setDefaultEncoding(settings.value("/Processing/encoding", "System"))
76
77    context.setExpressionContext(createExpressionContext())
78
79    if iface and iface.mapCanvas() and iface.mapCanvas().mapSettings().isTemporal():
80        context.setCurrentTimeRange(iface.mapCanvas().mapSettings().temporalRange())
81
82    return context
83
84
85def createExpressionContext():
86    context = QgsExpressionContext()
87    context.appendScope(QgsExpressionContextUtils.globalScope())
88    context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))
89
90    if iface and iface.mapCanvas():
91        context.appendScope(QgsExpressionContextUtils.mapSettingsScope(iface.mapCanvas().mapSettings()))
92
93    processingScope = QgsExpressionContextScope()
94
95    if iface and iface.mapCanvas():
96        extent = iface.mapCanvas().fullExtent()
97        processingScope.setVariable('fullextent_minx', extent.xMinimum())
98        processingScope.setVariable('fullextent_miny', extent.yMinimum())
99        processingScope.setVariable('fullextent_maxx', extent.xMaximum())
100        processingScope.setVariable('fullextent_maxy', extent.yMaximum())
101
102    context.appendScope(processingScope)
103    return context
104
105
106def load(fileName, name=None, crs=None, style=None, isRaster=False):
107    """
108    Loads a layer/table into the current project, given its file.
109
110    .. deprecated:: 3.0
111    Do not use, will be removed in QGIS 4.0
112    """
113
114    from warnings import warn
115    warn("processing.load is deprecated and will be removed in QGIS 4.0", DeprecationWarning)
116
117    if fileName is None:
118        return
119
120    if name is None:
121        name = os.path.split(fileName)[1]
122
123    if isRaster:
124        options = QgsRasterLayer.LayerOptions()
125        options.skipCrsValidation = True
126        qgslayer = QgsRasterLayer(fileName, name, 'gdal', options)
127        if qgslayer.isValid():
128            if crs is not None and qgslayer.crs() is None:
129                qgslayer.setCrs(crs, False)
130            if style is None:
131                style = ProcessingConfig.getSetting(ProcessingConfig.RASTER_STYLE)
132            qgslayer.loadNamedStyle(style)
133            QgsProject.instance().addMapLayers([qgslayer])
134        else:
135            raise RuntimeError(QCoreApplication.translate('dataobject',
136                                                          'Could not load layer: {0}\nCheck the processing framework log to look for errors.').format(
137                fileName))
138    else:
139        options = QgsVectorLayer.LayerOptions()
140        options.skipCrsValidation = True
141        qgslayer = QgsVectorLayer(fileName, name, 'ogr', options)
142        if qgslayer.isValid():
143            if crs is not None and qgslayer.crs() is None:
144                qgslayer.setCrs(crs, False)
145            if style is None:
146                if qgslayer.geometryType() == QgsWkbTypes.PointGeometry:
147                    style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POINT_STYLE)
148                elif qgslayer.geometryType() == QgsWkbTypes.LineGeometry:
149                    style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_LINE_STYLE)
150                else:
151                    style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POLYGON_STYLE)
152            qgslayer.loadNamedStyle(style)
153            QgsProject.instance().addMapLayers([qgslayer])
154
155    return qgslayer
156
157
158def getRasterSublayer(path, param):
159    layer = QgsRasterLayer(path)
160
161    try:
162        # If the layer is a raster layer and has multiple sublayers, let the user chose one.
163        # Based on QgisApp::askUserForGDALSublayers
164        if layer and param.showSublayersDialog and layer.dataProvider().name() == "gdal" and len(layer.subLayers()) > 1:
165            layers = []
166            subLayerNum = 0
167            # simplify raster sublayer name
168            for subLayer in layer.subLayers():
169                # if netcdf/hdf use all text after filename
170                if bool(re.match('netcdf', subLayer, re.I)) or bool(re.match('hdf', subLayer, re.I)):
171                    subLayer = subLayer.split(path)[1]
172                    subLayer = subLayer[1:]
173                else:
174                    # remove driver name and file name
175                    subLayer.replace(subLayer.split(QgsDataProvider.SUBLAYER_SEPARATOR)[0], "")
176                    subLayer.replace(path, "")
177                # remove any : or " left over
178                if subLayer.startswith(":"):
179                    subLayer = subLayer[1:]
180                if subLayer.startswith("\""):
181                    subLayer = subLayer[1:]
182                if subLayer.endswith(":"):
183                    subLayer = subLayer[:-1]
184                if subLayer.endswith("\""):
185                    subLayer = subLayer[:-1]
186
187                ld = QgsSublayersDialog.LayerDefinition()
188                ld.layerId = subLayerNum
189                ld.layerName = subLayer
190                layers.append(ld)
191                subLayerNum = subLayerNum + 1
192
193            # Use QgsSublayersDialog
194            # Would be good if QgsSublayersDialog had an option to allow only one sublayer to be selected
195            chooseSublayersDialog = QgsSublayersDialog(QgsSublayersDialog.Gdal, "gdal")
196            chooseSublayersDialog.populateLayerTable(layers)
197
198            if chooseSublayersDialog.exec_():
199                return layer.subLayers()[chooseSublayersDialog.selectionIndexes()[0]]
200            else:
201                # If user pressed cancel then just return the input path
202                return path
203        else:
204            # If the sublayers selection dialog is not to be shown then just return the input path
205            return path
206    except:
207        # If the layer is not a raster layer, then just return the input path
208        return path
209