mirror of
https://github.com/qgis/QGIS.git
synced 2025-06-19 00:02:48 -04:00
Add a plugin to DB Manager to support virtual layers
This commit is contained in:
parent
e60712e7cf
commit
9e14f09862
@ -3,6 +3,7 @@ ADD_SUBDIRECTORY(spatialite)
|
||||
IF(WITH_ORACLE)
|
||||
ADD_SUBDIRECTORY(oracle)
|
||||
ENDIF(WITH_ORACLE)
|
||||
ADD_SUBDIRECTORY(vlayers)
|
||||
|
||||
FILE(GLOB PY_FILES *.py)
|
||||
PLUGIN_INSTALL(db_manager db_plugins ${PY_FILES})
|
||||
|
@ -0,0 +1,7 @@
|
||||
|
||||
FILE(GLOB PY_FILES *.py)
|
||||
|
||||
PYQT_ADD_RESOURCES(PYRC_FILES resources.qrc)
|
||||
|
||||
PLUGIN_INSTALL(db_manager db_plugins/vlayers ${PY_FILES} ${PYRC_FILES})
|
||||
|
430
python/plugins/db_manager/db_plugins/vlayers/connector.py
Normal file
430
python/plugins/db_manager/db_plugins/vlayers/connector.py
Normal file
@ -0,0 +1,430 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
/***************************************************************************
|
||||
Name : Virtual layers plugin for DB Manager
|
||||
Date : December 2015
|
||||
copyright : (C) 2015 by Hugo Mercier
|
||||
email : hugo dot mercier at oslandia 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 PyQt4.QtCore import QFile, QUrl, QTemporaryFile
|
||||
from PyQt4.QtGui import QApplication
|
||||
|
||||
from ..connector import DBConnector
|
||||
from ..plugin import ConnectionError, DbError, Table
|
||||
|
||||
from qgis.core import *
|
||||
|
||||
import sqlite3
|
||||
|
||||
|
||||
class sqlite3_connection:
|
||||
|
||||
def __init__(self, sqlite_file):
|
||||
self.conn = sqlite3.connect(sqlite_file)
|
||||
|
||||
def __enter__(self):
|
||||
return self.conn
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.conn.close()
|
||||
|
||||
|
||||
def getQueryGeometryName(sqlite_file):
|
||||
# introspect the file
|
||||
with sqlite3_connection(sqlite_file) as conn:
|
||||
c = conn.cursor()
|
||||
for r in c.execute("SELECT url FROM _meta"):
|
||||
d = QgsVirtualLayerDefinition.fromUrl(QUrl.fromEncoded(r[0]))
|
||||
if d.hasDefinedGeometry():
|
||||
return d.geometryField()
|
||||
return None
|
||||
|
||||
|
||||
def classFactory():
|
||||
return VLayerConnector
|
||||
|
||||
|
||||
# Tables in DB Manager are identified by their display names
|
||||
# This global registry maps a display name with a layer id
|
||||
# It is filled when getVectorTables is called
|
||||
class VLayerRegistry:
|
||||
_instance = None
|
||||
|
||||
@classmethod
|
||||
def instance(cls):
|
||||
if cls._instance == None:
|
||||
cls._instance = VLayerRegistry()
|
||||
return cls._instance
|
||||
|
||||
def __init__(self):
|
||||
self.layers = {}
|
||||
|
||||
def reset(self):
|
||||
self.layers = {}
|
||||
|
||||
def has(self, k):
|
||||
return k in self.layers
|
||||
|
||||
def get(self, k):
|
||||
return self.layers.get(k)
|
||||
|
||||
def __getitem__(self, k):
|
||||
return self.get(k)
|
||||
|
||||
def set(self, k, l):
|
||||
self.layers[k] = l
|
||||
|
||||
def __setitem__(self, k, l):
|
||||
self.set(k, l)
|
||||
|
||||
def items(self):
|
||||
return self.layers.items()
|
||||
|
||||
def getLayer(self, l):
|
||||
lid = self.layers.get(l)
|
||||
if lid is None:
|
||||
return lid
|
||||
return QgsMapLayerRegistry.instance().mapLayer(lid)
|
||||
|
||||
|
||||
class VLayerConnector(DBConnector):
|
||||
|
||||
def __init__(self, uri):
|
||||
pass
|
||||
|
||||
def _execute(self, cursor, sql):
|
||||
# This is only used to get list of fields
|
||||
class DummyCursor:
|
||||
|
||||
def __init__(self, sql):
|
||||
self.sql = sql
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
return DummyCursor(sql)
|
||||
|
||||
def _get_cursor(self, name=None):
|
||||
print "_get_cursor_", name
|
||||
|
||||
def _get_cursor_columns(self, c):
|
||||
tf = QTemporaryFile()
|
||||
tf.open()
|
||||
tmp = tf.fileName()
|
||||
tf.close()
|
||||
|
||||
q = QUrl.toPercentEncoding(c.sql)
|
||||
p = QgsVectorLayer("%s?query=%s" % (tmp, q), "vv", "virtual")
|
||||
if not p.isValid():
|
||||
return []
|
||||
f = [f.name() for f in p.fields()]
|
||||
if p.geometryType() != QGis.WKBNoGeometry:
|
||||
gn = getQueryGeometryName(tmp)
|
||||
if gn:
|
||||
f += [gn]
|
||||
return f
|
||||
|
||||
def uri(self):
|
||||
return QgsDataSourceURI("qgis")
|
||||
|
||||
def getInfo(self):
|
||||
return "info"
|
||||
|
||||
def getSpatialInfo(self):
|
||||
return None
|
||||
|
||||
def hasSpatialSupport(self):
|
||||
return True
|
||||
|
||||
def hasRasterSupport(self):
|
||||
return False
|
||||
|
||||
def hasCustomQuerySupport(self):
|
||||
return True
|
||||
|
||||
def hasTableColumnEditingSupport(self):
|
||||
return False
|
||||
|
||||
def fieldTypes(self):
|
||||
return [
|
||||
"integer", "bigint", "smallint", # integers
|
||||
"real", "double", "float", "numeric", # floats
|
||||
"varchar", "varchar(255)", "character(20)", "text", # strings
|
||||
"date", "datetime" # date/time
|
||||
]
|
||||
|
||||
def getSchemas(self):
|
||||
return None
|
||||
|
||||
def getTables(self, schema=None, add_sys_tables=False):
|
||||
""" get list of tables """
|
||||
return self.getVectorTables()
|
||||
|
||||
def getVectorTables(self, schema=None):
|
||||
""" get list of table with a geometry column
|
||||
it returns:
|
||||
name (table name)
|
||||
is_system_table
|
||||
type = 'view' (is a view?)
|
||||
geometry_column:
|
||||
f_table_name (the table name in geometry_columns may be in a wrong case, use this to load the layer)
|
||||
f_geometry_column
|
||||
type
|
||||
coord_dimension
|
||||
srid
|
||||
"""
|
||||
reg = VLayerRegistry.instance()
|
||||
VLayerRegistry.instance().reset()
|
||||
lst = []
|
||||
for _, l in QgsMapLayerRegistry.instance().mapLayers().items():
|
||||
if l.type() == QgsMapLayer.VectorLayer:
|
||||
|
||||
lname = l.name()
|
||||
# if there is already a layer with this name, use the layer id
|
||||
# as name
|
||||
if reg.has(lname):
|
||||
lname = l.id()
|
||||
VLayerRegistry.instance().set(lname, l.id())
|
||||
|
||||
geomType = None
|
||||
dim = None
|
||||
g = l.dataProvider().geometryType()
|
||||
if g == QGis.WKBPoint:
|
||||
geomType = 'POINT'
|
||||
dim = 'XY'
|
||||
elif g == QGis.WKBLineString:
|
||||
geomType = 'LINESTRING'
|
||||
dim = 'XY'
|
||||
elif g == QGis.WKBPolygon:
|
||||
geomType = 'POLYGON'
|
||||
dim = 'XY'
|
||||
elif g == QGis.WKBMultiPoint:
|
||||
geomType = 'MULTIPOINT'
|
||||
dim = 'XY'
|
||||
elif g == QGis.WKBMultiLineString:
|
||||
geomType = 'MULTILINESTRING'
|
||||
dim = 'XY'
|
||||
elif g == QGis.WKBMultiPolygon:
|
||||
geomType = 'MULTIPOLYGON'
|
||||
dim = 'XY'
|
||||
elif g == QGis.WKBPoint25D:
|
||||
geomType = 'POINT'
|
||||
dim = 'XYZ'
|
||||
elif g == QGis.WKBLineString25D:
|
||||
geomType = 'LINESTRING'
|
||||
dim = 'XYZ'
|
||||
elif g == QGis.WKBPolygon25D:
|
||||
geomType = 'POLYGON'
|
||||
dim = 'XYZ'
|
||||
elif g == QGis.WKBMultiPoint25D:
|
||||
geomType = 'MULTIPOINT'
|
||||
dim = 'XYZ'
|
||||
elif g == QGis.WKBMultiLineString25D:
|
||||
geomType = 'MULTILINESTRING'
|
||||
dim = 'XYZ'
|
||||
elif g == QGis.WKBMultiPolygon25D:
|
||||
geomType = 'MULTIPOLYGON'
|
||||
dim = 'XYZ'
|
||||
lst.append(
|
||||
(Table.VectorType, lname, False, False, l.id(), 'geometry', geomType, dim, l.crs().postgisSrid()))
|
||||
return lst
|
||||
|
||||
def getRasterTables(self, schema=None):
|
||||
return []
|
||||
|
||||
def getTableRowCount(self, table):
|
||||
t = table[1]
|
||||
l = VLayerRegistry.instance().getLayer(t)
|
||||
return l.featureCount()
|
||||
|
||||
def getTableFields(self, table):
|
||||
""" return list of columns in table """
|
||||
t = table[1]
|
||||
l = VLayerRegistry.instance().getLayer(t)
|
||||
# id, name, type, nonnull, default, pk
|
||||
n = l.dataProvider().fields().size()
|
||||
f = [(i, f.name(), f.typeName(), False, None, False)
|
||||
for i, f in enumerate(l.dataProvider().fields())]
|
||||
f += [(n, "geometry", "geometry", False, None, False)]
|
||||
return f
|
||||
|
||||
def getTableIndexes(self, table):
|
||||
return []
|
||||
|
||||
def getTableConstraints(self, table):
|
||||
return None
|
||||
|
||||
def getTableTriggers(self, table):
|
||||
return []
|
||||
|
||||
def deleteTableTrigger(self, trigger, table=None):
|
||||
return
|
||||
|
||||
def getTableExtent(self, table, geom):
|
||||
is_id, t = table
|
||||
if is_id:
|
||||
l = QgsMapLayerRegistry.instance().mapLayer(t)
|
||||
else:
|
||||
l = VLayerRegistry.instance().getLayer(t)
|
||||
e = l.extent()
|
||||
r = (e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum())
|
||||
return r
|
||||
|
||||
def getViewDefinition(self, view):
|
||||
print "**unimplemented** getViewDefinition"
|
||||
|
||||
def getSpatialRefInfo(self, srid):
|
||||
crs = QgsCoordinateReferenceSystem(srid)
|
||||
return crs.description()
|
||||
|
||||
def isVectorTable(self, table):
|
||||
return True
|
||||
|
||||
def isRasterTable(self, table):
|
||||
return False
|
||||
|
||||
def createTable(self, table, field_defs, pkey):
|
||||
print "**unimplemented** createTable"
|
||||
return False
|
||||
|
||||
def deleteTable(self, table):
|
||||
print "**unimplemented** deleteTable"
|
||||
return False
|
||||
|
||||
def emptyTable(self, table):
|
||||
print "**unimplemented** emptyTable"
|
||||
return False
|
||||
|
||||
def renameTable(self, table, new_table):
|
||||
print "**unimplemented** renameTable"
|
||||
return False
|
||||
|
||||
def moveTable(self, table, new_table, new_schema=None):
|
||||
print "**unimplemented** moveTable"
|
||||
return False
|
||||
|
||||
def createView(self, view, query):
|
||||
print "**unimplemented** createView"
|
||||
return False
|
||||
|
||||
def deleteView(self, view):
|
||||
print "**unimplemented** deleteView"
|
||||
return False
|
||||
|
||||
def renameView(self, view, new_name):
|
||||
print "**unimplemented** renameView"
|
||||
return False
|
||||
|
||||
def runVacuum(self):
|
||||
print "**unimplemented** runVacuum"
|
||||
return False
|
||||
|
||||
def addTableColumn(self, table, field_def):
|
||||
print "**unimplemented** addTableColumn"
|
||||
return False
|
||||
|
||||
def deleteTableColumn(self, table, column):
|
||||
print "**unimplemented** deleteTableColumn"
|
||||
|
||||
def updateTableColumn(self, table, column, new_name, new_data_type=None, new_not_null=None, new_default=None):
|
||||
print "**unimplemented** updateTableColumn"
|
||||
|
||||
def renameTableColumn(self, table, column, new_name):
|
||||
print "**unimplemented** renameTableColumn"
|
||||
return False
|
||||
|
||||
def setColumnType(self, table, column, data_type):
|
||||
print "**unimplemented** setColumnType"
|
||||
return False
|
||||
|
||||
def setColumnDefault(self, table, column, default):
|
||||
print "**unimplemented** setColumnDefault"
|
||||
return False
|
||||
|
||||
def setColumnNull(self, table, column, is_null):
|
||||
print "**unimplemented** setColumnNull"
|
||||
return False
|
||||
|
||||
def isGeometryColumn(self, table, column):
|
||||
print "**unimplemented** isGeometryColumn"
|
||||
return False
|
||||
|
||||
def addGeometryColumn(self, table, geom_column='geometry', geom_type='POINT', srid=-1, dim=2):
|
||||
print "**unimplemented** addGeometryColumn"
|
||||
return False
|
||||
|
||||
def deleteGeometryColumn(self, table, geom_column):
|
||||
print "**unimplemented** deleteGeometryColumn"
|
||||
return False
|
||||
|
||||
def addTableUniqueConstraint(self, table, column):
|
||||
print "**unimplemented** addTableUniqueConstraint"
|
||||
return False
|
||||
|
||||
def deleteTableConstraint(self, table, constraint):
|
||||
print "**unimplemented** deleteTableConstraint"
|
||||
return False
|
||||
|
||||
def addTablePrimaryKey(self, table, column):
|
||||
print "**unimplemented** addTablePrimaryKey"
|
||||
return False
|
||||
|
||||
def createTableIndex(self, table, name, column, unique=False):
|
||||
print "**unimplemented** createTableIndex"
|
||||
return False
|
||||
|
||||
def deleteTableIndex(self, table, name):
|
||||
print "**unimplemented** deleteTableIndex"
|
||||
return False
|
||||
|
||||
def createSpatialIndex(self, table, geom_column='geometry'):
|
||||
print "**unimplemented** createSpatialIndex"
|
||||
return False
|
||||
|
||||
def deleteSpatialIndex(self, table, geom_column='geometry'):
|
||||
print "**unimplemented** deleteSpatialIndex"
|
||||
return False
|
||||
|
||||
def hasSpatialIndex(self, table, geom_column='geometry'):
|
||||
print "**unimplemented** hasSpatialIndex"
|
||||
return False
|
||||
|
||||
def execution_error_types(self):
|
||||
print "**unimplemented** execution_error_types"
|
||||
return False
|
||||
|
||||
def connection_error_types(self):
|
||||
print "**unimplemented** connection_error_types"
|
||||
return False
|
||||
|
||||
def getSqlDictionary(self):
|
||||
from .sql_dictionary import getSqlDictionary
|
||||
sql_dict = getSqlDictionary()
|
||||
|
||||
if True:
|
||||
items = []
|
||||
for tbl in self.getTables():
|
||||
items.append(tbl[1]) # table name
|
||||
|
||||
for fld in self.getTableFields((None, tbl[1])):
|
||||
items.append(fld[1]) # field name
|
||||
|
||||
sql_dict["identifier"] = items
|
||||
return sql_dict
|
||||
|
||||
def getQueryBuilderDictionary(self):
|
||||
from .sql_dictionary import getQueryBuilderDictionary
|
||||
|
||||
return getQueryBuilderDictionary()
|
111
python/plugins/db_manager/db_plugins/vlayers/data_model.py
Normal file
111
python/plugins/db_manager/db_plugins/vlayers/data_model.py
Normal file
@ -0,0 +1,111 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
/***************************************************************************
|
||||
Name : Virtual layers plugin for DB Manager
|
||||
Date : December 2015
|
||||
copyright : (C) 2015 by Hugo Mercier
|
||||
email : hugo dot mercier at oslandia 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 ..data_model import TableDataModel, BaseTableModel
|
||||
|
||||
from .connector import VLayerRegistry, getQueryGeometryName
|
||||
from .plugin import LVectorTable
|
||||
from ..plugin import DbError
|
||||
|
||||
from PyQt4.QtCore import QUrl, QTime, QTemporaryFile
|
||||
from qgis.core import QgsProviderRegistry, QgsErrorMessage, QGis, QgsVectorLayer
|
||||
|
||||
import os
|
||||
|
||||
|
||||
class LTableDataModel(TableDataModel):
|
||||
|
||||
def __init__(self, table, parent=None):
|
||||
TableDataModel.__init__(self, table, parent)
|
||||
|
||||
self.layer = None
|
||||
|
||||
if isinstance(table, LVectorTable):
|
||||
self.layer = VLayerRegistry.instance().getLayer(table.name)
|
||||
else:
|
||||
self.layer = VLayerRegistry.instance().getLayer(table)
|
||||
|
||||
if not self.layer:
|
||||
return
|
||||
# populate self.resdata
|
||||
self.resdata = []
|
||||
for f in self.layer.getFeatures():
|
||||
self.resdata.append(f.attributes())
|
||||
|
||||
self.fetchedFrom = 0
|
||||
self.fetchedCount = len(self.resdata)
|
||||
|
||||
def rowCount(self, index=None):
|
||||
if self.layer:
|
||||
return self.layer.featureCount()
|
||||
return 0
|
||||
|
||||
|
||||
class LSqlResultModel(BaseTableModel):
|
||||
# BaseTableModel
|
||||
|
||||
def __init__(self, db, sql, parent=None):
|
||||
# create a virtual layer with non-geometry results
|
||||
q = QUrl.toPercentEncoding(sql)
|
||||
t = QTime()
|
||||
t.start()
|
||||
|
||||
tf = QTemporaryFile()
|
||||
tf.open()
|
||||
tmp = tf.fileName()
|
||||
tf.close()
|
||||
|
||||
p = QgsVectorLayer("%s?query=%s" % (tmp, q), "vv", "virtual")
|
||||
self._secs = t.elapsed() / 1000.0
|
||||
|
||||
if not p.isValid():
|
||||
data = []
|
||||
header = []
|
||||
raise DbError(p.dataProvider().error().summary(), sql)
|
||||
else:
|
||||
header = [f.name() for f in p.fields()]
|
||||
has_geometry = False
|
||||
if p.geometryType() != QGis.WKBNoGeometry:
|
||||
gn = getQueryGeometryName(tmp)
|
||||
if gn:
|
||||
has_geometry = True
|
||||
header += [gn]
|
||||
|
||||
data = []
|
||||
for f in p.getFeatures():
|
||||
a = f.attributes()
|
||||
if has_geometry:
|
||||
if f.geometry():
|
||||
a += [f.geometry().exportToWkt()]
|
||||
else:
|
||||
a += [None]
|
||||
data += [a]
|
||||
|
||||
self._secs = 0
|
||||
self._affectedRows = len(data)
|
||||
|
||||
BaseTableModel.__init__(self, header, data, parent)
|
||||
|
||||
def secs(self):
|
||||
return self._secs
|
||||
|
||||
def affectedRows(self):
|
||||
return self._affectedRows
|
46
python/plugins/db_manager/db_plugins/vlayers/info_model.py
Normal file
46
python/plugins/db_manager/db_plugins/vlayers/info_model.py
Normal file
@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
/***************************************************************************
|
||||
Name : Virtual layers plugin for DB Manager
|
||||
Date : December 2015
|
||||
copyright : (C) 2015 by Hugo Mercier
|
||||
email : hugo dot mercier at oslandia 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 PyQt4.QtGui import QApplication
|
||||
|
||||
from ..info_model import DatabaseInfo
|
||||
from ..html_elems import HtmlTable
|
||||
|
||||
|
||||
class LDatabaseInfo(DatabaseInfo):
|
||||
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
|
||||
def connectionDetails(self):
|
||||
tbl = [
|
||||
]
|
||||
return HtmlTable(tbl)
|
||||
|
||||
def generalInfo(self):
|
||||
info = self.db.connector.getInfo()
|
||||
tbl = [
|
||||
(QApplication.translate("DBManagerPlugin", "SQLite version:"), "3")
|
||||
]
|
||||
return HtmlTable(tbl)
|
||||
|
||||
def privilegesDetails(self):
|
||||
return None
|
191
python/plugins/db_manager/db_plugins/vlayers/plugin.py
Normal file
191
python/plugins/db_manager/db_plugins/vlayers/plugin.py
Normal file
@ -0,0 +1,191 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
/***************************************************************************
|
||||
Name : DB Manager plugin for virtual layers
|
||||
Date : December 2015
|
||||
copyright : (C) 2015 by Hugo Mercier
|
||||
email : hugo dot mercier at oslandia 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. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
"""
|
||||
|
||||
# this will disable the dbplugin if the connector raise an ImportError
|
||||
from .connector import VLayerConnector
|
||||
|
||||
from PyQt4.QtCore import Qt, QSettings, QUrl
|
||||
from PyQt4.QtGui import QIcon, QApplication, QAction
|
||||
from qgis.core import QgsVectorLayer, QgsMapLayerRegistry
|
||||
from qgis.gui import QgsMessageBar
|
||||
|
||||
from ..plugin import DBPlugin, Database, Table, VectorTable, RasterTable, TableField, TableIndex, TableTrigger, InvalidDataException
|
||||
try:
|
||||
from . import resources_rc
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def classFactory():
|
||||
return VLayerDBPlugin
|
||||
|
||||
|
||||
class VLayerDBPlugin(DBPlugin):
|
||||
|
||||
@classmethod
|
||||
def icon(self):
|
||||
return QIcon(":/db_manager/vlayers/icon")
|
||||
|
||||
@classmethod
|
||||
def typeName(self):
|
||||
return 'vlayers'
|
||||
|
||||
@classmethod
|
||||
def typeNameString(self):
|
||||
return 'Virtual Layers'
|
||||
|
||||
@classmethod
|
||||
def providerName(self):
|
||||
return 'virtual'
|
||||
|
||||
@classmethod
|
||||
def connectionSettingsKey(self):
|
||||
return 'vlayers'
|
||||
|
||||
@classmethod
|
||||
def connections(self):
|
||||
return [VLayerDBPlugin('QGIS layers')]
|
||||
|
||||
def databasesFactory(self, connection, uri):
|
||||
return FakeDatabase(connection, uri)
|
||||
|
||||
def database(self):
|
||||
return self.db
|
||||
|
||||
# def info( self ):
|
||||
|
||||
def connect(self, parent=None):
|
||||
self.connectToUri("qgis")
|
||||
return True
|
||||
|
||||
|
||||
class FakeDatabase(Database):
|
||||
|
||||
def __init__(self, connection, uri):
|
||||
Database.__init__(self, connection, uri)
|
||||
|
||||
def connectorsFactory(self, uri):
|
||||
return VLayerConnector(uri)
|
||||
|
||||
def dataTablesFactory(self, row, db, schema=None):
|
||||
return LTable(row, db, schema)
|
||||
|
||||
def vectorTablesFactory(self, row, db, schema=None):
|
||||
return LVectorTable(row, db, schema)
|
||||
|
||||
def rasterTablesFactory(self, row, db, schema=None):
|
||||
return None
|
||||
|
||||
def info(self):
|
||||
from .info_model import LDatabaseInfo
|
||||
return LDatabaseInfo(self)
|
||||
|
||||
def sqlResultModel(self, sql, parent):
|
||||
from .data_model import LSqlResultModel
|
||||
return LSqlResultModel(self, sql, parent)
|
||||
|
||||
def toSqlLayer(self, sql, geomCol, uniqueCol, layerName="QueryLayer", layerType=None, avoidSelectById=False, _filter=""):
|
||||
q = QUrl.toPercentEncoding(sql)
|
||||
s = "?query=%s" % q
|
||||
if uniqueCol is not None:
|
||||
s += "&uid=" + uniqueCol
|
||||
if geomCol is not None:
|
||||
s += "&geometry=" + geomCol
|
||||
vl = QgsVectorLayer(s, layerName, "virtual")
|
||||
if _filter:
|
||||
vl.setSubsetString(_filter)
|
||||
return vl
|
||||
|
||||
def registerDatabaseActions(self, mainWindow):
|
||||
return
|
||||
|
||||
def runAction(self, action):
|
||||
return
|
||||
|
||||
def uniqueIdFunction(self):
|
||||
return None
|
||||
|
||||
def explicitSpatialIndex(self):
|
||||
return True
|
||||
|
||||
def spatialIndexClause(self, src_table, src_column, dest_table, dest_column):
|
||||
return '"%s"._search_frame_ = "%s"."%s"' % (src_table, dest_table, dest_column)
|
||||
|
||||
|
||||
class LTable(Table):
|
||||
|
||||
def __init__(self, row, db, schema=None):
|
||||
Table.__init__(self, db, None)
|
||||
self.name, self.isView, self.isSysTable = row
|
||||
|
||||
def tableFieldsFactory(self, row, table):
|
||||
return LTableField(row, table)
|
||||
|
||||
def tableDataModel(self, parent):
|
||||
from .data_model import LTableDataModel
|
||||
return LTableDataModel(self, parent)
|
||||
|
||||
def canBeAddedToCanvas(self):
|
||||
return False
|
||||
|
||||
|
||||
class LVectorTable(LTable, VectorTable):
|
||||
|
||||
def __init__(self, row, db, schema=None):
|
||||
LTable.__init__(self, row[:-5], db, schema)
|
||||
VectorTable.__init__(self, db, schema)
|
||||
# SpatiaLite does case-insensitive checks for table names, but the
|
||||
# SL provider didn't do the same in QGis < 1.9, so self.geomTableName
|
||||
# stores the table name like stored in the geometry_columns table
|
||||
self.geomTableName, self.geomColumn, self.geomType, self.geomDim, self.srid = row[
|
||||
-5:]
|
||||
|
||||
def uri(self):
|
||||
uri = self.database().uri()
|
||||
uri.setDataSource('', self.geomTableName, self.geomColumn)
|
||||
return uri
|
||||
|
||||
def hasSpatialIndex(self, geom_column=None):
|
||||
return True
|
||||
|
||||
def createSpatialIndex(self, geom_column=None):
|
||||
return
|
||||
|
||||
def deleteSpatialIndex(self, geom_column=None):
|
||||
return
|
||||
|
||||
def refreshTableEstimatedExtent(self):
|
||||
self.extent = self.database().connector.getTableExtent(
|
||||
("id", self.geomTableName), None)
|
||||
|
||||
def runAction(self, action):
|
||||
return
|
||||
|
||||
def toMapLayer(self):
|
||||
return QgsMapLayerRegistry.instance().mapLayer(self.geomTableName)
|
||||
|
||||
|
||||
class LTableField(TableField):
|
||||
|
||||
def __init__(self, row, table):
|
||||
TableField.__init__(self, table)
|
||||
self.num, self.name, self.dataType, self.notNull, self.default, self.primaryKey = row
|
||||
self.hasDefault = self.default
|
@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/db_manager/vlayers">
|
||||
<file alias="icon">vlayer.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
136
python/plugins/db_manager/db_plugins/vlayers/sql_dictionary.py
Normal file
136
python/plugins/db_manager/db_plugins/vlayers/sql_dictionary.py
Normal file
@ -0,0 +1,136 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# keywords
|
||||
keywords = [
|
||||
# TODO get them from a reference page
|
||||
"action", "add", "after", "all", "alter", "analyze", "and", "as", "asc",
|
||||
"before", "begin", "between", "by", "cascade", "case", "cast", "check",
|
||||
"collate", "column", "commit", "constraint", "create", "cross", "current_date",
|
||||
"current_time", "current_timestamp", "default", "deferrable", "deferred",
|
||||
"delete", "desc", "distinct", "drop", "each", "else", "end", "escape",
|
||||
"except", "exists", "for", "foreign", "from", "full", "group", "having",
|
||||
"ignore", "immediate", "in", "initially", "inner", "insert", "intersect",
|
||||
"into", "is", "isnull", "join", "key", "left", "like", "limit", "match",
|
||||
"natural", "no", "not", "notnull", "null", "of", "offset", "on", "or", "order",
|
||||
"outer", "primary", "references", "release", "restrict", "right", "rollback",
|
||||
"row", "savepoint", "select", "set", "table", "temporary", "then", "to",
|
||||
"transaction", "trigger", "union", "unique", "update", "using", "values",
|
||||
"view", "when", "where",
|
||||
|
||||
"abort", "attach", "autoincrement", "conflict", "database", "detach",
|
||||
"exclusive", "explain", "fail", "glob", "if", "index", "indexed", "instead",
|
||||
"plan", "pragma", "query", "raise", "regexp", "reindex", "rename", "replace",
|
||||
"temp", "vacuum", "virtual"
|
||||
]
|
||||
spatialite_keywords = []
|
||||
|
||||
# functions
|
||||
functions = [
|
||||
# TODO get them from a reference page
|
||||
"changes", "coalesce", "glob", "ifnull", "hex", "last_insert_rowid",
|
||||
"nullif", "quote", "random",
|
||||
"randomblob", "replace", "round", "soundex", "total_change",
|
||||
"typeof", "zeroblob", "date", "datetime", "julianday", "strftime"
|
||||
]
|
||||
operators = [
|
||||
' AND ', ' OR ', '||', ' < ', ' <= ', ' > ', ' >= ', ' = ', ' <> ', ' IS ', ' IS NOT ', ' IN ', ' LIKE ', ' GLOB ', ' MATCH ', ' REGEXP '
|
||||
]
|
||||
|
||||
math_functions = [
|
||||
# SQL math functions
|
||||
"Abs", "ACos", "ASin", "ATan", "Cos", "Cot", "Degrees", "Exp", "Floor", "Log", "Log2",
|
||||
"Log10", "Pi", "Radians", "Round", "Sign", "Sin", "Sqrt", "StdDev_Pop", "StdDev_Samp", "Tan",
|
||||
"Var_Pop", "Var_Samp"]
|
||||
|
||||
string_functions = ["Length", "Lower", "Upper", "Like", "Trim", "LTrim", "RTrim", "Replace", "Substr"]
|
||||
|
||||
aggregate_functions = [
|
||||
"Max", "Min", "Avg", "Count", "Sum", "Group_Concat", "Total", "Var_Pop", "Var_Samp", "StdDev_Pop", "StdDev_Samp"
|
||||
]
|
||||
|
||||
spatialite_functions = [ # from www.gaia-gis.it/spatialite-2.3.0/spatialite-sql-2.3.0.html
|
||||
# SQL utility functions for BLOB objects
|
||||
"*iszipblob", "*ispdfblob", "*isgifblob", "*ispngblob", "*isjpegblob", "*isexifblob",
|
||||
"*isexifgpsblob", "*geomfromexifgpsblob", "MakePoint", "BuildMbr", "*buildcirclembr", "ST_MinX",
|
||||
"ST_MinY", "ST_MaxX", "ST_MaxY",
|
||||
# SQL functions for constructing a geometric object given its Well-known Text Representation
|
||||
"ST_GeomFromText", "*pointfromtext",
|
||||
# SQL functions for constructing a geometric object given its Well-known Binary Representation
|
||||
"*geomfromwkb", "*pointfromwkb",
|
||||
# SQL functions for obtaining the Well-known Text / Well-known Binary Representation of a geometric object
|
||||
"ST_AsText", "ST_AsBinary",
|
||||
# SQL functions supporting exotic geometric formats
|
||||
"*assvg", "*asfgf", "*geomfromfgf",
|
||||
# SQL functions on type Geometry
|
||||
"ST_Dimension", "ST_GeometryType", "ST_Srid", "ST_SetSrid", "ST_isEmpty", "ST_isSimple", "ST_isValid", "ST_Boundary",
|
||||
"ST_Envelope",
|
||||
# SQL functions on type Point
|
||||
"ST_X", "ST_Y",
|
||||
# SQL functions on type Curve [Linestring or Ring]
|
||||
"ST_StartPoint", "ST_EndPoint", "ST_Length", "ST_isClosed", "ST_isRing", "ST_Simplify",
|
||||
"*simplifypreservetopology",
|
||||
# SQL functions on type LineString
|
||||
"ST_NumPoints", "ST_PointN",
|
||||
# SQL functions on type Surface [Polygon or Ring]
|
||||
"ST_Centroid", "ST_PointOnSurface", "ST_Area",
|
||||
# SQL functions on type Polygon
|
||||
"ST_ExteriorRing", "ST_InteriorRingN",
|
||||
# SQL functions on type GeomCollection
|
||||
"ST_NumGeometries", "ST_GeometryN",
|
||||
# SQL functions that test approximative spatial relationships via MBRs
|
||||
"MbrEqual", "MbrDisjoint", "MbrTouches", "MbrWithin", "MbrOverlaps", "MbrIntersects",
|
||||
"MbrContains",
|
||||
# SQL functions that test spatial relationships
|
||||
"ST_Equals", "ST_Disjoint", "ST_Touches", "ST_Within", "ST_Overlaps", "ST_Crosses", "ST_Intersects", "ST_Contains",
|
||||
"ST_Relate",
|
||||
# SQL functions for distance relationships
|
||||
"ST_Distance",
|
||||
# SQL functions that implement spatial operators
|
||||
"ST_Intersection", "ST_Difference", "ST_Union", "ST_SymDifference", "ST_Buffer", "ST_ConvexHull",
|
||||
# SQL functions for coordinate transformations
|
||||
"ST_Transform",
|
||||
# SQL functions for Spatial-MetaData and Spatial-Index handling
|
||||
"*initspatialmetadata", "*addgeometrycolumn", "*recovergeometrycolumn", "*discardgeometrycolumn",
|
||||
"*createspatialindex", "*creatembrcache", "*disablespatialindex",
|
||||
# SQL functions implementing FDO/OGR compatibily
|
||||
"*checkspatialmetadata", "*autofdostart", "*autofdostop", "*initfdospatialmetadata",
|
||||
"*addfdogeometrycolumn", "*recoverfdogeometrycolumn", "*discardfdogeometrycolumn",
|
||||
# SQL functions for MbrCache-based queries
|
||||
"*filtermbrwithin", "*filtermbrcontains", "*filtermbrintersects", "*buildmbrfilter"
|
||||
]
|
||||
|
||||
# constants
|
||||
constants = ["null", "false", "true"]
|
||||
spatialite_constants = []
|
||||
|
||||
|
||||
def getSqlDictionary(spatial=True):
|
||||
def strip_star(s):
|
||||
if s[0] == '*':
|
||||
return s.lower()[1:]
|
||||
else:
|
||||
return s.lower()
|
||||
|
||||
k, c, f = list(keywords), list(constants), list(functions)
|
||||
|
||||
if spatial:
|
||||
k += spatialite_keywords
|
||||
f += spatialite_functions
|
||||
c += spatialite_constants
|
||||
|
||||
return {'keyword': map(strip_star, k), 'constant': map(strip_star, c), 'function': map(strip_star, f)}
|
||||
|
||||
|
||||
def getQueryBuilderDictionary():
|
||||
# concat functions
|
||||
def ff(l):
|
||||
return filter(lambda s: s[0] != '*', l)
|
||||
|
||||
def add_paren(l):
|
||||
return map(lambda s: s + "(", l)
|
||||
foo = sorted(add_paren(ff(list(set.union(set(functions), set(spatialite_functions))))))
|
||||
m = sorted(add_paren(ff(math_functions)))
|
||||
agg = sorted(add_paren(ff(aggregate_functions)))
|
||||
op = ff(operators)
|
||||
s = sorted(add_paren(ff(string_functions)))
|
||||
return {'function': foo, 'math': m, 'aggregate': agg, 'operator': op, 'string': s}
|
242
python/plugins/db_manager/db_plugins/vlayers/vlayer.svg
Normal file
242
python/plugins/db_manager/db_plugins/vlayers/vlayer.svg
Normal file
@ -0,0 +1,242 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="32px"
|
||||
height="32px"
|
||||
id="svg5692"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="vlayer.svg"
|
||||
inkscape:export-filename="/media/home1/robert/svn/graphics/trunk/toolbar-icons/32x32/layer-vector.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<title
|
||||
id="title2829">GIS icon theme 0.2</title>
|
||||
<defs
|
||||
id="defs5694">
|
||||
<linearGradient
|
||||
id="linearGradient3812">
|
||||
<stop
|
||||
style="stop-color:#6e97c4;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3814" />
|
||||
<stop
|
||||
style="stop-color:#6e97c4;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3816" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 16 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="32 : 16 : 1"
|
||||
inkscape:persp3d-origin="16 : 10.666667 : 1"
|
||||
id="perspective3486" />
|
||||
<inkscape:perspective
|
||||
id="perspective3496"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3600"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective7871"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective8710"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective9811"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective4762"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3812"
|
||||
id="linearGradient3818"
|
||||
x1="37.316311"
|
||||
y1="36.925365"
|
||||
x2="3.1572213"
|
||||
y2="3.1572211"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.9375"
|
||||
inkscape:cx="8.4239343"
|
||||
inkscape:cy="11.653661"
|
||||
inkscape:current-layer="layer2"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
borderlayer="false"
|
||||
inkscape:window-width="1377"
|
||||
inkscape:window-height="807"
|
||||
inkscape:window-x="1932"
|
||||
inkscape:window-y="134"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:snap-global="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-grids="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5700"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true"
|
||||
dotted="true"
|
||||
originx="2.5px"
|
||||
originy="2.5px" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5697">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>GIS icon theme 0.2</dc:title>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Robert Szczepanek</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>Robert Szczepanek</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>GIS icons</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
<dc:coverage>GIS icons</dc:coverage>
|
||||
<dc:description>http://robert.szczepanek.pl/</dc:description>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/3.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Layer"
|
||||
style="display:inline">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;fill:none;stroke:#415a75;stroke-width:1.78710628;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="M 3.1572212,3.1572206 11.707585,23.749318 21.110969,3.3492624 28.84278,3.3200046"
|
||||
id="path2960"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#415a75;stroke-width:0.78947365;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path2958"
|
||||
sodipodi:cx="3.5"
|
||||
sodipodi:cy="12.5"
|
||||
sodipodi:rx="1"
|
||||
sodipodi:ry="1"
|
||||
d="m 4.5,12.5 a 1,1 0 1 1 -2,0 1,1 0 1 1 2,0 z"
|
||||
transform="matrix(2.263668,0,0,2.263668,-4.7656167,-25.138629)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#415a75;stroke-width:1.05263162;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path2958-0"
|
||||
sodipodi:cx="3.5"
|
||||
sodipodi:cy="12.5"
|
||||
sodipodi:rx="1"
|
||||
sodipodi:ry="1"
|
||||
d="m 4.5,12.5 a 1,1 0 1 1 -2,0 1,1 0 1 1 2,0 z"
|
||||
transform="matrix(2.263668,0,0,2.263668,3.8127783,-5.6260389)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#415a75;stroke-width:0.78947365;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path2958-9"
|
||||
sodipodi:cx="3.5"
|
||||
sodipodi:cy="12.5"
|
||||
sodipodi:rx="1"
|
||||
sodipodi:ry="1"
|
||||
d="m 4.5,12.5 a 1,1 0 1 1 -2,0 1,1 0 1 1 2,0 z"
|
||||
transform="matrix(2.263668,0,0,2.263668,12.949214,-24.994533)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#415a75;stroke-width:0.78947365;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path2958-6"
|
||||
sodipodi:cx="3.5"
|
||||
sodipodi:cy="12.5"
|
||||
sodipodi:rx="1"
|
||||
sodipodi:ry="1"
|
||||
d="m 4.5,12.5 a 1,1 0 1 1 -2,0 1,1 0 1 1 2,0 z"
|
||||
transform="matrix(2.263668,0,0,2.263668,20.919941,-24.975845)" />
|
||||
<path
|
||||
style="color:#000000;fill:url(#linearGradient3818);fill-opacity:1;stroke:#2e4e72;stroke-width:1.12999999999999989;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;fill-rule:nonzero;opacity:1"
|
||||
d="m 4.8072919,3.0420338 22.3854161,0 c 1.042824,0 1.875,0.832177 1.875,1.875001 l 0,22.1971812 c 0,1.042824 -0.832176,1.90625 -1.875,1.90625 l -22.3854161,0 c -1.0428239,0 -1.875,-0.863426 -1.875,-1.90625 l 0,-22.1971812 c 0,-1.042824 0.8321761,-1.875001 1.875,-1.875001 z"
|
||||
id="rect3022"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssssss" />
|
||||
<path
|
||||
style="color:#000000;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.12999999999999989;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="m 0,0 0,32.0625 32,0 L 32,0 z m 4.7761029,3.0844363 22.3854161,0 c 1.042824,0 1.875,0.8321765 1.875,1.8750003 l 0,22.1971814 c 0,1.042824 -0.832176,1.90625 -1.875,1.90625 l -22.3854161,0 c -1.0428235,0 -1.875,-0.863426 -1.875,-1.90625 l 0,-22.1971814 c 0,-1.0428238 0.8321765,-1.8750003 1.875,-1.8750003 z"
|
||||
id="path3078"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccsssssssss" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 10 KiB |
Loading…
x
Reference in New Issue
Block a user