mirror of
				https://github.com/qgis/QGIS.git
				synced 2025-10-30 00:07:09 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			368 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			368 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| """
 | |
| ***************************************************************************
 | |
|     GdalUtils.py
 | |
|     ---------------------
 | |
|     Date                 : August 2012
 | |
|     Copyright            : (C) 2012 by Victor Olaya
 | |
|     Email                : volayaf at gmail dot com
 | |
| ***************************************************************************
 | |
| *                                                                         *
 | |
| *   This program is free software; you can redistribute it and/or modify  *
 | |
| *   it under the terms of the GNU General Public License as published by  *
 | |
| *   the Free Software Foundation; either version 2 of the License, or     *
 | |
| *   (at your option) any later version.                                   *
 | |
| *                                                                         *
 | |
| ***************************************************************************
 | |
| """
 | |
| from builtins import str
 | |
| from builtins import range
 | |
| from builtins import object
 | |
| 
 | |
| __author__ = 'Victor Olaya'
 | |
| __date__ = 'August 2012'
 | |
| __copyright__ = '(C) 2012, Victor Olaya'
 | |
| 
 | |
| # This will get replaced with a git SHA1 when you do a git archive
 | |
| 
 | |
| __revision__ = '$Format:%H$'
 | |
| 
 | |
| import os
 | |
| import subprocess
 | |
| import platform
 | |
| import re
 | |
| 
 | |
| import psycopg2
 | |
| 
 | |
| from osgeo import gdal
 | |
| from osgeo import ogr
 | |
| 
 | |
| from qgis.core import (QgsApplication,
 | |
|                        QgsVectorFileWriter,
 | |
|                        QgsProcessingFeedback,
 | |
|                        QgsProcessingUtils,
 | |
|                        QgsMessageLog,
 | |
|                        QgsSettings,
 | |
|                        QgsCredentials,
 | |
|                        QgsDataSourceUri)
 | |
| from processing.core.ProcessingConfig import ProcessingConfig
 | |
| from processing.tools.system import isWindows, isMac
 | |
| 
 | |
| try:
 | |
|     from osgeo import gdal  # NOQA
 | |
| 
 | |
|     gdalAvailable = True
 | |
| except:
 | |
|     gdalAvailable = False
 | |
| 
 | |
| 
 | |
| class GdalUtils(object):
 | |
|     GDAL_HELP_PATH = 'GDAL_HELP_PATH'
 | |
| 
 | |
|     supportedRasters = None
 | |
| 
 | |
|     @staticmethod
 | |
|     def runGdal(commands, feedback=None):
 | |
|         if feedback is None:
 | |
|             feedback = QgsProcessingFeedback()
 | |
|         envval = os.getenv('PATH')
 | |
|         # We need to give some extra hints to get things picked up on OS X
 | |
|         isDarwin = False
 | |
|         try:
 | |
|             isDarwin = platform.system() == 'Darwin'
 | |
|         except IOError:  # https://travis-ci.org/m-kuhn/QGIS#L1493-L1526
 | |
|             pass
 | |
|         if isDarwin and os.path.isfile(os.path.join(QgsApplication.prefixPath(), "bin", "gdalinfo")):
 | |
|             # Looks like there's a bundled gdal. Let's use it.
 | |
|             os.environ['PATH'] = "{}{}{}".format(os.path.join(QgsApplication.prefixPath(), "bin"), os.pathsep, envval)
 | |
|             os.environ['DYLD_LIBRARY_PATH'] = os.path.join(QgsApplication.prefixPath(), "lib")
 | |
|         else:
 | |
|             # Other platforms should use default gdal finder codepath
 | |
|             settings = QgsSettings()
 | |
|             path = settings.value('/GdalTools/gdalPath', '')
 | |
|             if not path.lower() in envval.lower().split(os.pathsep):
 | |
|                 envval += '{}{}'.format(os.pathsep, path)
 | |
|                 os.putenv('PATH', envval)
 | |
| 
 | |
|         fused_command = ' '.join([str(c) for c in commands])
 | |
|         QgsMessageLog.logMessage(fused_command, 'Processing', QgsMessageLog.INFO)
 | |
|         feedback.pushInfo('GDAL command:')
 | |
|         feedback.pushCommandInfo(fused_command)
 | |
|         feedback.pushInfo('GDAL command output:')
 | |
|         success = False
 | |
|         retry_count = 0
 | |
|         while not success:
 | |
|             loglines = []
 | |
|             loglines.append('GDAL execution console output')
 | |
|             try:
 | |
|                 with subprocess.Popen(
 | |
|                     fused_command,
 | |
|                     shell=True,
 | |
|                     stdout=subprocess.PIPE,
 | |
|                     stdin=subprocess.DEVNULL,
 | |
|                     stderr=subprocess.STDOUT,
 | |
|                     universal_newlines=True,
 | |
|                 ) as proc:
 | |
|                     for line in proc.stdout:
 | |
|                         feedback.pushConsoleInfo(line)
 | |
|                         loglines.append(line)
 | |
|                     success = True
 | |
|             except IOError as e:
 | |
|                 if retry_count < 5:
 | |
|                     retry_count += 1
 | |
|                 else:
 | |
|                     raise IOError(
 | |
|                         e.message + u'\nTried 5 times without success. Last iteration stopped after reading {} line(s).\nLast line(s):\n{}'.format(
 | |
|                             len(loglines), u'\n'.join(loglines[-10:])))
 | |
| 
 | |
|             QgsMessageLog.logMessage('\n'.join(loglines), 'Processing', QgsMessageLog.INFO)
 | |
|             GdalUtils.consoleOutput = loglines
 | |
| 
 | |
|     @staticmethod
 | |
|     def getConsoleOutput():
 | |
|         return GdalUtils.consoleOutput
 | |
| 
 | |
|     @staticmethod
 | |
|     def getSupportedRasters():
 | |
|         if not gdalAvailable:
 | |
|             return {}
 | |
| 
 | |
|         if GdalUtils.supportedRasters is not None:
 | |
|             return GdalUtils.supportedRasters
 | |
| 
 | |
|         if gdal.GetDriverCount() == 0:
 | |
|             gdal.AllRegister()
 | |
| 
 | |
|         GdalUtils.supportedRasters = {}
 | |
|         GdalUtils.supportedRasters['GTiff'] = ['tif']
 | |
|         for i in range(gdal.GetDriverCount()):
 | |
|             driver = gdal.GetDriver(i)
 | |
|             if driver is None:
 | |
|                 continue
 | |
|             shortName = driver.ShortName
 | |
|             metadata = driver.GetMetadata()
 | |
|             if gdal.DCAP_RASTER not in metadata \
 | |
|                     or metadata[gdal.DCAP_RASTER] != 'YES':
 | |
|                 continue
 | |
| 
 | |
|             # ===================================================================
 | |
|             # if gdal.DCAP_CREATE not in metadata \
 | |
|             #         or metadata[gdal.DCAP_CREATE] != 'YES':
 | |
|             #     continue
 | |
|             # ===================================================================
 | |
|             if gdal.DMD_EXTENSION in metadata:
 | |
|                 extensions = metadata[gdal.DMD_EXTENSION].split('/')
 | |
|                 if extensions:
 | |
|                     GdalUtils.supportedRasters[shortName] = extensions
 | |
| 
 | |
|         return GdalUtils.supportedRasters
 | |
| 
 | |
|     @staticmethod
 | |
|     def getSupportedRasterExtensions():
 | |
|         allexts = ['tif']
 | |
|         for exts in list(GdalUtils.getSupportedRasters().values()):
 | |
|             for ext in exts:
 | |
|                 if ext not in allexts and ext != '':
 | |
|                     allexts.append(ext)
 | |
|         return allexts
 | |
| 
 | |
|     @staticmethod
 | |
|     def getVectorDriverFromFileName(filename):
 | |
|         ext = os.path.splitext(filename)[1]
 | |
|         if ext == '':
 | |
|             return 'ESRI Shapefile'
 | |
| 
 | |
|         formats = QgsVectorFileWriter.supportedFiltersAndFormats()
 | |
|         for k, v in list(formats.items()):
 | |
|             if ext in k:
 | |
|                 return v
 | |
|         return 'ESRI Shapefile'
 | |
| 
 | |
|     @staticmethod
 | |
|     def getFormatShortNameFromFilename(filename):
 | |
|         ext = filename[filename.rfind('.') + 1:]
 | |
|         supported = GdalUtils.getSupportedRasters()
 | |
|         for name in list(supported.keys()):
 | |
|             exts = supported[name]
 | |
|             if ext in exts:
 | |
|                 return name
 | |
|         return 'GTiff'
 | |
| 
 | |
|     @staticmethod
 | |
|     def escapeAndJoin(strList):
 | |
|         joined = ''
 | |
|         for s in strList:
 | |
|             if not isinstance(s, str):
 | |
|                 s = str(s)
 | |
|             if s and s[0] != '-' and ' ' in s:
 | |
|                 escaped = '"' + s.replace('\\', '\\\\').replace('"', '\\"') \
 | |
|                           + '"'
 | |
|             else:
 | |
|                 escaped = s
 | |
|             if escaped is not None:
 | |
|                 joined += escaped + ' '
 | |
|         return joined.strip()
 | |
| 
 | |
|     @staticmethod
 | |
|     def version():
 | |
|         return int(gdal.VersionInfo('VERSION_NUM'))
 | |
| 
 | |
|     @staticmethod
 | |
|     def readableVersion():
 | |
|         return gdal.VersionInfo('RELEASE_NAME')
 | |
| 
 | |
|     @staticmethod
 | |
|     def gdalHelpPath():
 | |
|         helpPath = ProcessingConfig.getSetting(GdalUtils.GDAL_HELP_PATH)
 | |
| 
 | |
|         if helpPath is None:
 | |
|             if isWindows():
 | |
|                 pass
 | |
|             elif isMac():
 | |
|                 pass
 | |
|             else:
 | |
|                 searchPaths = ['/usr/share/doc/libgdal-doc/gdal']
 | |
|                 for path in searchPaths:
 | |
|                     if os.path.exists(path):
 | |
|                         helpPath = os.path.abspath(path)
 | |
|                         break
 | |
| 
 | |
|         return helpPath if helpPath is not None else 'http://www.gdal.org/'
 | |
| 
 | |
|     @staticmethod
 | |
|     def ogrConnectionString(uri, context):
 | |
|         """Generates OGR connection string from layer source
 | |
|         """
 | |
|         return GdalUtils.ogrConnectionStringAndFormat(uri, context)[0]
 | |
| 
 | |
|     @staticmethod
 | |
|     def ogrConnectionStringAndFormat(uri, context):
 | |
|         """Generates OGR connection string and format string from layer source
 | |
|         Returned values are a tuple of the connection string and format string
 | |
|         """
 | |
|         ogrstr = None
 | |
|         format = None
 | |
| 
 | |
|         layer = QgsProcessingUtils.mapLayerFromString(uri, context, False)
 | |
|         if layer is None:
 | |
|             path, ext = os.path.splitext(uri)
 | |
|             format = QgsVectorFileWriter.driverForExtension(ext)
 | |
|             return '"' + uri + '"', '"' + format + '"'
 | |
| 
 | |
|         provider = layer.dataProvider().name()
 | |
|         if provider == 'spatialite':
 | |
|             # dbname='/geodata/osm_ch.sqlite' table="places" (Geometry) sql=
 | |
|             regex = re.compile("dbname='(.+)'")
 | |
|             r = regex.search(str(layer.source()))
 | |
|             ogrstr = r.groups()[0]
 | |
|             format = 'SQLite'
 | |
|         elif provider == 'postgres':
 | |
|             # dbname='ktryjh_iuuqef' host=spacialdb.com port=9999
 | |
|             # user='ktryjh_iuuqef' password='xyqwer' sslmode=disable
 | |
|             # key='gid' estimatedmetadata=true srid=4326 type=MULTIPOLYGON
 | |
|             # table="t4" (geom) sql=
 | |
|             dsUri = QgsDataSourceUri(layer.dataProvider().dataSourceUri())
 | |
|             conninfo = dsUri.connectionInfo()
 | |
|             conn = None
 | |
|             ok = False
 | |
|             while not conn:
 | |
|                 try:
 | |
|                     conn = psycopg2.connect(dsUri.connectionInfo())
 | |
|                 except psycopg2.OperationalError:
 | |
|                     (ok, user, passwd) = QgsCredentials.instance().get(conninfo, dsUri.username(), dsUri.password())
 | |
|                     if not ok:
 | |
|                         break
 | |
| 
 | |
|                     dsUri.setUsername(user)
 | |
|                     dsUri.setPassword(passwd)
 | |
| 
 | |
|             if not conn:
 | |
|                 raise RuntimeError('Could not connect to PostgreSQL database - check connection info')
 | |
| 
 | |
|             if ok:
 | |
|                 QgsCredentials.instance().put(conninfo, user, passwd)
 | |
| 
 | |
|             ogrstr = "PG:%s" % dsUri.connectionInfo()
 | |
|             format = 'PostgreSQL'
 | |
|         elif provider == "oracle":
 | |
|             # OCI:user/password@host:port/service:table
 | |
|             dsUri = QgsDataSourceUri(layer.dataProvider().dataSourceUri())
 | |
|             ogrstr = "OCI:"
 | |
|             if dsUri.username() != "":
 | |
|                 ogrstr += dsUri.username()
 | |
|                 if dsUri.password() != "":
 | |
|                     ogrstr += "/" + dsUri.password()
 | |
|                 delim = "@"
 | |
| 
 | |
|             if dsUri.host() != "":
 | |
|                 ogrstr += delim + dsUri.host()
 | |
|                 delim = ""
 | |
|                 if dsUri.port() != "" and dsUri.port() != '1521':
 | |
|                     ogrstr += ":" + dsUri.port()
 | |
|                 ogrstr += "/"
 | |
|                 if dsUri.database() != "":
 | |
|                     ogrstr += dsUri.database()
 | |
|             elif dsUri.database() != "":
 | |
|                 ogrstr += delim + dsUri.database()
 | |
| 
 | |
|             if ogrstr == "OCI:":
 | |
|                 raise RuntimeError('Invalid oracle data source - check connection info')
 | |
| 
 | |
|             ogrstr += ":"
 | |
|             if dsUri.schema() != "":
 | |
|                 ogrstr += dsUri.schema() + "."
 | |
| 
 | |
|             ogrstr += dsUri.table()
 | |
|             format = 'OCI'
 | |
|         else:
 | |
|             ogrstr = str(layer.source()).split("|")[0]
 | |
|             path, ext = os.path.splitext(ogrstr)
 | |
|             format = QgsVectorFileWriter.driverForExtension(ext)
 | |
| 
 | |
|         return '"' + ogrstr + '"', '"' + format + '"'
 | |
| 
 | |
|     @staticmethod
 | |
|     def ogrLayerName(uri):
 | |
|         uri = uri.strip('"')
 | |
|         if os.path.isfile(uri):
 | |
|             return os.path.basename(os.path.splitext(uri)[0])
 | |
| 
 | |
|         if ' table=' in uri:
 | |
|             # table="schema"."table"
 | |
|             re_table_schema = re.compile(' table="([^"]*)"\\."([^"]*)"')
 | |
|             r = re_table_schema.search(uri)
 | |
|             if r:
 | |
|                 return r.groups()[0] + '.' + r.groups()[1]
 | |
|             # table="table"
 | |
|             re_table = re.compile(' table="([^"]*)"')
 | |
|             r = re_table.search(uri)
 | |
|             if r:
 | |
|                 return r.groups()[0]
 | |
|         elif 'layername' in uri:
 | |
|             regex = re.compile('(layername=)([^|]*)')
 | |
|             r = regex.search(uri)
 | |
|             return r.groups()[1]
 | |
| 
 | |
|         fields = uri.split('|')
 | |
|         basePath = fields[0]
 | |
|         fields = fields[1:]
 | |
|         layerid = 0
 | |
|         for f in fields:
 | |
|             if f.startswith('layername='):
 | |
|                 return f.split('=')[1]
 | |
|             if f.startswith('layerid='):
 | |
|                 layerid = int(f.split('=')[1])
 | |
| 
 | |
|         ds = ogr.Open(basePath)
 | |
|         if not ds:
 | |
|             return None
 | |
| 
 | |
|         ly = ds.GetLayer(layerid)
 | |
|         if not ly:
 | |
|             return None
 | |
| 
 | |
|         name = ly.GetName()
 | |
|         ds = None
 | |
|         return name
 |