2013-02-23 21:43:17 +01:00
# -*- coding: utf-8 -*-
"""
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
vector . py
- - - - - - - - - - - - - - - - - - - - -
Date : February 2013
Copyright : ( C ) 2013 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 . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
"""
2016-08-04 09:10:08 +02:00
from __future__ import print_function
2016-09-21 18:24:26 +02:00
from future import standard_library
standard_library . install_aliases ( )
from builtins import map
from builtins import str
from builtins import range
from builtins import object
2013-10-01 20:52:22 +03:00
2016-12-15 09:43:31 +01:00
2013-02-23 21:43:17 +01:00
__author__ = ' Victor Olaya '
__date__ = ' February 2013 '
__copyright__ = ' (C) 2013, Victor Olaya '
2013-10-01 20:52:22 +03:00
2013-02-23 21:43:17 +01:00
# This will get replaced with a git SHA1 when you do a git archive
2013-10-01 20:52:22 +03:00
2013-02-23 21:43:17 +01:00
__revision__ = ' $Format: % H$ '
2016-01-22 15:45:09 +02:00
import re
import os
2014-08-06 18:48:51 +03:00
import csv
2013-11-28 12:57:45 +01:00
import uuid
2014-08-06 18:48:51 +03:00
import codecs
2016-09-21 18:24:26 +02:00
import io
2013-11-28 12:57:45 +01:00
2016-01-22 15:45:09 +02:00
import psycopg2
2016-11-11 17:25:45 +02:00
from osgeo import ogr
2016-01-22 15:45:09 +02:00
2016-12-15 09:43:31 +01:00
from qgis . PyQt . QtCore import QVariant , QSettings , QCoreApplication
2016-08-04 09:10:08 +02:00
from qgis . core import ( Qgis , QgsFields , QgsField , QgsGeometry , QgsRectangle , QgsWkbTypes ,
2016-12-10 15:18:12 +08:00
QgsSpatialIndex , QgsProject , QgsMapLayer , QgsVectorLayer ,
2016-08-04 09:10:08 +02:00
QgsVectorFileWriter , QgsDistanceArea , QgsDataSourceUri , QgsCredentials ,
QgsFeatureRequest , QgsWkbTypes )
2016-01-22 15:45:09 +02:00
2013-10-01 20:52:22 +03:00
from processing . core . ProcessingConfig import ProcessingConfig
2016-12-15 09:43:31 +01:00
from processing . core . ProcessingLog import ProcessingLog
2015-11-05 21:16:55 +01:00
from processing . core . GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
2016-06-01 14:15:46 +03:00
from processing . tools import dataobjects , spatialite , postgis
2013-10-01 20:52:22 +03:00
2013-02-16 00:23:56 +01:00
2014-12-18 09:12:35 +02:00
TYPE_MAP = {
2015-08-22 14:29:41 +02:00
str : QVariant . String ,
2014-12-18 09:12:35 +02:00
float : QVariant . Double ,
int : QVariant . Int ,
bool : QVariant . Bool
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
}
2014-12-18 09:12:35 +02:00
2015-06-23 14:14:09 +02:00
TYPE_MAP_MEMORY_LAYER = {
QVariant . String : " string " ,
QVariant . Double : " double " ,
2016-01-29 08:43:15 +07:00
QVariant . Int : " integer " ,
QVariant . Date : " date " ,
QVariant . DateTime : " datetime " ,
QVariant . Time : " time "
2015-06-23 14:14:09 +02:00
}
2014-12-18 09:12:35 +02:00
2015-11-05 21:16:55 +01:00
TYPE_MAP_POSTGIS_LAYER = {
QVariant . String : " VARCHAR " ,
QVariant . Double : " REAL " ,
QVariant . Int : " INTEGER " ,
QVariant . Bool : " BOOLEAN "
}
2015-11-06 17:50:38 +01:00
TYPE_MAP_SPATIALITE_LAYER = {
QVariant . String : " VARCHAR " ,
QVariant . Double : " REAL " ,
QVariant . Int : " INTEGER " ,
QVariant . Bool : " INTEGER "
}
2015-08-22 14:29:41 +02:00
2016-07-13 12:06:41 +10:00
def features ( layer , request = QgsFeatureRequest ( ) ) :
2013-10-01 20:52:22 +03:00
""" This returns an iterator over features in a vector layer,
considering the selection that might exist in the layer , and the
configuration that indicates whether to use only selected feature
or all of them .
2016-07-21 22:01:38 +10:00
This should be used by algorithms instead of calling the Qgis API
2013-10-01 20:52:22 +03:00
directly , to ensure a consistent behaviour across algorithms .
"""
2016-09-21 18:24:26 +02:00
class Features ( object ) :
2013-10-01 20:52:22 +03:00
2016-12-15 09:43:31 +01:00
DO_NOT_CHECK , IGNORE , RAISE_EXCEPTION = range ( 3 )
2016-07-13 12:06:41 +10:00
def __init__ ( self , layer , request ) :
2013-09-12 13:19:00 +02:00
self . layer = layer
2013-10-01 20:52:22 +03:00
self . selection = False
2016-07-13 12:06:41 +10:00
if ProcessingConfig . getSetting ( ProcessingConfig . USE_SELECTED ) \
and layer . selectedFeatureCount ( ) > 0 :
self . iter = layer . selectedFeaturesIterator ( request )
self . selection = True
else :
self . iter = layer . getFeatures ( request )
2016-12-15 09:43:31 +01:00
invalidFeaturesMethod = ProcessingConfig . getSetting ( ProcessingConfig . FILTER_INVALID_GEOMETRIES )
def filterFeature ( f , ignoreInvalid ) :
geom = f . geometry ( )
if geom is None :
ProcessingLog . addToLog ( ProcessingLog . LOG_INFO ,
self . tr ( ' Feature with NULL geometry found. ' ) )
elif not geom . isGeosValid ( ) :
ProcessingLog . addToLog ( ProcessingLog . LOG_ERROR ,
self . tr ( ' GEOS geoprocessing error: One or more input features have invalid geometry. ' ) )
if ignoreInvalid :
return False
else :
2016-12-15 12:25:20 +01:00
raise GeoAlgorithmExecutionException ( self . tr ( ' Features with invalid geometries found. Please fix these geometries or specify the " Ignore invalid input features " flag ' ) )
2016-12-15 09:43:31 +01:00
return True
if invalidFeaturesMethod == self . IGNORE :
2017-01-09 18:24:49 +01:00
self . iter = filter ( lambda x : filterFeature ( x , True ) , self . iter )
2016-12-15 09:43:31 +01:00
elif invalidFeaturesMethod == self . RAISE_EXCEPTION :
2017-01-09 18:24:49 +01:00
self . iter = filter ( lambda x : filterFeature ( x , False ) , self . iter )
2013-09-17 12:33:57 +02:00
2013-09-12 13:19:00 +02:00
def __iter__ ( self ) :
2013-11-06 00:15:19 +01:00
return self . iter
2013-09-17 12:33:57 +02:00
2013-09-12 13:19:00 +02:00
def __len__ ( self ) :
if self . selection :
return int ( self . layer . selectedFeatureCount ( ) )
else :
return int ( self . layer . featureCount ( ) )
2016-12-15 09:43:31 +01:00
def tr ( self , string ) :
return QCoreApplication . translate ( " FeatureIterator " , string )
2013-09-17 12:33:57 +02:00
2016-07-13 12:06:41 +10:00
return Features ( layer , request )
2013-09-12 13:19:00 +02:00
2013-10-01 20:52:22 +03:00
2013-09-12 13:19:00 +02:00
def uniqueValues ( layer , attribute ) :
2013-10-01 20:52:22 +03:00
""" Returns a list of unique values for a given attribute.
Attribute can be defined using a field names or a zero - based
field index . It considers the existing selection .
"""
2016-10-12 17:05:15 +10:00
2013-04-10 15:03:17 +02:00
fieldIndex = resolveFieldIndex ( layer , attribute )
2016-10-12 17:05:15 +10:00
if ProcessingConfig . getSetting ( ProcessingConfig . USE_SELECTED ) \
and layer . selectedFeatureCount ( ) > 0 :
# iterate through selected features
values = [ ]
request = QgsFeatureRequest ( ) . setSubsetOfAttributes ( [ fieldIndex ] ) . setFlags ( QgsFeatureRequest . NoGeometry )
feats = features ( layer , request )
for feat in feats :
if feat . attributes ( ) [ fieldIndex ] not in values :
values . append ( feat . attributes ( ) [ fieldIndex ] )
return values
else :
# no selection, or not considering selecting
# so we can take advantage of provider side unique value optimisations
return layer . uniqueValues ( fieldIndex )
2013-02-16 00:23:56 +01:00
2013-10-01 20:52:22 +03:00
2013-04-10 15:03:17 +02:00
def resolveFieldIndex ( layer , attr ) :
2013-10-01 20:52:22 +03:00
""" This method takes an object and returns the index field it
refers to in a layer . If the passed object is an integer , it
returns the same integer value . If the passed value is not an
integer , it returns the field whose name is the string
2013-04-10 15:03:17 +02:00
representation of the passed object .
2013-10-01 20:52:22 +03:00
Ir raises an exception if the int value is larger than the number
of fields , or if the passed object does not correspond to any
field .
"""
2013-04-10 15:03:17 +02:00
if isinstance ( attr , int ) :
return attr
else :
2016-09-22 12:09:13 +02:00
index = layer . fields ( ) . lookupField ( attr )
2013-02-16 00:23:56 +01:00
if index == - 1 :
raise ValueError ( ' Wrong field name ' )
2013-04-15 07:16:20 +02:00
return index
2013-04-10 15:03:17 +02:00
def values ( layer , * attributes ) :
2013-10-01 20:52:22 +03:00
""" Returns the values in the attributes table of a vector layer,
for the passed fields .
2013-04-10 15:03:17 +02:00
Field can be passed as field names or as zero - based field indices .
Returns a dict of lists , with the passed field identifiers as keys .
2013-06-03 21:25:22 +02:00
It considers the existing selection .
2013-10-01 20:52:22 +03:00
It assummes fields are numeric or contain values that can be parsed
to a number .
"""
2013-04-10 15:03:17 +02:00
ret = { }
2016-10-12 16:56:29 +10:00
indices = [ ]
attr_keys = { }
2013-04-10 15:03:17 +02:00
for attr in attributes :
index = resolveFieldIndex ( layer , attr )
2016-10-12 16:56:29 +10:00
indices . append ( index )
attr_keys [ index ] = attr
# use an optimised feature request
request = QgsFeatureRequest ( ) . setSubsetOfAttributes ( indices ) . setFlags ( QgsFeatureRequest . NoGeometry )
for feature in features ( layer , request ) :
for i in indices :
# convert attribute value to number
2013-02-16 00:23:56 +01:00
try :
2016-10-12 16:56:29 +10:00
v = float ( feature . attributes ( ) [ i ] )
2013-02-16 00:23:56 +01:00
except :
2016-10-12 16:56:29 +10:00
v = None
k = attr_keys [ i ]
if k in ret :
ret [ k ] . append ( v )
else :
ret [ k ] = [ v ]
2013-02-16 00:23:56 +01:00
return ret
2013-02-28 22:08:32 +01:00
2014-12-18 09:12:35 +02:00
2015-08-22 14:29:41 +02:00
def testForUniqueness ( fieldList1 , fieldList2 ) :
2013-10-22 18:42:52 +01:00
''' Returns a modified version of fieldList2, removing naming
collisions with fieldList1 . '''
changed = True
while changed :
changed = False
2015-08-22 14:29:41 +02:00
for i in range ( 0 , len ( fieldList1 ) ) :
for j in range ( 0 , len ( fieldList2 ) ) :
2013-10-22 18:42:52 +01:00
if fieldList1 [ i ] . name ( ) == fieldList2 [ j ] . name ( ) :
field = fieldList2 [ j ]
2015-08-22 14:29:41 +02:00
name = createUniqueFieldName ( field . name ( ) , fieldList1 )
2013-10-22 18:42:52 +01:00
fieldList2 [ j ] = QgsField ( name , field . type ( ) , len = field . length ( ) , prec = field . precision ( ) , comment = field . comment ( ) )
changed = True
return fieldList2
2013-10-01 20:52:22 +03:00
2014-12-18 09:12:35 +02:00
2013-04-10 15:03:17 +02:00
def spatialindex ( layer ) :
2013-10-01 20:52:22 +03:00
""" Creates a spatial index for the passed vector layer.
"""
2016-10-18 16:54:16 +03:00
request = QgsFeatureRequest ( )
request . setSubsetOfAttributes ( [ ] )
if ProcessingConfig . getSetting ( ProcessingConfig . USE_SELECTED ) \
and layer . selectedFeatureCount ( ) > 0 :
2016-10-19 09:19:00 +03:00
idx = QgsSpatialIndex ( layer . selectedFeaturesIterator ( request ) )
2016-10-18 16:54:16 +03:00
else :
idx = QgsSpatialIndex ( layer . getFeatures ( request ) )
2013-04-10 15:03:17 +02:00
return idx
2013-09-11 19:32:38 +02:00
def createUniqueFieldName ( fieldName , fieldList ) :
2013-12-29 14:35:11 +02:00
def nextname ( name ) :
num = 1
while True :
2015-08-22 14:29:41 +02:00
returnname = ' {name} _ {num} ' . format ( name = name [ : 8 ] , num = num )
2013-12-29 14:35:11 +02:00
yield returnname
num + = 1
2013-09-11 19:32:38 +02:00
2013-12-29 14:35:11 +02:00
def found ( name ) :
return any ( f . name ( ) == name for f in fieldList )
2013-09-11 19:32:38 +02:00
2013-12-29 14:35:11 +02:00
shortName = fieldName [ : 10 ]
2013-09-11 19:32:38 +02:00
2013-12-29 14:35:11 +02:00
if not fieldList :
2013-09-11 19:32:38 +02:00
return shortName
2013-12-29 14:35:11 +02:00
if not found ( shortName ) :
return shortName
2013-09-11 19:32:38 +02:00
2013-12-29 14:35:11 +02:00
for newname in nextname ( shortName ) :
if not found ( newname ) :
return newname
2013-10-01 20:52:22 +03:00
2014-12-18 09:12:35 +02:00
2013-09-11 19:32:38 +02:00
def findOrCreateField ( layer , fieldList , fieldName , fieldLen = 24 , fieldPrec = 15 ) :
2016-09-22 12:09:13 +02:00
idx = layer . fields ( ) . lookupField ( fieldName )
2013-09-11 19:32:38 +02:00
if idx == - 1 :
fn = createUniqueFieldName ( fieldName , fieldList )
2013-10-01 20:52:22 +03:00
field = QgsField ( fn , QVariant . Double , ' ' , fieldLen , fieldPrec )
2013-09-11 19:32:38 +02:00
idx = len ( fieldList )
fieldList . append ( field )
2013-10-01 20:52:22 +03:00
return ( idx , fieldList )
2013-09-11 19:32:38 +02:00
def extractPoints ( geom ) :
points = [ ]
2016-08-04 09:10:08 +02:00
if geom . type ( ) == QgsWkbTypes . PointGeometry :
2013-09-11 19:32:38 +02:00
if geom . isMultipart ( ) :
points = geom . asMultiPoint ( )
else :
points . append ( geom . asPoint ( ) )
2016-08-04 09:10:08 +02:00
elif geom . type ( ) == QgsWkbTypes . LineGeometry :
2013-09-11 19:32:38 +02:00
if geom . isMultipart ( ) :
lines = geom . asMultiPolyline ( )
for line in lines :
points . extend ( line )
else :
points = geom . asPolyline ( )
2016-08-04 09:10:08 +02:00
elif geom . type ( ) == QgsWkbTypes . PolygonGeometry :
2013-09-11 19:32:38 +02:00
if geom . isMultipart ( ) :
polygons = geom . asMultiPolygon ( )
for poly in polygons :
for line in poly :
points . extend ( line )
else :
polygon = geom . asPolygon ( )
for line in polygon :
points . extend ( line )
return points
2013-10-01 20:52:22 +03:00
2013-09-11 19:32:38 +02:00
def simpleMeasure ( geom , method = 0 , ellips = None , crs = None ) :
2013-10-01 20:52:22 +03:00
# Method defines calculation type:
2013-09-11 19:32:38 +02:00
# 0 - layer CRS
# 1 - project CRS
# 2 - ellipsoidal
2013-10-01 20:52:22 +03:00
2016-10-11 08:46:38 +10:00
if geom . type ( ) == QgsWkbTypes . PointGeometry :
if not geom . isMultipart ( ) :
pt = geom . geometry ( )
attr1 = pt . x ( )
attr2 = pt . y ( )
else :
pt = geom . asMultiPoint ( )
attr1 = pt [ 0 ] . x ( )
attr2 = pt [ 0 ] . y ( )
2013-09-11 19:32:38 +02:00
else :
measure = QgsDistanceArea ( )
if method == 2 :
measure . setSourceCrs ( crs )
measure . setEllipsoid ( ellips )
measure . setEllipsoidalMode ( True )
2016-08-04 09:10:08 +02:00
if geom . type ( ) == QgsWkbTypes . PolygonGeometry :
2016-09-15 18:26:43 +10:00
attr1 = measure . measureArea ( geom )
2013-09-11 19:32:38 +02:00
attr2 = measure . measurePerimeter ( geom )
else :
2016-09-15 18:26:43 +10:00
attr1 = measure . measureLength ( geom )
2013-09-11 19:32:38 +02:00
attr2 = None
return ( attr1 , attr2 )
2013-10-01 20:52:22 +03:00
2013-09-11 19:32:38 +02:00
def getUniqueValues ( layer , fieldIndex ) :
values = [ ]
2013-09-12 13:19:00 +02:00
feats = features ( layer )
for feat in feats :
2013-09-11 19:32:38 +02:00
if feat . attributes ( ) [ fieldIndex ] not in values :
values . append ( feat . attributes ( ) [ fieldIndex ] )
return values
2013-10-01 20:52:22 +03:00
2013-09-11 19:32:38 +02:00
def getUniqueValuesCount ( layer , fieldIndex ) :
return len ( getUniqueValues ( layer , fieldIndex ) )
2013-10-01 20:52:22 +03:00
2013-09-11 19:32:38 +02:00
def combineVectorFields ( layerA , layerB ) :
2013-10-01 20:52:22 +03:00
""" Create single field map from two input field maps.
"""
2013-09-11 19:32:38 +02:00
fields = [ ]
2016-08-05 15:05:36 +03:00
fieldsA = layerA . fields ( )
2013-09-11 19:32:38 +02:00
fields . extend ( fieldsA )
2016-09-21 18:24:26 +02:00
namesA = [ str ( f . name ( ) ) . lower ( ) for f in fieldsA ]
2016-08-05 15:05:36 +03:00
fieldsB = layerB . fields ( )
2013-09-11 19:32:38 +02:00
for field in fieldsB :
2016-09-21 18:24:26 +02:00
name = str ( field . name ( ) ) . lower ( )
2013-09-11 19:32:38 +02:00
if name in namesA :
2013-10-01 20:52:22 +03:00
idx = 2
2016-09-21 18:24:26 +02:00
newName = name + ' _ ' + str ( idx )
2013-09-11 19:32:38 +02:00
while newName in namesA :
idx + = 1
2016-09-21 18:24:26 +02:00
newName = name + ' _ ' + str ( idx )
2013-09-11 19:32:38 +02:00
field = QgsField ( newName , field . type ( ) , field . typeName ( ) )
fields . append ( field )
return fields
2013-11-28 12:57:45 +01:00
2014-03-25 19:51:49 +02:00
def duplicateInMemory ( layer , newName = ' ' , addToRegistry = False ) :
""" Return a memory copy of a layer
2013-11-28 12:57:45 +01:00
2014-03-25 19:51:49 +02:00
layer : QgsVectorLayer that shall be copied to memory .
new_name : The name of the copied layer .
add_to_registry : if True , the new layer will be added to the QgsMapRegistry
2013-11-28 12:57:45 +01:00
2014-03-25 19:51:49 +02:00
Returns an in - memory copy of a layer .
2013-11-28 12:57:45 +01:00
"""
2014-03-25 19:51:49 +02:00
if newName is ' ' :
newName = layer . name ( ) + ' (Memory) '
2013-11-28 12:57:45 +01:00
if layer . type ( ) == QgsMapLayer . VectorLayer :
2014-03-25 19:51:49 +02:00
geomType = layer . geometryType ( )
2016-08-04 09:10:08 +02:00
if geomType == QgsWkbTypes . PointGeometry :
2014-03-25 19:51:49 +02:00
strType = ' Point '
2016-08-04 09:10:08 +02:00
elif geomType == QgsWkbTypes . LineGeometry :
2014-03-25 19:51:49 +02:00
strType = ' Line '
2016-08-04 09:10:08 +02:00
elif geomType == QgsWkbTypes . PolygonGeometry :
2014-03-25 19:51:49 +02:00
strType = ' Polygon '
2013-11-28 12:57:45 +01:00
else :
2014-03-25 19:51:49 +02:00
raise RuntimeError ( ' Layer is whether Point nor Line nor Polygon ' )
2013-11-28 12:57:45 +01:00
else :
raise RuntimeError ( ' Layer is not a VectorLayer ' )
crs = layer . crs ( ) . authid ( ) . lower ( )
2016-09-21 18:24:26 +02:00
myUuid = str ( uuid . uuid4 ( ) )
2014-03-25 19:51:49 +02:00
uri = ' %s ?crs= %s &index=yes&uuid= %s ' % ( strType , crs , myUuid )
memLayer = QgsVectorLayer ( uri , newName , ' memory ' )
memProvider = memLayer . dataProvider ( )
2013-11-28 12:57:45 +01:00
provider = layer . dataProvider ( )
2016-08-05 15:05:36 +03:00
fields = layer . fields ( ) . toList ( )
2014-03-25 19:51:49 +02:00
memProvider . addAttributes ( fields )
memLayer . updateFields ( )
2013-11-28 12:57:45 +01:00
for ft in provider . getFeatures ( ) :
2014-03-25 19:51:49 +02:00
memProvider . addFeatures ( [ ft ] )
2013-11-28 12:57:45 +01:00
2014-03-25 19:51:49 +02:00
if addToRegistry :
if memLayer . isValid ( ) :
2016-12-10 15:18:12 +08:00
QgsProject . instance ( ) . addMapLayer ( memLayer )
2013-11-28 12:57:45 +01:00
else :
raise RuntimeError ( ' Layer invalid ' )
2014-04-25 12:39:06 +03:00
return memLayer
2014-12-18 09:12:35 +02:00
2014-04-25 12:39:06 +03:00
def checkMinDistance ( point , index , distance , points ) :
""" Check if distance from given point to all other points is greater
than given value .
"""
if distance == 0 :
return True
neighbors = index . nearestNeighbor ( point , 1 )
if len ( neighbors ) == 0 :
return True
if neighbors [ 0 ] in points :
np = points [ neighbors [ 0 ] ]
if np . sqrDist ( point ) < ( distance * distance ) :
return False
return True
2014-07-14 14:19:09 +02:00
2014-08-03 16:46:13 +02:00
2014-08-28 09:33:05 +03:00
def _toQgsField ( f ) :
2014-08-03 16:46:13 +02:00
if isinstance ( f , QgsField ) :
return f
return QgsField ( f [ 0 ] , TYPE_MAP . get ( f [ 1 ] , QVariant . String ) )
2014-12-18 09:12:35 +02:00
2015-09-29 14:24:17 +02:00
def snapToPrecision ( geom , precision ) :
snapped = QgsGeometry ( geom )
if precision == 0.0 :
return snapped
i = 0
p = snapped . vertexAt ( i )
while p . x ( ) != 0.0 and p . y ( ) != 0.0 :
2015-10-30 23:30:16 +01:00
x = round ( p . x ( ) / precision , 0 ) * precision
y = round ( p . y ( ) / precision , 0 ) * precision
snapped . moveVertex ( x , y , i )
2015-09-29 14:24:17 +02:00
i = i + 1
p = snapped . vertexAt ( i )
return snapped
def bufferedBoundingBox ( bbox , buffer_size ) :
if buffer_size == 0.0 :
return QgsRectangle ( bbox )
return QgsRectangle (
bbox . xMinimum ( ) - buffer_size ,
bbox . yMinimum ( ) - buffer_size ,
bbox . xMaximum ( ) + buffer_size ,
bbox . yMaximum ( ) + buffer_size )
2016-01-22 15:45:09 +02:00
def ogrConnectionString ( uri ) :
""" Generates OGR connection sting from layer source
"""
ogrstr = None
layer = dataobjects . getObjectFromUri ( uri , False )
if layer is None :
return ' " ' + uri + ' " '
provider = layer . dataProvider ( ) . name ( )
if provider == ' spatialite ' :
# dbname='/geodata/osm_ch.sqlite' table="places" (Geometry) sql=
regex = re . compile ( " dbname= ' (.+) ' " )
2016-09-21 18:24:26 +02:00
r = regex . search ( str ( layer . source ( ) ) )
2016-01-22 15:45:09 +02:00
ogrstr = r . groups ( ) [ 0 ]
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=
2016-08-04 09:10:08 +02:00
dsUri = QgsDataSourceUri ( layer . dataProvider ( ) . dataSourceUri ( ) )
2016-01-22 15:45:09 +02:00
conninfo = dsUri . connectionInfo ( )
conn = None
ok = False
while not conn :
try :
conn = psycopg2 . connect ( dsUri . connectionInfo ( ) )
2016-03-21 04:58:12 +01:00
except psycopg2 . OperationalError :
2016-01-22 15:45:09 +02:00
( 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 ( )
elif provider == " oracle " :
# OCI:user/password@host:port/service:table
2016-08-04 09:10:08 +02:00
dsUri = QgsDataSourceUri ( layer . dataProvider ( ) . dataSourceUri ( ) )
2016-01-22 15:45:09 +02:00
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 ( )
else :
2016-09-21 18:24:26 +02:00
ogrstr = str ( layer . source ( ) ) . split ( " | " ) [ 0 ]
2016-01-22 15:45:09 +02:00
return ' " ' + ogrstr + ' " '
def ogrLayerName ( uri ) :
2016-11-11 17:25:45 +02:00
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 ]
2016-11-01 10:25:59 +02:00
elif ' layername ' in uri :
2016-11-11 17:25:45 +02:00
regex = re . compile ( ' (layername=)([^|]*) ' )
2016-11-01 10:25:59 +02:00
r = regex . search ( uri )
return r . groups ( ) [ 1 ]
2016-10-17 11:53:53 +02:00
2016-11-11 17:25:45 +02:00
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
2016-01-22 15:45:09 +02:00
2016-09-21 18:24:26 +02:00
class VectorWriter ( object ) :
2014-07-14 14:19:09 +02:00
MEMORY_LAYER_PREFIX = ' memory: '
2015-11-05 21:16:55 +01:00
POSTGIS_LAYER_PREFIX = ' postgis: '
2015-11-06 17:50:38 +01:00
SPATIALITE_LAYER_PREFIX = ' spatialite: '
2014-07-14 14:19:09 +02:00
2016-04-28 13:57:50 +02:00
nogeometry_extensions = [
u ' csv ' ,
u ' dbf ' ,
u ' ods ' ,
u ' xlsx ' ,
]
2015-11-05 21:16:55 +01:00
def __init__ ( self , destination , encoding , fields , geometryType ,
2014-07-14 14:19:09 +02:00
crs , options = None ) :
2015-11-05 21:16:55 +01:00
self . destination = destination
self . isNotFileBased = False
self . layer = None
2014-07-14 14:19:09 +02:00
self . writer = None
if encoding is None :
settings = QSettings ( )
2016-10-27 21:21:52 +03:00
encoding = settings . value ( ' /Processing/encoding ' , ' System ' , str )
2014-07-14 14:19:09 +02:00
2015-11-05 21:16:55 +01:00
if self . destination . startswith ( self . MEMORY_LAYER_PREFIX ) :
self . isNotFileBased = True
2014-07-14 14:19:09 +02:00
2016-10-05 09:45:55 +03:00
uri = QgsWkbTypes . displayString ( geometryType ) + " ?uuid= " + str ( uuid . uuid4 ( ) )
2014-07-14 14:19:09 +02:00
if crs . isValid ( ) :
2015-02-17 21:40:50 +01:00
uri + = ' &crs= ' + crs . authid ( )
2015-06-23 14:14:09 +02:00
fieldsdesc = [ ]
for f in fields :
qgsfield = _toQgsField ( f )
2015-08-22 14:29:41 +02:00
fieldsdesc . append ( ' field= %s : %s ' % ( qgsfield . name ( ) ,
TYPE_MAP_MEMORY_LAYER . get ( qgsfield . type ( ) , " string " ) ) )
2015-02-17 21:40:50 +01:00
if fieldsdesc :
2015-06-23 14:14:09 +02:00
uri + = ' & ' + ' & ' . join ( fieldsdesc )
2014-07-14 14:19:09 +02:00
2015-11-05 21:16:55 +01:00
self . layer = QgsVectorLayer ( uri , self . destination , ' memory ' )
self . writer = self . layer . dataProvider ( )
elif self . destination . startswith ( self . POSTGIS_LAYER_PREFIX ) :
self . isNotFileBased = True
2016-08-04 09:10:08 +02:00
uri = QgsDataSourceUri ( self . destination [ len ( self . POSTGIS_LAYER_PREFIX ) : ] )
2015-11-05 21:16:55 +01:00
connInfo = uri . connectionInfo ( )
2015-11-10 20:21:10 +00:00
( success , user , passwd ) = QgsCredentials . instance ( ) . get ( connInfo , None , None )
2015-11-05 21:16:55 +01:00
if success :
QgsCredentials . instance ( ) . put ( connInfo , user , passwd )
else :
raise GeoAlgorithmExecutionException ( " Couldn ' t connect to database " )
try :
2016-06-01 14:15:46 +03:00
db = postgis . GeoDB ( host = uri . host ( ) , port = int ( uri . port ( ) ) ,
2016-06-02 08:48:57 +10:00
dbname = uri . database ( ) , user = user , passwd = passwd )
2016-06-01 14:15:46 +03:00
except postgis . DbError as e :
2015-11-05 21:16:55 +01:00
raise GeoAlgorithmExecutionException (
" Couldn ' t connect to database: \n %s " % e . message )
def _runSQL ( sql ) :
try :
2016-09-21 18:24:26 +02:00
db . _exec_sql_and_commit ( str ( sql ) )
2016-06-01 14:15:46 +03:00
except postgis . DbError as e :
2015-11-05 21:16:55 +01:00
raise GeoAlgorithmExecutionException (
' Error creating output PostGIS table: \n %s ' % e . message )
fields = [ _toQgsField ( f ) for f in fields ]
fieldsdesc = " , " . join ( ' %s %s ' % ( f . name ( ) ,
2015-11-10 20:21:10 +00:00
TYPE_MAP_POSTGIS_LAYER . get ( f . type ( ) , " VARCHAR " ) )
2015-11-05 21:16:55 +01:00
for f in fields )
_runSQL ( " CREATE TABLE %s . %s ( %s ) " % ( uri . schema ( ) , uri . table ( ) . lower ( ) , fieldsdesc ) )
2016-08-04 09:10:08 +02:00
if geometryType != QgsWkbTypes . NullGeometry :
2016-04-28 13:57:50 +02:00
_runSQL ( " SELECT AddGeometryColumn( ' {schema} ' , ' {table} ' , ' the_geom ' , {srid} , ' {typmod} ' , 2) " . format (
table = uri . table ( ) . lower ( ) , schema = uri . schema ( ) , srid = crs . authid ( ) . split ( " : " ) [ - 1 ] ,
2016-10-05 09:45:55 +03:00
typmod = QgsWkbTypes . displayString ( geometryType ) . upper ( ) ) )
2015-11-05 21:16:55 +01:00
2015-11-09 08:50:35 +01:00
self . layer = QgsVectorLayer ( uri . uri ( ) , uri . table ( ) , " postgres " )
2015-11-06 17:50:38 +01:00
self . writer = self . layer . dataProvider ( )
elif self . destination . startswith ( self . SPATIALITE_LAYER_PREFIX ) :
self . isNotFileBased = True
2016-08-04 09:10:08 +02:00
uri = QgsDataSourceUri ( self . destination [ len ( self . SPATIALITE_LAYER_PREFIX ) : ] )
2015-11-06 17:50:38 +01:00
try :
2016-06-01 14:15:46 +03:00
db = spatialite . GeoDB ( uri = uri )
except spatialite . DbError as e :
2015-11-06 17:50:38 +01:00
raise GeoAlgorithmExecutionException (
" Couldn ' t connect to database: \n %s " % e . message )
def _runSQL ( sql ) :
try :
2016-09-21 18:24:26 +02:00
db . _exec_sql_and_commit ( str ( sql ) )
2016-06-01 14:15:46 +03:00
except spatialite . DbError as e :
2015-11-06 17:50:38 +01:00
raise GeoAlgorithmExecutionException (
2016-09-21 18:24:26 +02:00
' Error creating output Spatialite table: \n %s ' % str ( e ) )
2015-11-06 17:50:38 +01:00
fields = [ _toQgsField ( f ) for f in fields ]
fieldsdesc = " , " . join ( ' %s %s ' % ( f . name ( ) ,
2015-11-10 20:21:10 +00:00
TYPE_MAP_SPATIALITE_LAYER . get ( f . type ( ) , " VARCHAR " ) )
2015-11-06 17:50:38 +01:00
for f in fields )
2015-11-09 08:50:35 +01:00
2015-11-06 17:50:38 +01:00
_runSQL ( " DROP TABLE IF EXISTS %s " % uri . table ( ) . lower ( ) )
_runSQL ( " CREATE TABLE %s ( %s ) " % ( uri . table ( ) . lower ( ) , fieldsdesc ) )
2016-08-04 09:10:08 +02:00
if geometryType != QgsWkbTypes . NullGeometry :
2016-04-28 13:57:50 +02:00
_runSQL ( " SELECT AddGeometryColumn( ' {table} ' , ' the_geom ' , {srid} , ' {typmod} ' , 2) " . format (
table = uri . table ( ) . lower ( ) , srid = crs . authid ( ) . split ( " : " ) [ - 1 ] ,
2016-10-05 09:45:55 +03:00
typmod = QgsWkbTypes . displayString ( geometryType ) . upper ( ) ) )
2015-11-06 17:50:38 +01:00
self . layer = QgsVectorLayer ( uri . uri ( ) , uri . table ( ) , " spatialite " )
2015-11-05 21:16:55 +01:00
self . writer = self . layer . dataProvider ( )
2014-07-14 14:19:09 +02:00
else :
formats = QgsVectorFileWriter . supportedFiltersAndFormats ( )
OGRCodes = { }
2016-09-21 18:24:26 +02:00
for ( key , value ) in list ( formats . items ( ) ) :
extension = str ( key )
2014-07-14 14:19:09 +02:00
extension = extension [ extension . find ( ' *. ' ) + 2 : ]
extension = extension [ : extension . find ( ' ' ) ]
OGRCodes [ extension ] = value
2016-04-28 13:57:50 +02:00
OGRCodes [ ' dbf ' ] = " DBF file "
2014-07-14 14:19:09 +02:00
2015-11-05 21:16:55 +01:00
extension = self . destination [ self . destination . rfind ( ' . ' ) + 1 : ]
2016-04-28 13:57:50 +02:00
2014-07-14 14:19:09 +02:00
if extension not in OGRCodes :
extension = ' shp '
2015-11-05 21:16:55 +01:00
self . destination = self . destination + ' .shp '
2014-07-14 14:19:09 +02:00
2016-08-10 16:19:29 +10:00
if geometryType == QgsWkbTypes . NoGeometry :
2016-04-28 13:57:50 +02:00
if extension == ' shp ' :
extension = ' dbf '
self . destination = self . destination [ : self . destination . rfind ( ' . ' ) ] + ' .dbf '
if extension not in self . nogeometry_extensions :
raise GeoAlgorithmExecutionException (
" Unsupported format for tables with no geometry " )
2014-07-14 14:19:09 +02:00
qgsfields = QgsFields ( )
for field in fields :
2014-08-03 16:46:13 +02:00
qgsfields . append ( _toQgsField ( field ) )
2014-07-14 14:19:09 +02:00
2016-08-11 19:07:41 +10:00
# use default dataset/layer options
dataset_options = QgsVectorFileWriter . defaultDatasetOptions ( OGRCodes [ extension ] )
layer_options = QgsVectorFileWriter . defaultLayerOptions ( OGRCodes [ extension ] )
2015-11-05 21:16:55 +01:00
self . writer = QgsVectorFileWriter ( self . destination , encoding ,
2016-08-11 19:07:41 +10:00
qgsfields , geometryType , crs , OGRCodes [ extension ] ,
dataset_options , layer_options )
2014-07-14 14:19:09 +02:00
def addFeature ( self , feature ) :
2015-11-05 21:16:55 +01:00
if self . isNotFileBased :
2014-07-14 14:19:09 +02:00
self . writer . addFeatures ( [ feature ] )
else :
self . writer . addFeature ( feature )
2014-08-03 16:46:13 +02:00
2014-07-14 14:19:09 +02:00
2016-09-21 18:24:26 +02:00
class TableWriter ( object ) :
2014-07-14 14:19:09 +02:00
def __init__ ( self , fileName , encoding , fields ) :
self . fileName = fileName
if not self . fileName . lower ( ) . endswith ( ' csv ' ) :
self . fileName + = ' .csv '
self . encoding = encoding
if self . encoding is None or encoding == ' System ' :
self . encoding = ' utf-8 '
with open ( self . fileName , ' wb ' ) as csvFile :
self . writer = UnicodeWriter ( csvFile , encoding = self . encoding )
if len ( fields ) != 0 :
self . writer . writerow ( fields )
def addRecord ( self , values ) :
with open ( self . fileName , ' ab ' ) as csvFile :
self . writer = UnicodeWriter ( csvFile , encoding = self . encoding )
self . writer . writerow ( values )
def addRecords ( self , records ) :
with open ( self . fileName , ' ab ' ) as csvFile :
self . writer = UnicodeWriter ( csvFile , encoding = self . encoding )
self . writer . writerows ( records )
2016-09-21 18:24:26 +02:00
class UnicodeWriter ( object ) :
2014-07-14 14:19:09 +02:00
def __init__ ( self , f , dialect = csv . excel , encoding = ' utf-8 ' , * * kwds ) :
2016-09-21 18:24:26 +02:00
self . queue = io . StringIO ( )
2014-07-14 14:19:09 +02:00
self . writer = csv . writer ( self . queue , dialect = dialect , * * kwds )
self . stream = f
self . encoder = codecs . getincrementalencoder ( encoding ) ( )
def writerow ( self , row ) :
2016-09-21 18:24:26 +02:00
row = list ( map ( str , row ) )
2014-07-14 14:19:09 +02:00
try :
self . writer . writerow ( [ s . encode ( ' utf-8 ' ) for s in row ] )
except :
self . writer . writerow ( row )
data = self . queue . getvalue ( )
data = data . decode ( ' utf-8 ' )
data = self . encoder . encode ( data )
self . stream . write ( data )
self . queue . truncate ( 0 )
def writerows ( self , rows ) :
for row in rows :
2014-08-03 16:46:13 +02:00
self . writerow ( row )