# -*- coding: utf-8 -*-
### Copyright (C) 2008-2012 Antonio Valentino <a_valentino@users.sf.net>
### This file is part of GSDView.
### GSDView 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.
### GSDView is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
### GNU General Public License for more details.
### You should have received a copy of the GNU General Public License
### along with GSDView; if not, write to the Free Software
### Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
'''Helper tools and custom components for binding OGR and Qt4.'''
__author__ = 'Antonio Valentino <a_valentino@users.sf.net>'
__date__ = '$Date$'
__revision__ = '$Revision$'
import logging
from osgeo import ogr, osr
from qt import QtCore, QtGui
from .. import qt4draw
### Graphics Items ############################################################
#~ class GraphicsLayerItem(qt4draw.GraphicsItemGroup):
#~ '''Qt graphics item representing an OGR Layer.'''
#~ Type = QtGui.QGraphicsItem.UserType + 102
#~ def __init__(self, name=None, index=None, datasource=None,
#~ parent=None, scene=None, **kargs):
#~ super(GraphicsLayerItem, self).__init__(parent, scene, **kargs)
#~ self.name = name
#~ self.index = index
#~ self.datasource = datasource
#~ if name:
#~ self.setToolTip('Layer: %s' % name)
#~ def graphicsFeature(self, fid):
#~ #return self.childItems()[fid]
#~ for item in self.childItems():
#~ try:
#~ if item.fid == fid:
#~ return item
#~ except AttributeError:
#~ logging.debug('item "%s" has no feature ID.' % item)
#~ return None
#~ class GraphicsFeatureItem(qt4draw.GraphicsItemGroup):
#~ '''Qt graphics item representing an OGR feature.'''
#~ Type = QtGui.QGraphicsItem.UserType + 103
#~ def __init__(self, fid=None, parent=None, scene=None, **kargs):
#~ super(GraphicsFeatureItem, self).__init__(parent, scene, **kargs)
#~ self.fid = fid
### Helpers for geometry management ###########################################
[docs]def singleGeometryToGraphicsItem(geom, transform=None):
'''Convert a single OGR geometry into a Qt4 graphics item.
A "single geometry" is an OGR gemetry that don't include other
geometries (GetGeometryCount() == 0).
If the *transform* callable is provided then each point in the
geometry is converted using the `transform(x, y, z)` call before
genereting the graphics item path.
.. note: for 2.5D geometries the *z* value is ignored.
:param geom:
a single OGR geometry
:param transform:
callable object for arbitrary coordinate conversion
:returns:
a Qt4 graphics item representing the geometry
.. seealso:: :func:`geometryToGraphicsItem`
'''
assert geom.GetGeometryCount() == 0
gtype = geom.GetGeometryType()
if gtype in (ogr.wkbPoint, ogr.wkbPoint25D):
# @TODO: check
RADIUS = 3
point = geom.GetPoint()
if transform:
point = transform(*point)
qitem = qt4draw.GraphicsPointItem(point[0], point[1], RADIUS)
# @TODO: style options should be set in a more general way.
# Probably it is better to set them in an external function.
# Red point
pen = qitem.pen()
pen.setColor(QtCore.Qt.red)
#pen.setWidth(15)
qitem.setPen(pen)
brush = qitem.brush()
brush.setColor(QtCore.Qt.red)
#brush.setStyle(QtCore.Qt.SolidPattern)
qitem.setBrush(brush)
elif (gtype in (ogr.wkbLineString, ogr.wkbLineString25D) and
geom.GetPointCount() == 2):
p0 = geom.GetPoint(0)
p1 = geom.GetPoint(1)
if transform:
p0 = transform(*p0)
p1 = transform(*p1)
qline = QtCore.QLineF(p0[0], p0[1], p1[0], p1[1])
qitem = QtGui.QGraphicsLineItem(qline)
elif gtype in (ogr.wkbLinearRing, ogr.wkbPolygon, ogr.wkbPolygon25D,
ogr.wkbLineString, ogr.wkbLineString25D):
# @NOTE: use only if geometry is a ring
if geom.IsRing():
qpoly = QtGui.QPolygonF(geom.GetPointCount())
for index in range(geom.GetPointCount()):
point = geom.GetPoint(index)
if transform:
point = transform(*point)
qpoly[index] = QtCore.QPointF(point[0], point[1])
qitem = QtGui.QGraphicsPolygonItem(qpoly)
#qitem.setFillRule(QtCore.Qt.WindingFill) # @TODO: check
else:
qpath = QtGui.QPainterPath()
#qpath.setFillRule(QtCore.Qt.WindingFill) # @TODO: check
point = geom.GetPoint(0)
if transform:
point = transform(*point)
qpath.moveTo(point[0], point[1])
for index in range(1, geom.GetPointCount()):
point = geom.GetPoint(index)
if transform:
point = transform(*point)
qpath.lineTo(point[0], point[1])
qitem = QtGui.QGraphicsPathItem(qpath)
elif gtype in (ogr.wkbMultiPoint, ogr.wkbMultiPoint25D,
ogr.wkbMultiLineString, ogr.wkbMultiLineString25D,
ogr.wkbMultiPolygon, ogr.wkbMultiPolygon25D,
ogr.wkbGeometryCollection, ogr.wkbGeometryCollection25D):
raise ValueError('should not happen.')
elif gtype in (ogr.wkbUnknown, ogr.wkbNone):
raise ValueError('invalid geopetry type: '
'"%s"' % geom.GetGeometryName())
else:
raise ValueError('invalid geopetry type: "%d"' % gtype)
return qitem
[docs]def geometryToGraphicsItem(geom, transform=None):
'''Convert an OGR geometry into a Qt4 graphics item.
If the *transform* callable is provided then each point in the
geometry is converted using the `transform(x, y, z)` call before
genereting the graphics item path.
.. note: for 2.5D geometries the *z* value is ignored.
:param geom:
an OGR geometry
:param transform:
callable object for arbitrary coordinate conversion
:returns:
a Qt4 graphics item representing the geometry
.. seealso:: :func:`singleGeometryToGraphicsItem`
'''
if geom.GetGeometryCount() > 1:
#qitem = QtGui.QGraphicsItemGroup()
qitem = qt4draw.GraphicsItemGroup()
for index, subgeom in enumerate(geom):
qsubitem = geometryToGraphicsItem(subgeom, transform)
if qsubitem:
qsubitem.setData(DATAKEY['index'], index)
qitem.addToGroup(qsubitem)
else:
logging.debug('unable to instantiate a graphics item from '
'OGR geometry "%s"' % subgeom)
#qitem.setFlag(QtGui.QGraphicsItem.ItemIsSelectable)
return qitem
elif geom.GetGeometryCount() == 1:
subgeom = geom.GetGeometryRef(0)
return geometryToGraphicsItem(subgeom, transform)
else:
qitem = singleGeometryToGraphicsItem(geom, transform)
qitem.setFlag(QtGui.QGraphicsItem.ItemIsSelectable)
return qitem
#: the max number of features that are converted whan the graphics item
#: for a layer is generated
MAX_FEATURE_COUNT = 600
DATAKEY = {
'name': 1,
'index': 2,
'datasource': 3,
'FID': 4,
}
[docs]def layerToGraphicsItem(layer, srs=None, transform=None):
'''Convert an OGR layer into a Qt4 graphics item.
If the *srs* parameter is provided each feature is converted into
the target spatial reference system before generating the graphics
item.
If the *transform* callable is provided then each point in the
geometry is converted using the `transform(x, y, z)` call before
genereting the graphics item path.
.. note: for 2.5D geometries the *z* value is ignored.
:param layer:
ane OGR layer
:param srs:
the target OSR spatial reference system
:param transform:
callable object for arbitrary coordinate conversion
:returns:
a Qt4 graphics item (QGraphicsItemGroup) representing the layer
.. seealso:: :func:`singleGeometryToGraphicsItem`,
:func:`geometryToGraphicsItem` and
:data:`MAX_FEATURE_COUNT`
'''
layer_srs = layer.GetSpatialRef()
if (srs is not None and layer_srs is not None and
not layer_srs.IsSame(srs)):
srs_transform = osr.CoordinateTransformation(layer_srs, srs)
else:
srs_transform = None
if layer.GetFeatureCount() > MAX_FEATURE_COUNT:
raise RuntimeError('too many features in layed %s: %d' % (
layer.GetName(), layer.GetFeatureCount()))
#~ print 'extent:', layer.GetExtent() # @TODO: check
#qlayer = GraphicsLayerItem(layer.GetName())
qlayer = qt4draw.GraphicsItemGroup()
qlayer.setData(DATAKEY['name'], layer.GetName())
for feature in layer:
#qfeature = GraphicsFeatureItem(feature.GetFID())
qfeature = qt4draw.GraphicsItemGroup()
qfeature.setData(DATAKEY['FID'], feature.GetFID())
geom = feature.GetGeometryRef()
if geom:
geotransform = srs_transform
geom_srs = geom.GetSpatialReference()
if geom_srs:
if (layer_srs is not None and not geom_srs.IsSame(layer_srs)):
if srs is not None:
geotransform = osr.CoordinateTransformation(geom_srs,
srs)
else:
geotransform = osr.CoordinateTransformation(geom_srs,
layer_srs)
elif srs is not None and not geom_srs.IsSame(srs):
geotransform = osr.CoordinateTransformation(geom_srs, srs)
if geotransform:
geom = transformGeometry(geom, geotransform)
qitem = geometryToGraphicsItem(geom, transform)
if qitem:
qfeature.addToGroup(qitem)
else:
logging.warning('unable to instantiate a graphics '
'item from OGR geometry "%s"' % geom)
else:
logging.info('feature %d has no geometry' %
feature.GetFID())
qlayer.addToGroup(qfeature)
nfeatures = len(qlayer.childItems())
if nfeatures != layer.GetFeatureCount():
logging.warning('only %d of %d geometries converted to graphics items '
'for layer "%s"' % (nfeatures, layer.GetFeatureCount(),
layer.GetName()))
qlayer.setToolTip('Layer "%s": %d features.' % (layer.GetName(),
nfeatures))
return qlayer
### Helpers for layers management #############################################
#~ class LayerItemModel(QtGui.QStandardItemModel):
#~ #def __init__(self, parent=None, **kargs):
#~ # super(LayerItemModel, self).__init__(parent, **kargs)
#~ # # @TODO: spatial filter
#~ def addLayer(self, layer):
#~ '''Add a new layer on top of the stack.'''
#~ pass
#~ def insertLayer(self, row, layer):
#~ '''Insert a new layer in the specified position.'''
#~ pass
#~ def removeLayer(self, layer):
#~ '''Remove specified layer.'''
#~ if not isinstance(layer, basestring):
#~ name = layer.GetName()
#~ else:
#~ name = layer
#~ pass
#~ def move(self, src, dst):
#~ pass
#~ def move(self, itemselection, dst):
#~ #QItemSelection
#~ pass
#~ def moveToTop(self, row):
#~ pass
#~ def moveToTop(self, itemselection):
#~ #QItemSelection
#~ pass
#~ def moveToBottom(self, row):
#~ pass
#~ def moveToBottom(self, itemselection):
#~ #QItemSelection
#~ pass
#~ def _updateZValues(self):
#~ # ??
#~ pass
### OGR feature style #########################################################
'''Style String Syntax
Each feature object has a style property (a string)::
<style_property> = "<style_def>" | "" | "@<style_name>" | "{<field_name>}"
* "<style_def>" is defined later in this section.
* An empty style property means that the feature directly inherits its
style from the layer it is in.
* "@<style_name>" is a reference to a predefined style in the layer or
the dataset's style table. The layer's table is looked up first,
and if style_name is not found there then the dataset's table will be
looked up.
* Finally, "{<field_name>}" means that the style property should be
read from the specified attribute field.
The <style_def> is the real style definition.
It is a combination of 1 or more style parts separated by semicolons.
Each style_part uses a drawing tool to define a portion of the complete
graphical representation::
<style_def> = <style_part>[;<style_part>[;...]]
<style_part> = <tool_name>([<tool_param>[,<tool_param>[,...]]])
<tool_name> = name of a drawing tool, for now: PEN | BRUSH | SYMBOL | LABEL
<tool_param> = <param_name>:<param_value>
<param_name> = see list of parameters names for each drawing tool
<param_value> = <value> | <value><units>
<value> = "<string_value>" | <numeric_value> | {<field_name>}
<units> = g | px | pt | mm | cm | in
.. seealso:: http://www.gdal.org/ogr/ogr_feature_style.html sect 2.2
'''
#gdal-1.8.x/src/autotest/ogr/ogr_dgn.py
#gdal-1.8.x/src/autotest/ogr/ogr_dxf.py
#gdal-1.8.x/src/autotest/ogr/ogr_openir.py
#gdal-1.8.x/src/autotest/ogr/ogr_sqltest.py