QGIS/python/plugins/osm/OsmAddRelationDlg.py
2011-09-15 21:33:42 +03:00

804 lines
35 KiB
Python
Executable File

"""@package OsmAddRelationDlg
The main class of this module (OsmAddRelationDlg) is descendant of "Create OSM Relation" dialog.
The dialog either shows detail info on existing relation or is empty when no relation id is passed to constructor.
In brief this module (and its main class) just provides easy way to create or change OSM relation.
...
"""
from ui_OsmAddRelationDlg import Ui_OsmAddRelationDlg
from map_tools.OsmIdentifyMT import OsmIdentifyMT
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from sip import unwrapinstance
from qgis.core import *
from qgis.gui import *
import sqlite3
import unicodedata
class OsmAddRelationDlg(QDialog, Ui_OsmAddRelationDlg):
"""This class is direct descendant of "Create OSM Relation" dialog. It provides easy way to create
or change OSM relation. Methods of OsmAddRelationDlg class catch signals emitted when changing relations
type, tags or members, submitting or rejecting the whole dialog. After catching signal, methods must
perform appropriate operation using methods of OsmDatabaseManager. The other methods serve to initialize
dialog when displaying info on existing relation."""
def __init__(self, plugin, newRelationFirstMember=None, relationToEdit=None):
"""The constructor.
@param plugin pointer to OSM Plugin object; parent of this object
@param newRelationFirstMember info on feature (in form: "idSPACEtype") which will be first member of new relation
@param relationToEdit if relation is given, this dialog is for editing of existing relation, not for creation a new one
"""
QDialog.__init__(self,None)
self.setupUi(self)
self.dockWidget=plugin.dockWidget
self.plugin=plugin
self.dbm=plugin.dbm
self.ur=plugin.undoredo
self.canvas=plugin.canvas
# set icons for tool buttons (identify,move,createPoint,createLine,createPolygon)
self.chooseMemberButton.setIcon(QIcon(":/plugins/osm_plugin/images/osm_identify.png"))
self.removeMemberButton.setIcon(QIcon(":/plugins/osm_plugin/images/osm_removeMember.png"))
self.removeTagButton.setIcon(QIcon(":/plugins/osm_plugin/images/osm_removeTag.png"))
self.loadStandardTagsButton.setIcon(QIcon(":/plugins/osm_plugin/images/osm_generateTags.png"))
self.typeInfoButton.setIcon(QIcon(":/plugins/osm_plugin/images/osm_questionMark.png"))
self.info = dict()
self.newTagLabel = "<new tag here>"
self.newMemberLabel = "<new member here>"
self.tagInfoTextEdit.setText("<nothing>")
if relationToEdit:
# we are editing existing relation
self.editing = True
self.relId = relationToEdit
self.createRelButton.setText( self.tr("Save") )
self.setWindowTitle( self.tr("Edit OSM relation") )
else:
self.editing = False
# we are adding new relation
if newRelationFirstMember:
ix = newRelationFirstMember.indexOf(" ")
self.firstMemberType = QString(newRelationFirstMember).left(ix)
self.firstMemberId = QString(newRelationFirstMember).right(len(newRelationFirstMember)-ix-1)
self.relTags=[]
self.relTagsEditIndex=-1
self.relMembersRoleEditIndex=-1
self.relMembersTypeEditIndex=-1
self.connectDlgSignals()
# clear all dialog items first
self.clear()
# load default values to combobox determining relation type
self.relationTypes=["boundary","multipolygon","restriction","route","enforcement"]
self.typeCombo.addItem("")
self.typeCombo.addItems(self.relationTypes)
if self.editing:
# we are editing existing relation, we'll load relation data first
self.loadRelationData(self.relId)
else:
if newRelationFirstMember:
self.addRelationMember(self.firstMemberId,self.firstMemberType,None)
# enable related tool buttons
self.removeMemberButton.setEnabled(True)
self.relMembersLoaded = True
self.relMembersTable.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.relTagsTable.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.relMembersTable.setSelectionBehavior(QAbstractItemView.SelectRows)
self.relTagsTable.setSelectionBehavior(QAbstractItemView.SelectRows)
self.typeInfoButton.setEnabled(False)
def connectDlgSignals(self):
"""Function connects important dialog signals to appropriate slots.
"""
# signals on working with tag and member tables
QObject.connect(self.typeCombo, SIGNAL("currentIndexChanged(const QString &)"), self.__onTypeSelectionChanged)
QObject.connect(self.relTagsTable, SIGNAL("itemDoubleClicked(QTableWidgetItem*)"), self.__onTagsItemDoubleClicked)
QObject.connect(self.relMembersTable, SIGNAL("itemDoubleClicked(QTableWidgetItem*)"), self.__onMembersItemDoubleClicked)
QObject.connect(self.relTagsTable, SIGNAL("cellChanged(int,int)"), self.__onTagsCellChanged)
QObject.connect(self.relMembersTable, SIGNAL("cellChanged(int,int)"), self.__onMembersCellChanged)
# signals on buttons clicking
QObject.connect(self.createRelButton, SIGNAL("clicked()"), self.createOrUpdateRelation)
QObject.connect(self.stornoButton, SIGNAL("clicked()"), self.stornoDialog)
QObject.connect(self.typeInfoButton, SIGNAL("clicked()"), self.__showTypeInfo)
QObject.connect(self.loadStandardTagsButton, SIGNAL("clicked()"), self.loadStandardTags)
QObject.connect(self.removeTagButton, SIGNAL("clicked()"), self.removeSelectedRelTags)
QObject.connect(self.removeMemberButton, SIGNAL("clicked()"), self.removeSelectedRelMembers)
QObject.connect(self.chooseMemberButton, SIGNAL("clicked()"), self.__startIdentifyingMember)
def addRelationMember(self,memberId,memberType,memberRole):
"""Function inserts one record into table representing list of all relations' members.
New record is put to the first place of the list.
@param memberId identifier of new relation member
@param memberType type of new relation member
@param memberRole role of new relation member
"""
# insert row for new relation member
self.relMembersTable.insertRow(0)
self.relMembersTable.setItem(0,0,QTableWidgetItem(str(memberId)))
self.relMembersTable.setItem(0,1,QTableWidgetItem(str(memberType)))
self.relMembersTable.setItem(0,2,QTableWidgetItem(str(memberRole)))
self.relMembersTable.item(0,0).setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable)
self.relMembersTable.item(0,1).setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable)
self.relMembersTable.item(0,2).setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable)
def addRelationTag(self,tagKey,tagValue):
"""Function inserts one record into table representing list of all relations' tags.
New record is put to the first place of the list.
@param tagKey key of inserted tag
@param tagValue value of inserted tag
"""
# insert row for new relation tag
self.relTagsTable.insertRow(0)
self.relTagsTable.setItem(0,0,QTableWidgetItem(tagKey))
self.relTagsTable.setItem(0,1,QTableWidgetItem(tagValue))
self.relTagsTable.item(0,0).setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.relTagsTable.item(0,1).setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable)
def loadRelationData(self,relId):
"""Function fills dialog items with data of given relation.
Data of relation means its type, tags and members.
@param relId identifier of relation
"""
# load tags of specified relation
self.relTags=self.dbm.getFeatureTags(relId,'Relation')
rowCount = len(self.relTags)
for i in range(0,rowCount):
key = self.relTags[i][0]
value = self.relTags[i][1]
if key=="type":
# tag with key "type" is not shown in relation tags table, there is special combobox for it instead
ix=self.typeCombo.findText(value)
if ix<>-1:
self.typeCombo.setCurrentIndex(ix)
else:
self.typeCombo.setEditText(value)
else:
# all other tags are displayed in tags table
self.addRelationTag(key,value)
# fill relation members table with relation members data
mems=self.dbm.getRelationMembers(relId)
# printing members
for i in range(0,len(mems)):
self.addRelationMember(mems[i][0],mems[i][1],mems[i][2])
# enable related tool buttons
self.removeMemberButton.setEnabled(True)
# set new flags values
self.relTagsLoaded = True
self.relMembersLoaded = True
def __startIdentifyingMember(self):
"""Function enables maptool for identifying relation members directly on map.
"""
if self.chooseMemberButton.isChecked():
self.mapTool=OsmIdentifyMT(self.canvas, self.dockWidget, self.dbm)
self.canvas.setMapTool(self.mapTool)
self.canvas.setCursor(QCursor(Qt.ArrowCursor))
self.canvas.setFocus(Qt.OtherFocusReason)
else:
self.addRelationMember(self.dockWidget.feature.id(),self.dockWidget.featureType,"")
self.canvas.unsetMapTool(self.mapTool)
del self.mapTool
self.mapTool=None
def removeSelectedRelTags(self):
"""Function removes all selected tags from relation tags table.
"""
# remove selected tags (rows)
selectedItems=self.relTagsTable.selectedItems()
selectedRowsIndexes=[]
lastRowIndex=self.relTagsTable.rowCount()-1
self.relTagsTable.setCurrentCell(lastRowIndex,0)
for i in selectedItems:
if i.column()==0 and not i.row()==lastRowIndex:
selectedRowsIndexes.append(i.row())
selectedRowsIndexes.sort()
selectedRowsIndexes.reverse()
for ix in selectedRowsIndexes:
key=self.relTagsTable.item(ix,0).text()
self.relTagsTable.removeRow(ix)
def removeSelectedRelMembers(self):
"""Function removes all selected members from relation members table.
"""
# remove selected members (rows)
selectedItems=self.relMembersTable.selectedItems()
selectedRowsIndexes=[]
lastRowIndex=self.relMembersTable.rowCount()-1
self.relMembersTable.setCurrentCell(lastRowIndex,0)
for i in selectedItems:
if i.column()==0 and not i.row()==lastRowIndex:
selectedRowsIndexes.append(i.row())
selectedRowsIndexes.sort()
selectedRowsIndexes.reverse()
for ix in selectedRowsIndexes:
key=self.relMembersTable.item(ix,0).text()
self.relMembersTable.removeRow(ix)
def __onTagsCellChanged(self,row,column):
"""Function to handle user changes in cells of relations' tags table.
It's called automatically whenever signal "cellChanged(...)" is emitted.
@param row index of a row that has changed
@param column index of a column that has changed
"""
if not self.relTagsLoaded:
return
if row==self.relTagsTable.rowCount()-1:
# changing value of the last row
key = self.relTagsTable.item(row,0).text()
if key=="" or key==self.newTagLabel:
return
# adding new tag to table
if column==0:
# only ascii keys are allowed
nkfd_form=unicodedata.normalize('NFKD', unicode(key.toUtf8(),'utf-8'))
key_only_ascii=nkfd_form.encode('ASCII', 'ignore')
newLastRow = row+1
self.relTagsTable.setRowCount(row+2)
self.relTagsTable.setItem(newLastRow,0,QTableWidgetItem(self.newTagLabel))
self.relTagsTable.setItem(newLastRow,1,QTableWidgetItem(""))
self.relTagsTable.item(newLastRow,0).setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable)
self.relTagsTable.item(newLastRow,1).setFlags(Qt.ItemIsEnabled)
self.relTagsTable.item(row,0).setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.relTagsTable.item(row,1).setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable)
self.relTagsTable.item(row,0).setText(QString(key_only_ascii))
def __onMembersCellChanged(self,row,column):
"""Function to handle user changes in cells of relations' members table.
it's called automatically whenever signal "cellChanged(...)" is emitted.
@param row index of a row that has changed
@param column index of a column that has changed
"""
if not self.relMembersLoaded:
return
if row==self.relMembersTable.rowCount()-1:
# changing value of the last row
memberId = self.relMembersTable.item(row,0).text()
if memberId=="" or memberId==self.newMemberLabel:
return
# adding new member
if column==0:
newLastRow = row+1
self.relMembersTable.setRowCount(row+2)
self.relMembersTable.setItem(newLastRow,0,QTableWidgetItem(self.newMemberLabel))
self.relMembersTable.setItem(newLastRow,1,QTableWidgetItem(""))
self.relMembersTable.setItem(newLastRow,2,QTableWidgetItem(""))
self.relMembersTable.item(newLastRow,0).setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable)
self.relMembersTable.item(newLastRow,1).setFlags(Qt.ItemIsEnabled)
self.relMembersTable.item(newLastRow,2).setFlags(Qt.ItemIsEnabled)
self.relMembersTable.item(row,0).setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable)
self.relMembersTable.item(row,1).setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable)
self.relMembersTable.item(row,2).setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable)
def clear(self):
"""Function removes content of all dialog items (with one exception) and set them to default values.
The only exception is combobox determining relation type.
It stays untouched.
"""
# clear table of relation properties(tags) and all related buttons
self.relTagsTable.clear()
self.removeTagButton.setEnabled(True)
# clear table of relation members and all related buttons
self.relMembersTable.clear()
self.chooseMemberButton.setEnabled(True)
self.removeMemberButton.setEnabled(False)
# clear information panel
self.tagInfoTextEdit.setText("<nothing>")
self.tagInfoTextEdit.setEnabled(False)
# set loading flags to false
self.relTagsLoaded = True
self.relMembersLoaded = True
# load default data into relation members table
self.relMembersTable.setColumnCount(3)
self.relMembersTable.setHorizontalHeaderItem(0,QTableWidgetItem("Id"))
self.relMembersTable.setHorizontalHeaderItem(1,QTableWidgetItem("Type"))
self.relMembersTable.setHorizontalHeaderItem(2,QTableWidgetItem("Role"))
self.relMembersTable.setRowCount(1)
self.relMembersTable.setItem(0,0,QTableWidgetItem(self.newMemberLabel))
self.relMembersTable.setItem(0,1,QTableWidgetItem(""))
self.relMembersTable.setItem(0,2,QTableWidgetItem(""))
self.relMembersTable.item(0,0).setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable)
self.relMembersTable.item(0,1).setFlags(Qt.ItemIsEnabled)
self.relMembersTable.item(0,2).setFlags(Qt.ItemIsEnabled)
# load default data into relation tags table
self.relTagsTable.setColumnCount(2)
self.relTagsTable.setHorizontalHeaderItem(0,QTableWidgetItem("Key"))
self.relTagsTable.setHorizontalHeaderItem(1,QTableWidgetItem("Value"))
self.relTagsTable.setRowCount(1)
self.relTagsTable.removeCellWidget(0,1)
self.relTagsTable.setItem(0,0,QTableWidgetItem(self.newTagLabel))
self.relTagsTable.setItem(0,1,QTableWidgetItem(""))
self.relTagsTable.item(0,0).setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable)
self.relTagsTable.item(0,1).setFlags(Qt.ItemIsEnabled)
def loadStandardTags(self):
"""Function clears relations' tags table and then loads tags that are typical
for chosen relation type.
This provides good way how to help users with creating standard relations.
User doesn't need to find out what tags are usually used for some relation.
"""
# clear relation tags table first
self.relTagsTable.clear()
self.relTagsTable.setColumnCount(2)
self.relTagsTable.setHorizontalHeaderItem(0,QTableWidgetItem("Key"))
self.relTagsTable.setHorizontalHeaderItem(1,QTableWidgetItem("Value"))
self.relTagsTable.setRowCount(1)
self.relTagsTable.removeCellWidget(0,1)
self.relTagsTable.setItem(0,0,QTableWidgetItem(self.newTagLabel))
self.relTagsTable.setItem(0,1,QTableWidgetItem(""))
self.relTagsTable.item(0,0).setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable)
self.relTagsTable.item(0,1).setFlags(Qt.ItemIsEnabled)
# find tags that are recommended for chosen relation type
self.relTags = self.determineSuitableTags(self.typeCombo.currentText())
# put found tags into relation tags table
rowCount = len(self.relTags)
for i in range(0,rowCount):
self.addRelationTag(list(self.relTags)[i],"")
# set variables and enable buttons for better manipulation with table
self.removeTagButton.setEnabled(True)
self.relTagsLoaded = True
def __onTypeSelectionChanged(self,typeName):
"""Function is called after currentIndexChanged(...) signal is emitted on "RELATION TYPE" combobox.
That means user select new relation type. Either one of predefined types or a new one.
Function doesn't perform change of relation type this time. Everything will be done
after submiting the whole dialog.
@param typeName name of new selected type
"""
# if non-standard typename was set up, loadStandardTagsButton is useless
if typeName.toAscii().data() in self.relationTypes:
self.loadStandardTagsButton.setEnabled(True)
self.typeInfoButton.setEnabled(True)
else:
self.loadStandardTagsButton.setEnabled(False)
self.typeInfoButton.setEnabled(False)
def determineSuitableMemberRoles(self,relType):
"""Function is used to find typical member roles to given relation type.
With help of this function plugin gives advice to user on relation creation.
@param relType name of relation type
@return list of typical roles to given type of relation
"""
roles = []
if relType=="boundary":
roles = ["enclave","exclave"]
elif relType=="multipolygon":
roles = ["outer","inner"]
elif relType=="restriction":
roles = ["from","to","via","location_hint"]
elif relType=="route":
roles = ["forward","backward","stop_0","stop_1","stop_2","stop_3","stop_4","stop_5","stop_6","stop_7","stop_8","stop_9"
,"forward_stop_0","forward_stop_1","forward_stop_2","forward_stop_3","forward_stop_4","forward_stop_5","forward_stop_6"
,"forward_stop_7","forward_stop_8","forward_stop_9","backward_stop_0","backward_stop_1","backward_stop_2"
,"backward_stop_3","backward_stop_4","backward_stop_5","backward_stop_6","backward_stop_7","backward_stop_8","backward_stop_9"]
elif relType=="enforcement":
roles = []
return roles
def determineSuitableTags(self,relType):
"""Function is used to find typical tags to given relation type.
With of this function plugin gives advice to user on relation creation.
@param relType name of relation type
@return list of typical tags to given type of relation
"""
tags = []
if relType=="boundary":
tags = dict(
boundary='For a real boundary (sometimes in the middle of a river or 12 Miles away from coastline).'
,land_area='For coastline and real boundaries on land.'
,name=''
,admin_level=''
)
elif relType=="multipolygon":
tags = dict()
elif relType=="restriction":
tags = {
"restriction":"If the first word is \"no\", then no routing is possible from the \"from\" to the \"to\" member, and if it is \"only_\", then you know that the only routing originating from the \"from\" member leads to the \"to\" member. The \"from\" and \"to\" members must start/end at the \"via\" node (see 1)."
,"except":"The restriction does not apply to these vehicle types (possible more than one: except=bicycle;psv)"
,"day_on":"For example, no right turn in the morning peak on weekdays might be day_on=Monday;day_off=Friday;hour_on=07:30;hour_off=09:30."
,"day_off":""
,"hour_on":""
,"hour_off":""
}
elif relType=="route":
tags = dict(
route='A road (e.g. the ways making up the A14 trunk road), bicycle route, hiking route or whatever route.'
,name='The route is known by this name (e.g. "Jubilee Cycle Route", "Pembrokeshire Coastal Path").'
,ref='The route is known by this reference (e.g. "A14", "NCN 11", "Citi 4" (bus number); in germany there is always a space between character and number, e.g. "A 1", "L 130", "K 5"; in france too).'
,network='A wider network of routes of which this is one example. For example, the UKs national cycle network; the Cambridge Citi bus network; the UKs long distance footpath network. (The "uk_" bit isnt particularly to identify it as belonging to the uk, merely to be a conventional way to separate the namespace.)'
,operator='The route is operated by this authority/company etc. e.g. "Stagecoach Cambridge", "Eurostar".'
,state='Sometimes routes may not be permanent (ie: diversions), or may be in a proposed state (ie: UK NCN routes are sometimes not official routes pending some negotiation or development). Connection is used for routes linking two different routes or linking a route with for example a village centre.'
,symbol='Describes the symbol that is used to mark the way along the route, e.g., "Red cross on white ground" for the "Frankenweg" in Franconia, Germany.'
,color='(optional) Color code noted in hex triplet format. Especially useful for public transport routes. Example: "#008080" for teal color.'
,description='Description tells what is special about this route.'
,distance='(optional) The distance covered by this route, if known. For information of users and automatic evaluation e.g. of completeness. Given including a unit and with a dot for decimals. (e.g. "12.5km")'
)
elif relType=="enforcement":
tags = dict()
return tags
def determineSuitableTagValues(self,relType,tagKey):
"""Function is used to find typical tag values for given relation type and given key.
With help of this function plugin gives advice to user on relation creation.
@param relType name of relation type
@param tagKey key of tag
@return list of typical tag values to given relation type
"""
vals = []
if relType=="boundary":
if tagKey=="boundary":
vals = ["administrative","national_park","political","civil"]
elif tagKey=="land_area":
vals = ["administrative"]
elif tagKey=="admin_level":
vals = ["1","2","3","4","5","6","7","8","9","10","11"]
elif relType=="restriction":
if tagKey=="restriction":
vals = ["no_right_turn","no_left_turn","no_u_turn","no_straight_on","only_right_turn","only_left_turn","only_straight_on"]
elif tagKey=="except":
vals = ["psv","bicycle","hgv","motorcar"]
elif relType=="route":
if tagKey=="route":
vals = ["road","bicycle","foot","hiking","bus","pilgrimage","detour","railway","tram","mtb","roller_skate","running","horse"]
elif tagKey=="network":
vals = ["ncn","rcn","lcn","uk_ldp","lwn","rwn","nwn","e-road"]
elif tagKey=="state":
vals = ["proposed","alternate","temporary","connection"]
return vals
def createRelation(self):
"""Function creates new OSM relation from dialog data.
It performs commit.
"""
# collect relation members data into a list
relMems=[]
for i in range(0,self.relMembersTable.rowCount()-1): # except from the last row
memId = self.relMembersTable.item(i,0).text().toUtf8().data()
memType = self.relMembersTable.item(i,1).text().toUtf8().data()
memRole = self.relMembersTable.item(i,2).text().toUtf8().data()
relMems.append((memId,memType,memRole))
# find out relation type
relType=self.typeCombo.currentText().toUtf8().data()
# call relation creation
self.ur.startAction("Create relation.")
relId=self.dbm.createRelation(relType,relMems)
relTags=[]
for i in range(0,self.relTagsTable.rowCount()-1): # except from the last row
key=self.relTagsTable.item(i,0).text().toUtf8().data()
val=self.relTagsTable.item(i,1).text().toUtf8().data()
relTags.append((key,val))
# relation type has to be stored as tag too
relTags.append(('type',relType))
# insert relation tags
self.dbm.insertTags(relId,'Relation',relTags)
# make actions persistent
self.dbm.commit()
self.ur.stopAction()
def updateRelation(self):
"""Function updates existing OSM relation.
It performs commit.
Relation is not given in parameter; it's in member variable of this class.
"""
self.ur.startAction("Update relation.")
# find out relation type and change it
relType=self.typeCombo.currentText().toUtf8().data()
self.dbm.changeRelationType(self.relId,relType)
# remove all relation tags and members
self.dbm.removeFeaturesTags(self.relId,'Relation')
# collect relation members into a list
relMems=[]
for i in range(0,self.relMembersTable.rowCount()-1): # except from the last row
memId = self.relMembersTable.item(i,0).text().toUtf8().data()
memType = self.relMembersTable.item(i,1).text().toUtf8().data()
memRole = self.relMembersTable.item(i,2).text().toUtf8().data()
relMems.append((memId,memType,memRole))
# remove old members and insert new ones
self.dbm.changeAllRelationMembers(self.relId,relMems)
# collect relation tags into a list
relTags=[]
for i in range(0,self.relTagsTable.rowCount()-1): # except from the last row
key=self.relTagsTable.item(i,0).text().toUtf8().data()
val=self.relTagsTable.item(i,1).text().toUtf8().data()
relTags.append((key,val))
# relation type has to be stored as tag too
relTags.append(('type',relType))
# insert relation tags
self.dbm.insertTags(self.relId,'Relation',relTags)
# make actions persistent
self.ur.stopAction()
self.dbm.commit()
def createOrUpdateRelation(self):
"""Function starts process of creation of new OSM relation from dialog data.
When in editing mode function updates opened relation instead.
"""
if not self.editing:
# lets create new relation from predefined information
self.createRelation()
else:
# lets update existing relation
self.updateRelation()
# close addRelation dialog
self.close()
#load features' relations info into dockWidget again
if self.dockWidget.feature:
self.dockWidget.reloadFeatureRelations()
def stornoDialog(self):
"""Function just cancels the whole dialog.
It is called after clicked() signal is emitted on "Storno" button.
"""
# close addRelation dialog
self.close()
def __onTagsItemDoubleClicked(self,item):
"""Function is called after itemDoubleClicked(...) signal is emitted on table of relation tags.
It shows combobox with possible values for given item of table.
@param item item of table of relation tags
"""
if item.column()==0:
return
if self.relTagsEditIndex<>None:
row=self.relTagsEditIndex
if row<>-1:
value=self.relTagsTable.cellWidget(row,1).currentText()
self.relTagsTable.item(row,1).setText(value)
self.relTagsTable.removeCellWidget(row,1)
tagValues = self.determineSuitableTagValues(self.typeCombo.currentText(),self.relTagsTable.item(item.row(),0).text())
if len(tagValues)>0:
valCombo=QComboBox()
valCombo.setEditable(True)
valCombo.addItem("")
valCombo.addItems(tagValues)
ix=valCombo.findText(self.relTagsTable.item(item.row(),1).text())
valCombo.setCurrentIndex(ix)
self.relTagsTable.setCellWidget(item.row(),1,valCombo)
self.relTagsEditIndex=item.row()
QObject.connect(valCombo, SIGNAL("currentIndexChanged(const QString &)"), self.__onValueSelectionChanged)
def __onMembersItemDoubleClicked(self,item):
"""Function is called after itemDoubleClicked(...) signal is emitted on table of relation members.
It shows combobox with possible values for given item of table.
@param item item of table of relation members
"""
if (item.column()==0) or (item.row()==self.relMembersTable.rowCount()-1):
return
if item.column()==2:
if self.relMembersRoleEditIndex<>None:
row=self.relMembersRoleEditIndex
if row<>-1:
role=self.relMembersTable.cellWidget(row,2).currentText()
self.relMembersTable.item(row,2).setText(role)
self.relMembersTable.removeCellWidget(row,2)
memberRoles = self.determineSuitableMemberRoles(self.typeCombo.currentText())
if len(memberRoles)>0:
rolesCombo=QComboBox()
rolesCombo.setEditable(True)
rolesCombo.addItem("")
rolesCombo.addItems(memberRoles)
ix=rolesCombo.findText(self.relMembersTable.item(item.row(),1).text())
rolesCombo.setCurrentIndex(ix)
self.relMembersTable.setCellWidget(item.row(),2,rolesCombo)
self.relMembersRoleEditIndex=item.row()
QObject.connect(rolesCombo, SIGNAL("currentIndexChanged(const QString &)"), self.__onRoleSelectionChanged)
elif item.column()==1:
if self.relMembersTypeEditIndex<>None:
row=self.relMembersTypeEditIndex
if row<>-1:
memType=self.relMembersTable.cellWidget(row,1).currentText()
self.relMembersTable.item(row,1).setText(memType)
self.relMembersTable.removeCellWidget(row,1)
memberTypes=["Point","Line","Polygon","Relation"]
memTypesCombo=QComboBox()
memTypesCombo.setEditable(True)
memTypesCombo.addItem("")
memTypesCombo.addItems(memberTypes)
ix=memTypesCombo.findText(self.relMembersTable.item(item.row(),1).text())
memTypesCombo.setCurrentIndex(ix)
self.relMembersTable.setCellWidget(item.row(),1,memTypesCombo)
self.relMembersTypeEditIndex=item.row()
QObject.connect(memTypesCombo, SIGNAL("currentIndexChanged(const QString &)"), self.__onMemTypeSelectionChanged)
def __onValueSelectionChanged(self,value):
"""Function is called after currentIndexChanged(...) signal is emitted on combobox of table item.
This combobox is related to table of relation tags (column Value).
@param value new current value in combobox
"""
row=self.relTagsEditIndex
self.relTagsTable.item(row,1).setText(value)
self.relTagsTable.removeCellWidget(row,1)
self.relTagsEditIndex=-1
def __onRoleSelectionChanged(self,role):
"""Function is called after currentIndexChanged(...) signal is emitted on combobox of table item.
This combobox is related to table of relation members (column Role).
@param role new current value in combobox
"""
row=self.relMembersRoleEditIndex
self.relMembersTable.item(row,2).setText(role)
self.relMembersTable.removeCellWidget(row,2)
self.relMembersRoleEditIndex=-1
def __onMemTypeSelectionChanged(self,memType):
"""Function is called after currentIndexChanged(...) signal is emitted on combobox of table item.
This combobox is related to table of relation members (column Type).
@param memType new current value in combobox
"""
row=self.relMembersTypeEditIndex
self.relMembersTable.item(row,1).setText(memType)
self.relMembersTable.removeCellWidget(row,1)
self.relMembersTypeEditIndex=-1
def __showTypeInfo(self):
"""Function shows messagebox with brief information on currently selected relation type.
"""
typeName = self.typeCombo.currentText()
info = ""
if typeName=="boundary":
info = QCoreApplication.translate( "OsmAddRelationDlg", "for grouping boundaries and marking enclaves / exclaves" )
elif typeName=="multipolygon":
info = QCoreApplication.translate( "OsmAddRelationDlg", "to put holes into areas (might have to be renamed, see article)" )
elif typeName=="restriction":
info = QCoreApplication.translate( "OsmAddRelationDlg", "any kind of turn restriction" )
elif typeName=="route":
info = QCoreApplication.translate( "OsmAddRelationDlg", "like bus routes, cycle routes and numbered highways" )
elif typeName=="enforcement":
info = QCoreApplication.translate( "OsmAddRelationDlg", "traffic enforcement devices; speed cameras, redlight cameras, weight checks, ..." )
QMessageBox.information(self, self.tr("OSM Information"), info)