mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
1549 lines
58 KiB
Python
Executable File
1549 lines
58 KiB
Python
Executable File
# -*- coding: utf-8 -*-
|
|
"""@package OsmUploadDlg
|
|
Module provides simple way of uploading current OSM data.
|
|
|
|
When performing edit operations all changed features are marked as 'U'=Updated.
|
|
All new features are marked as 'A'=Added and all removed features are marked as 'R'=Removed.
|
|
Uploader checks features statuses first.
|
|
|
|
Then it creates HTTP connection. Upload is done in correct order
|
|
so that data on OSM server always stay consistent.
|
|
|
|
Upload phases and their exact order:
|
|
- phases: 0.changeset creation, 1.nodes creation, 2.ways deletion, 3.ways update,
|
|
4.ways addition, 5.nodes deletion, 6.nodes update, 7.relation creation,
|
|
8.relation deletion, 9.relation update, 10.changeset closing
|
|
"""
|
|
|
|
|
|
from ui_OsmUploadDlg import Ui_OsmUploadDlg
|
|
import OsmPlugin
|
|
|
|
from PyQt4.QtCore import *
|
|
from PyQt4.QtGui import *
|
|
from PyQt4.QtNetwork import *
|
|
from sys import *
|
|
|
|
|
|
class OsmUploadDlg(QDialog, Ui_OsmUploadDlg):
|
|
"""Class provides simple way of uploading current OSM data.
|
|
|
|
When performing edit operations all changed features are marked as 'U'=Updated.
|
|
All new features are marked as 'A'=Added and all removed features are marked as 'R'=Removed.
|
|
|
|
Uploader checks features statuses first.
|
|
Then it creates HTTP connection. Upload is done in correct order
|
|
so that data on OSM server always stay consistent.
|
|
|
|
Upload phases and their exact order:
|
|
- phases: 0.changeset creation, 1.nodes creation, 2.ways deletion, 3.ways update,
|
|
4.ways addition, 5.nodes deletion, 6.nodes update, 7.relation creation,
|
|
8.relation deletion, 9.relation update, 10.changeset closing
|
|
"""
|
|
|
|
def __init__(self,plugin):
|
|
"""The constructor.
|
|
|
|
@param plugin is pointer to instance of OSM Plugin.
|
|
"""
|
|
|
|
QDialog.__init__(self,None)
|
|
self.setupUi(self)
|
|
|
|
self.dockWidget=plugin.dockWidget
|
|
self.plugin=plugin
|
|
self.dbm=plugin.dbm
|
|
self.ur=plugin.undoredo
|
|
|
|
self.urlHost = "api.openstreetmap.org"
|
|
self.uploadButton = self.buttonBox.addButton(self.tr("Upload"), QDialogButtonBox.ActionRole)
|
|
|
|
self.uploadChangesTable.setColumnCount(5)
|
|
self.uploadChangesTable.setColumnWidth(0,80)
|
|
self.uploadChangesTable.setColumnWidth(1,60)
|
|
self.uploadChangesTable.setColumnWidth(2,60)
|
|
self.uploadChangesTable.setColumnWidth(3,60)
|
|
self.uploadChangesTable.setColumnWidth(4,60)
|
|
|
|
self.uploadChangesTable.setHeaderLabels(["","Points","Lines","Polygons","Relations"])
|
|
key=QString(self.dbm.currentKey)
|
|
p1=key.lastIndexOf(".")
|
|
p2=key.lastIndexOf("/")
|
|
key=key.mid(p2+1,p1-p2-1)
|
|
self.groupBox.setTitle(QString("Changes in ").append(key))
|
|
|
|
item = QTreeWidgetItem(["Added","0","0","0","0"])
|
|
for i in range(1,5):
|
|
item.setTextAlignment(i,Qt.AlignHCenter)
|
|
self.uploadChangesTable.addTopLevelItem(item)
|
|
|
|
item = QTreeWidgetItem(["Removed","0","0","0","0"])
|
|
for i in range(1,5):
|
|
item.setTextAlignment(i,Qt.AlignHCenter)
|
|
self.uploadChangesTable.addTopLevelItem(item)
|
|
|
|
item = QTreeWidgetItem(["Changed","0","0","0","0"])
|
|
for i in range(1,5):
|
|
item.setTextAlignment(i,Qt.AlignHCenter)
|
|
self.uploadChangesTable.addTopLevelItem(item)
|
|
|
|
self.userLineEdit.setFocus()
|
|
self.passwdLineEdit.setEchoMode(QLineEdit.Password)
|
|
self.uploadButton.setEnabled(False)
|
|
self.pseudoId_map={}
|
|
self.featureId_map={}
|
|
self.qhttp_map={}
|
|
self.progressDialog=QProgressDialog(self)
|
|
self.progressDialog.setModal(True)
|
|
self.finished=False
|
|
self.httpRequestAborted=False
|
|
self.savePasswd=False
|
|
self.changesetId=None
|
|
|
|
settings=QSettings()
|
|
if settings.contains("/OSM_Plugin/uploadUser"):
|
|
uplUser=settings.value("/OSM_Plugin/uploadUser",QVariant(QString())).toString()
|
|
self.userLineEdit.setText(uplUser)
|
|
if settings.contains("/OSM_Plugin/uploadPasswd"):
|
|
uplPasswd=settings.value("/OSM_Plugin/uploadPasswd",QVariant(QString())).toString()
|
|
self.passwdLineEdit.setText(uplPasswd)
|
|
self.chkSavePasswd.setChecked(True)
|
|
|
|
self.commentTextEdit.setFocus()
|
|
|
|
# phases: 0.changeset creation, 1.nodes creation, 2.ways deletion, 3.ways update,
|
|
# 4.ways addition, 5.nodes deletion, 6.nodes update, 7.relation creation,
|
|
# 8.relation deletion, 9.relation update, 10.changeset closing
|
|
self.phase=-1
|
|
self.cntPhases=11
|
|
self.cntActionsInPhase=[0]*self.cntPhases
|
|
self.cntActionsInPhaseDone=0
|
|
self.cntActionsDone=0
|
|
|
|
self.createStatistics()
|
|
if self.cntActionsAll<=2:
|
|
# no action to upload (except for changeset opening and closing)
|
|
self.commentTextEdit.setEnabled(False)
|
|
self.accountGroupBox.setEnabled(False)
|
|
|
|
self.showStatistics()
|
|
self.__prepareDatabaseQueries()
|
|
|
|
# connect signals
|
|
self.connect(self.uploadButton, SIGNAL("clicked()"), self.uploadChanges)
|
|
self.connect(self.userLineEdit, SIGNAL("textChanged(const QString &)"), self.enableUploadButton)
|
|
self.connect(self.passwdLineEdit, SIGNAL("textChanged(const QString &)"), self.enableUploadButton)
|
|
self.connect(self.chkShowPasswd, SIGNAL("clicked()"), self.__showPassword)
|
|
self.connect(self.chkSavePasswd, SIGNAL("clicked()"), self.__savePassword)
|
|
self.connect(self.progressDialog, SIGNAL("canceled()"), self.__cancelProgressDlg)
|
|
|
|
self.enableUploadButton()
|
|
|
|
# http connection
|
|
self.http=QHttp(self)
|
|
|
|
self.connect(self.http,SIGNAL("responseHeaderReceived(QHttpResponseHeader)"), self.__readResponseHeader)
|
|
self.connect(self.http,SIGNAL("authenticationRequired(const QString &, quint16, QAuthenticator *)"), self.__authRequired)
|
|
|
|
self.__setProxy()
|
|
self.reqSetHost=self.http.setHost(self.urlHost, 80)
|
|
self.httpRequestAborted = False
|
|
self.httpRequestCanceled = False
|
|
self.httpRequestZombie = False
|
|
self.responseHeader=""
|
|
|
|
# increase maximum recursion depth in python (__uploadStep is recursive function)
|
|
setrecursionlimit(1000000)
|
|
|
|
|
|
def uploadChanges(self):
|
|
"""Main function; starts the whole upload process.
|
|
If checkbox for password saving was checked, password is stored to Quantum GIS settings.
|
|
"""
|
|
|
|
self.progressDialog.setWindowTitle(self.tr("OSM Upload"))
|
|
self.progressDialog.setLabelText(self.tr("Uploading data..."))
|
|
self.uploadButton.setEnabled(False)
|
|
|
|
self.cursor=self.dbm.getConnection().cursor()
|
|
|
|
settings=QSettings()
|
|
settings.setValue("/OSM_Plugin/uploadUser",QVariant(self.userLineEdit.text()))
|
|
if self.savePasswd:
|
|
settings.setValue("/OSM_Plugin/uploadPasswd",QVariant(self.passwdLineEdit.text()))
|
|
else:
|
|
settings.remove("/OSM_Plugin/uploadPasswd")
|
|
|
|
self.reqSetUser=self.http.setUser(self.userLineEdit.text(),self.passwdLineEdit.text())
|
|
|
|
# start upload with its first step (the next steps will follow automatically)
|
|
self.__uploadStep()
|
|
|
|
|
|
def __uploadStep(self):
|
|
"""Function calls the next step of uploading.
|
|
"""
|
|
|
|
# first update progressbar value
|
|
self.progressDialog.setMaximum(self.cntActionsAll)
|
|
self.progressDialog.setValue(self.cntActionsDone)
|
|
|
|
# check if (in actual upload phase) number of done actions reach all actions that should be done;
|
|
# if it does, switch actual upload phase to the next one in the order
|
|
if (self.phase==-1) or (self.cntActionsInPhaseDone==self.cntActionsInPhase[self.phase]):
|
|
self.dbm.commit() # commit actions performed in finished phase
|
|
|
|
# search for first next phase in which some upload actions should be done
|
|
self.phase+=1
|
|
while self.phase<self.cntPhases and self.cntActionsInPhase[self.phase]==0:
|
|
self.phase+=1
|
|
|
|
# is there another phase to run?
|
|
if self.phase>=self.cntPhases:
|
|
self.cursor.close() # all upload phases were finished!
|
|
self.__finishUpload()
|
|
return
|
|
|
|
# yes, it is ;) tell its number
|
|
# print QString("Starting upload phase no.%1.").arg(self.phase)
|
|
|
|
# if necessary set up database cursor
|
|
if not self.phase in (0,10):
|
|
self.cursor.execute(self.selectQuery[self.phase])
|
|
|
|
# no action has been done yet in running phase
|
|
self.cntActionsInPhaseDone = 0
|
|
|
|
# just print common info
|
|
# print QString("Running step %1 of upload phase %2.").arg(self.cntActionsInPhaseDone+1).arg(self.phase)
|
|
|
|
# perform next action in running phase
|
|
if self.phase not in (0,10):
|
|
record = self.cursor.fetchone()
|
|
if record == None:
|
|
# strange situation, number of features ready to upload in actual phase was actually lower than it was signalized
|
|
# by self.cntActionsAll; well, ignore this step and run the next one, that will start the next upload phase!
|
|
self.cntActionsInPhaseDone = self.cntActionsInPhase[self.phase]
|
|
self.__uploadStep()
|
|
|
|
if self.phase == 0: # compulsory changeset creation
|
|
self.__createChangeset()
|
|
elif self.phase == 1: # nodes creation
|
|
self.__uploadNodeAddition(record)
|
|
elif self.phase == 2: # ways deletion
|
|
self.__uploadWayDeletion(record)
|
|
elif self.phase == 3: # ways update
|
|
self.__uploadWayUpdate(record)
|
|
elif self.phase == 4: # ways creation
|
|
self.__uploadWayAddition(record)
|
|
elif self.phase == 5: # nodes deletion
|
|
self.__uploadNodeDeletion(record)
|
|
elif self.phase == 6: # nodes update
|
|
self.__uploadNodeUpdate(record)
|
|
elif self.phase == 7: # relations creation
|
|
self.__uploadRelationAddition(record)
|
|
elif self.phase == 8: # relations deletion
|
|
self.__uploadRelationDeletion(record)
|
|
elif self.phase == 9: # relations update
|
|
self.__uploadRelationUpdate(record)
|
|
elif self.phase == 10: # compulsory changeset closing
|
|
self.__closeChangeset()
|
|
else:
|
|
print "Program shouldn't reach this place! (2)"
|
|
return
|
|
|
|
|
|
def __showPassword(self):
|
|
"""Function to show hidden password on dialog box,
|
|
so that user can verify if he has written it right.
|
|
"""
|
|
|
|
if self.chkShowPasswd.isChecked():
|
|
self.passwdLineEdit.setEchoMode(QLineEdit.Normal)
|
|
else:
|
|
self.passwdLineEdit.setEchoMode(QLineEdit.Password)
|
|
|
|
|
|
def __savePassword(self):
|
|
"""Function to show hidden password on dialog box,
|
|
so that user can verify if he has written it right.
|
|
"""
|
|
|
|
if self.chkSavePasswd.isChecked():
|
|
self.savePasswd=True
|
|
else:
|
|
self.savePasswd=False
|
|
|
|
|
|
def createStatistics(self):
|
|
"""Function finds out number of steps for all upload phases.
|
|
These statistics are found out with examining features' statuses.
|
|
Results are stored in uploaders' member variables.
|
|
"""
|
|
|
|
c=self.dbm.getConnection().cursor()
|
|
self.cntActionsAll = 0
|
|
|
|
# phases: 0.changeset creation, 1.nodes creation, 2.ways deletion, 3.ways update,
|
|
# 4.ways addition, 5.nodes deletion, 6.nodes update, 7.relation creation,
|
|
# 8.relation deletion, 9.relation update, 10.changeset closing
|
|
|
|
c.execute("select count(*) from node where status='A' and u=1")
|
|
for row in c:
|
|
self.cntNodesCreated = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
c.execute("select count(*) from way where status='A' and closed=0 and u=1")
|
|
for row in c:
|
|
self.cntLinesCreated = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
c.execute("select count(*) from way where status='A' and closed=1 and u=1")
|
|
for row in c:
|
|
self.cntPolygonsCreated = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
c.execute("select count(*) from node where status='R' and u=1")
|
|
for row in c:
|
|
self.cntNodesRemoved = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
c.execute("select count(*) from way where status='R' and closed=0 and u=1")
|
|
for row in c:
|
|
self.cntLinesRemoved = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
c.execute("select count(*) from way where status='R' and closed=1 and u=1")
|
|
for row in c:
|
|
self.cntPolygonsRemoved = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
c.execute("select count(*) from node where status='U' and u=1")
|
|
for row in c:
|
|
self.cntNodesUpdated = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
c.execute("select count(*) from way where status='U' and closed=0 and u=1")
|
|
for row in c:
|
|
self.cntLinesUpdated = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
c.execute("select count(*) from way where status='U' and closed=1 and u=1")
|
|
for row in c:
|
|
self.cntPolygonsUpdated = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
c.execute("select count(*) from relation where status='A' and u=1")
|
|
for row in c:
|
|
self.cntRelationsCreated = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
c.execute("select count(*) from relation where status='R' and u=1")
|
|
for row in c:
|
|
self.cntRelationsRemoved = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
c.execute("select count(*) from relation where status='U' and u=1")
|
|
for row in c:
|
|
self.cntRelationsUpdated = row[0]
|
|
self.cntActionsAll += row[0]
|
|
|
|
self.cntActionsInPhase[0] = 1
|
|
self.cntActionsInPhase[1] = self.cntNodesCreated
|
|
self.cntActionsInPhase[2] = self.cntLinesRemoved+self.cntPolygonsRemoved
|
|
self.cntActionsInPhase[3] = self.cntLinesUpdated+self.cntPolygonsUpdated
|
|
self.cntActionsInPhase[4] = self.cntLinesCreated+self.cntPolygonsCreated
|
|
self.cntActionsInPhase[5] = self.cntNodesRemoved
|
|
self.cntActionsInPhase[6] = self.cntNodesUpdated
|
|
self.cntActionsInPhase[7] = self.cntRelationsCreated
|
|
self.cntActionsInPhase[8] = self.cntRelationsRemoved
|
|
self.cntActionsInPhase[9] = self.cntRelationsUpdated
|
|
self.cntActionsInPhase[10] = 1
|
|
self.cntActionsAll += 2
|
|
c.close()
|
|
|
|
|
|
def zeroStatistics(self):
|
|
"""Function cancels all statistics of uploader.
|
|
"""
|
|
|
|
for i in range(1,self.cntPhases-1):
|
|
self.cntActionsInPhase[i] = 0
|
|
self.cntActionsAll = 2
|
|
|
|
self.cntNodesCreated = 0
|
|
self.cntLinesRemoved = 0
|
|
self.cntPolygonsRemoved = 0
|
|
self.cntLinesCreated = 0
|
|
self.cntPolygonsCreated = 0
|
|
self.cntNodesRemoved = 0
|
|
self.cntNodesUpdated = 0
|
|
self.cntLinesUpdated = 0
|
|
self.cntPolygonsUpdated = 0
|
|
self.cntRelationsCreated = 0
|
|
self.cntRelationsRemoved = 0
|
|
self.cntRelationsUpdated = 0
|
|
|
|
|
|
def showStatistics(self):
|
|
"""Function shows (statistics) counts of typical actions that are ready for upload.
|
|
|
|
Showing them is realized on OSM upload dialog with appropriate table of statistics.
|
|
"""
|
|
|
|
# phases: 0.changeset creation, 1.nodes creation, 2.ways deletion, 3.ways addition,
|
|
# 4.nodes deletion, 5.nodes update, 6.ways update, 7.relations addition, 8.relations deletion,
|
|
# 9.relations update, 10.changeset closing
|
|
|
|
self.uploadChangesTable.topLevelItem(0).setText(1,str(self.cntNodesCreated))
|
|
self.uploadChangesTable.topLevelItem(1).setText(1,str(self.cntNodesRemoved))
|
|
self.uploadChangesTable.topLevelItem(2).setText(1,str(self.cntNodesUpdated))
|
|
|
|
self.uploadChangesTable.topLevelItem(0).setText(2,str(self.cntLinesCreated))
|
|
self.uploadChangesTable.topLevelItem(1).setText(2,str(self.cntLinesRemoved))
|
|
self.uploadChangesTable.topLevelItem(2).setText(2,str(self.cntLinesUpdated))
|
|
|
|
self.uploadChangesTable.topLevelItem(0).setText(3,str(self.cntPolygonsCreated))
|
|
self.uploadChangesTable.topLevelItem(1).setText(3,str(self.cntPolygonsRemoved))
|
|
self.uploadChangesTable.topLevelItem(2).setText(3,str(self.cntPolygonsUpdated))
|
|
|
|
self.uploadChangesTable.topLevelItem(0).setText(4,str(self.cntRelationsCreated))
|
|
self.uploadChangesTable.topLevelItem(1).setText(4,str(self.cntRelationsRemoved))
|
|
self.uploadChangesTable.topLevelItem(2).setText(4,str(self.cntRelationsUpdated))
|
|
|
|
|
|
def __prepareDatabaseQueries(self):
|
|
"""Function prepares SQL queries for selecting objects to upload.
|
|
Resulting array of queries is stored in member variable.
|
|
"""
|
|
|
|
self.selectQuery = [
|
|
# changeset creation
|
|
""
|
|
# nodes creation
|
|
,"select n.id, n.lat, n.lon, n.user, n.timestamp, (select version_id \
|
|
from version where object_id=n.id and object_type='node') version_id from node n where n.status='A' and n.u=1"
|
|
# ways deletion
|
|
,"select w.id, w.user, w.timestamp, (select version_id from version where \
|
|
object_id=w.id and object_type='way') version_id, w.closed from way w where w.status='R' and w.u=1"
|
|
# ways update
|
|
,"select w.id, w.user, w.timestamp, (select version_id from version \
|
|
where object_id=w.id and object_type='way') version_id, w.closed from way w where w.status='U' and w.u=1"
|
|
# ways creation
|
|
,"select w.id, w.user, w.timestamp, (select version_id from version \
|
|
where object_id=w.id and object_type='way') version_id, w.closed from way w where w.status='A' and w.u=1"
|
|
# nodes deletion
|
|
,"select n.id, n.lat, n.lon, n.user, n.timestamp, (select version_id \
|
|
from version where object_id=n.id and object_type='node') version_id from node n where n.status='R' and n.u=1"
|
|
# nodes update
|
|
,"select n.id, n.lat, n.lon, n.user, n.timestamp, (select version_id \
|
|
from version where object_id=n.id and object_type='node') version_id from node n where n.status='U' and n.u=1"
|
|
|
|
# relations creation
|
|
,"select r.id, r.user, r.timestamp, (select version_id \
|
|
from version where object_id=r.id and object_type='relation') version_id from relation r where r.status='A' and r.u=1"
|
|
# relations deletion
|
|
,"select r.id, r.user, r.timestamp, (select version_id \
|
|
from version where object_id=r.id and object_type='relation') version_id from relation r where r.status='R' and r.u=1"
|
|
# relations update
|
|
,"select r.id, r.user, r.timestamp, (select version_id \
|
|
from version where object_id=r.id and object_type='relation') version_id from relation r where r.status='U' and r.u=1"
|
|
# changeset closing
|
|
,""
|
|
]
|
|
|
|
|
|
def __uploadNodeAddition(self, nodeRecord):
|
|
"""Sends upload request for addition of new node.
|
|
|
|
@param nodeRecord tuple object with information on node
|
|
"""
|
|
|
|
# create http connection with necessary authentication
|
|
urlPath = "/api/0.6/node/create"
|
|
|
|
# set http request's header
|
|
header = QHttpRequestHeader("PUT", urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
header.setContentType("text/xml; charset=utf-8")
|
|
|
|
# create http request's body (create XML with info about uploaded node)
|
|
requestXml = self.createNodeXml(nodeRecord)
|
|
|
|
# connect http response signals to appropriate functions
|
|
self.connect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpNodeAdditionReqFinished)
|
|
|
|
# send prepared request
|
|
requestBytes = requestXml.toAscii()
|
|
httpSessionId = self.http.request(header, requestBytes)
|
|
|
|
# remember http connection object and pseudoId, that was used
|
|
# to node's identification in sqlite3 database
|
|
self.qhttp_map[httpSessionId]=1
|
|
self.pseudoId_map[httpSessionId]=nodeRecord[0]
|
|
|
|
|
|
def __uploadNodeUpdate(self, nodeRecord):
|
|
"""Sends upload request for updating one node.
|
|
|
|
@param nodeRecord tuple object with information on node
|
|
"""
|
|
|
|
urlPath = QString("/api/0.6/node/%1").arg(nodeRecord[0])
|
|
|
|
# set http request's header
|
|
header = QHttpRequestHeader("PUT", urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
header.setContentType("text/xml; charset=utf-8")
|
|
|
|
# create http request's body (create XML with info about uploaded node)
|
|
requestXml = self.createNodeXml(nodeRecord)
|
|
|
|
# connect http response signals to appropriate functions
|
|
self.connect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpNodeUpdateReqFinished)
|
|
|
|
# send prepared request
|
|
requestBytes=requestXml.toAscii()
|
|
httpSessionId=self.http.request(header, requestBytes)
|
|
|
|
# remember http connection object
|
|
self.qhttp_map[httpSessionId]=1
|
|
self.featureId_map[httpSessionId]=nodeRecord[0]
|
|
|
|
|
|
def __uploadNodeDeletion(self, nodeRecord):
|
|
"""Send upload request for deletion of one node.
|
|
|
|
@param nodeRecord tuple object with information on node
|
|
"""
|
|
|
|
# create http connection with necessary authentication
|
|
urlPath = QString("/api/0.6/node/%1").arg(nodeRecord[0])
|
|
|
|
# set http request's header
|
|
header = QHttpRequestHeader("DELETE", urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
header.setContentType("text/xml; charset=utf-8")
|
|
|
|
# connect http response signals to appropriate functions
|
|
self.connect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpNodeDeletionReqFinished)
|
|
|
|
requestXml=self.createNodeDelXml(nodeRecord)
|
|
|
|
# send prepared request
|
|
requestBytes = requestXml.toAscii()
|
|
httpSessionId = self.http.request(header, requestBytes)
|
|
|
|
# remember http connection object
|
|
self.qhttp_map[httpSessionId]=1
|
|
self.featureId_map[httpSessionId]=nodeRecord[0]
|
|
|
|
|
|
def createNodeXml(self, nodeRecord):
|
|
"""Function creates XML that will be added as http body to node addition request.
|
|
|
|
@param nodeRecord tuple object with information on node
|
|
"""
|
|
|
|
requestXml = QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
|
|
version = nodeRecord[5] # version_id
|
|
requestXml.append(QString("<osm version=\"0.6\" generator=\"OpenStreetMap server\">"))
|
|
requestXml.append(QString("<node id=\"%1\" lat=\"%2\" lon=\"%3\" visible=\"true\" user=\"%4\" timestamp=\"%5\"")
|
|
.arg(nodeRecord[0]) # node_id
|
|
.arg(nodeRecord[1],0,'f',50) # lat
|
|
.arg(nodeRecord[2],0,'f',50) # lon
|
|
.arg(str(nodeRecord[3])) # user
|
|
.arg(str(nodeRecord[4])) # timestamp
|
|
)
|
|
if version<>None:
|
|
requestXml.append(QString(" version=\"%1\"").arg(str(version)))
|
|
requestXml.append(QString(" changeset=\"%1\">")
|
|
.arg(str(self.changesetId))
|
|
)
|
|
|
|
# selecting tags to construct correct XML
|
|
ct=self.dbm.getConnection().cursor()
|
|
ct.execute("select key, val from tag where object_id=:nodeId and object_type=\"node\" and u=1",{"nodeId":nodeRecord[0]})
|
|
for tagRecord in ct:
|
|
requestXml.append(QString("<tag k=\"%1\" v=\"%2\"/>").arg(tagRecord[0]).arg(tagRecord[1]))
|
|
ct.close()
|
|
|
|
requestXml.append(QString("</node></osm>"))
|
|
return requestXml
|
|
|
|
|
|
def createNodeDelXml(self, nodeRecord):
|
|
"""Function creates XML that will be added as http body to node deletion request.
|
|
|
|
@param nodeRecord tuple object with information on node
|
|
"""
|
|
|
|
requestXml = QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
|
|
requestXml.append(QString("<osm version=\"0.6\" generator=\"OpenStreetMap server\">"))
|
|
requestXml.append(QString("<node id=\"%1\" lat=\"%2\" lon=\"%3\" visible=\"true\" user=\"%4\" timestamp=\"%5\"")
|
|
.arg(nodeRecord[0])
|
|
.arg(nodeRecord[1],0,'f',50)
|
|
.arg(nodeRecord[2],0,'f',50)
|
|
.arg(str(nodeRecord[3]))
|
|
.arg(str(nodeRecord[4]))
|
|
)
|
|
requestXml.append(QString(" version=\"%1\"").arg(nodeRecord[5]))
|
|
requestXml.append(QString(" changeset=\"%1\"></node>").arg(str(self.changesetId)))
|
|
requestXml.append(QString("</osm>"))
|
|
return requestXml
|
|
|
|
|
|
def createWayXml(self, wayRecord):
|
|
"""Creates XML that have to be added as http body to way addition request.
|
|
|
|
@param wayRecord tuple object with information on way
|
|
"""
|
|
|
|
pseudoWayId = wayRecord[0]
|
|
requestXml = QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
|
|
|
|
version = wayRecord[3] # version_id
|
|
closed = wayRecord[4]
|
|
requestXml.append(QString("<osm version=\"0.6\" generator=\"OpenStreetMap server\">"))
|
|
requestXml.append(QString("<way id=\"%1\" visible=\"true\" timestamp=\"%3\" user=\"%2\"")
|
|
.arg(wayRecord[0]) # way_id
|
|
.arg(str(wayRecord[1])) # user
|
|
.arg(str(wayRecord[2])) # timestamp
|
|
)
|
|
|
|
if version<>None:
|
|
requestXml.append(QString(" version=\"%1\"").arg(str(version)))
|
|
requestXml.append(QString(" changeset=\"%1\">")
|
|
.arg(str(self.changesetId))
|
|
)
|
|
|
|
# selecting way members to construct correct XML
|
|
firstMember = None
|
|
cwm=self.dbm.getConnection().cursor()
|
|
cwm.execute("select node_id from way_member where way_id=:pseudoWayId and u=1",{"pseudoWayId": pseudoWayId})
|
|
for wayMemberRecord in cwm:
|
|
if firstMember==None:
|
|
firstMember=wayMemberRecord[0]
|
|
requestXml.append(QString("<nd ref=\"%1\"/>").arg(wayMemberRecord[0]))
|
|
cwm.close()
|
|
|
|
if closed==1:
|
|
tmp=None
|
|
|
|
if firstMember:
|
|
requestXml.append(QString("<nd ref=\"%1\"/>").arg(firstMember))
|
|
|
|
# selecting tags to construct correct XML
|
|
ct=self.dbm.getConnection().cursor()
|
|
ct.execute("select key, val from tag where object_id=:pseudoWayId and u=1",{"pseudoWayId": pseudoWayId})
|
|
for tagRecord in ct:
|
|
tmp=None
|
|
requestXml.append(QString("<tag k=\"%1\" v=\"%2\"/>").arg(tagRecord[0]).arg(tagRecord[1]))
|
|
ct.close()
|
|
|
|
# finish XML construction
|
|
requestXml.append(QString("</way></osm>"))
|
|
return requestXml
|
|
|
|
|
|
def __uploadWayAddition(self, wayRecord):
|
|
"""Sends upload request for addition of new way.
|
|
|
|
@param wayRecord tuple object with information on way
|
|
"""
|
|
|
|
pseudoWayId = wayRecord[0]
|
|
|
|
# create http connection with necessary authentication
|
|
urlPath = "/api/0.6/way/create"
|
|
|
|
# set http request's header
|
|
header = QHttpRequestHeader("PUT", urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
header.setContentType("text/xml; charset=utf-8")
|
|
|
|
# connect http response signals to appropriate functions
|
|
self.connect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpWayAdditionReqFinished)
|
|
|
|
# create http request's body (create XML with info about uploaded way)
|
|
requestXml = self.createWayXml(wayRecord)
|
|
|
|
# send prepared request
|
|
requestBytes = requestXml.toAscii()
|
|
httpSessionId = self.http.request(header, requestBytes)
|
|
|
|
# remember pseudoId, that was used to nodes identification in sqlite3 database
|
|
self.qhttp_map[httpSessionId]=1
|
|
self.pseudoId_map[httpSessionId]=pseudoWayId
|
|
|
|
|
|
def __uploadWayUpdate(self, wayRecord):
|
|
"""Send upload request for updating given way.
|
|
|
|
@param wayRecord tuple object with information on way
|
|
"""
|
|
|
|
# create http connection with necessary authentication
|
|
urlPath = QString("/api/0.6/way/%1").arg(wayRecord[0])
|
|
|
|
# set http request's header
|
|
header=QHttpRequestHeader("PUT", urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
header.setContentType("text/xml; charset=utf-8")
|
|
|
|
# connect http response signals to appropriate functions
|
|
self.connect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpWayUpdateReqFinished)
|
|
|
|
# create http request's body (create XML with info about uploaded way)
|
|
requestXml = self.createWayXml(wayRecord)
|
|
|
|
# send prepared request
|
|
requestBytes = requestXml.toAscii()
|
|
httpSessionId = self.http.request(header, requestBytes)
|
|
|
|
self.qhttp_map[httpSessionId]=1
|
|
self.featureId_map[httpSessionId]=wayRecord[0]
|
|
|
|
|
|
def __uploadWayDeletion(self, wayRecord):
|
|
"""Send upload request for removing given way.
|
|
|
|
@param wayRecord tuple object with information on way
|
|
"""
|
|
|
|
# create http connection with necessary authentication
|
|
urlPath = QString("/api/0.6/way/%1").arg(wayRecord[0])
|
|
|
|
# set http request's header
|
|
header = QHttpRequestHeader("DELETE", urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
header.setContentType("text/xml; charset=utf-8")
|
|
|
|
# connect http response signals to appropriate functions
|
|
self.connect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpWayDeletionReqFinished)
|
|
|
|
# create http request's body (create XML with info about uploaded way deletion)
|
|
requestXml = self.createWayXml(wayRecord)
|
|
|
|
# send prepared request
|
|
requestBytes = requestXml.toAscii()
|
|
httpSessionId = self.http.request(header, requestBytes)
|
|
|
|
# remember http connection object
|
|
self.qhttp_map[httpSessionId]=1
|
|
self.featureId_map[httpSessionId]=wayRecord[0]
|
|
|
|
|
|
def createRelationXml(self, relRecord):
|
|
"""Creates XML that will be added to relation addition/deletion/update http request.
|
|
|
|
@param relRecord tuple object with information on relation
|
|
"""
|
|
|
|
pseudoRelId = relRecord[0]
|
|
requestXml = QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
|
|
|
|
version = relRecord[3] # version_id
|
|
requestXml.append(QString("<osm version=\"0.6\" generator=\"OpenStreetMap server\">"))
|
|
requestXml.append(QString("<relation id=\"%1\" visible=\"true\" timestamp=\"%3\" user=\"%2\"")
|
|
.arg(relRecord[0]) # way_id
|
|
.arg(str(relRecord[1])) # user
|
|
.arg(str(relRecord[2])) # timestamp
|
|
)
|
|
|
|
if version<>None:
|
|
requestXml.append(QString(" version=\"%1\"").arg(str(version)))
|
|
requestXml.append(QString(" changeset=\"%1\">").arg(str(self.changesetId)))
|
|
|
|
# selecting way members to construct correct XML
|
|
firstMember = None
|
|
c=self.dbm.getConnection().cursor()
|
|
c.execute("select member_type, role, member_id from relation_member where relation_id=:pseudoRelId and u=1",{"pseudoRelId": pseudoRelId})
|
|
for relMemberRecord in c:
|
|
requestXml.append(QString("<member type=\"%1\"").arg(relMemberRecord[0]))
|
|
if relMemberRecord[1]<>None:
|
|
requestXml.append(QString(" role=\"%1\"").arg(relMemberRecord[1]))
|
|
requestXml.append(QString(" ref=\"%1\"/>").arg(relMemberRecord[2]))
|
|
c.close()
|
|
|
|
# selecting tags to construct correct XML
|
|
ct=self.dbm.getConnection().cursor()
|
|
ct.execute("select key, val from tag where object_id=:pseudoRelId and object_type='relation' and u=1",{"pseudoRelId": pseudoRelId})
|
|
for tagRecord in ct:
|
|
requestXml.append(QString("<tag k=\"%1\" v=\"%2\"/>").arg(tagRecord[0]).arg(tagRecord[1]))
|
|
ct.close()
|
|
|
|
# finish XML construction
|
|
requestXml.append(QString("</relation></osm>"))
|
|
return requestXml
|
|
|
|
|
|
def __uploadRelationAddition(self, relRecord):
|
|
"""Sends upload request for addition of new relation.
|
|
|
|
@param relRecord tuple object with information on relation
|
|
"""
|
|
|
|
pseudoRelId = relRecord[0]
|
|
|
|
# create http connection with necessary authentication
|
|
urlPath = "/api/0.6/relation/create"
|
|
|
|
# set http request's header
|
|
header = QHttpRequestHeader("PUT", urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
header.setContentType("text/xml; charset=utf-8")
|
|
|
|
# connect http response signals to appropriate functions
|
|
self.connect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpRelationAdditionReqFinished)
|
|
|
|
# create http request's body (create XML with info about uploaded way)
|
|
requestXml = self.createRelationXml(relRecord)
|
|
|
|
# send prepared request
|
|
requestBytes = requestXml.toAscii()
|
|
httpSessionId = self.http.request(header, requestBytes)
|
|
|
|
# remember pseudoId, that was used to nodes identification in sqlite3 database
|
|
self.qhttp_map[httpSessionId]=1
|
|
self.pseudoId_map[httpSessionId]=pseudoRelId
|
|
|
|
|
|
def __uploadRelationUpdate(self,relRecord):
|
|
"""Sends upload request for updating given relation.
|
|
|
|
@param relRecord tuple object with information on relation
|
|
"""
|
|
|
|
# create http connection with necessary authentication
|
|
urlPath = QString("/api/0.6/relation/%1").arg(relRecord[0])
|
|
|
|
# set http request's header
|
|
header=QHttpRequestHeader("PUT", urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
header.setContentType("text/xml; charset=utf-8")
|
|
|
|
# connect http response signals to appropriate functions
|
|
self.connect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpRelationUpdateReqFinished)
|
|
|
|
# create http request's body (create XML with info about uploaded way)
|
|
requestXml = self.createRelationXml(relRecord)
|
|
|
|
# send prepared request
|
|
requestBytes = requestXml.toAscii()
|
|
httpSessionId = self.http.request(header, requestBytes)
|
|
|
|
self.qhttp_map[httpSessionId]=1
|
|
self.featureId_map[httpSessionId]=relRecord[0]
|
|
|
|
|
|
def __uploadRelationDeletion(self, relRecord):
|
|
"""Function sends upload request for removing given relation.
|
|
|
|
@param relRecord tuple object with information on relation
|
|
"""
|
|
|
|
|
|
# create http connection with necessary authentication
|
|
urlPath = QString("/api/0.6/relation/%1").arg(relRecord[0])
|
|
|
|
# set http request's header
|
|
header=QHttpRequestHeader("DELETE", urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
header.setContentType("text/xml; charset=utf-8")
|
|
|
|
# connect http response signals to appropriate functions
|
|
self.connect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpRelationDeletionReqFinished)
|
|
|
|
# create http request's body (create XML with info about uploaded way deletion)
|
|
requestXml = self.createRelationXml(relRecord)
|
|
|
|
# send prepared request
|
|
requestBytes=requestXml.toAscii()
|
|
httpSessionId=self.http.request(header, requestBytes)
|
|
|
|
# remember http connection object
|
|
self.qhttp_map[httpSessionId]=1
|
|
self.featureId_map[httpSessionId]=relRecord[0]
|
|
|
|
|
|
def escape_html(self,text):
|
|
"""Function replaces chars that are problematic for html,
|
|
such as < > & " ' with < > & " '
|
|
|
|
@param text text to replace problematic characters in
|
|
@return transformed text
|
|
"""
|
|
|
|
text=text.replace(QString("&"), QString("&")); # must be first
|
|
text=text.replace(QString("<"), QString("<"));
|
|
text=text.replace(QString(">"), QString(">"));
|
|
text=text.replace(QString("\""), QString("""));
|
|
text=text.replace(QString("'"), QString("'"));
|
|
return text
|
|
|
|
|
|
def __createChangeset(self):
|
|
"""Function sends request for creating new OSM changeset.
|
|
|
|
Changeset creation has to be first upload action.
|
|
Changeset closing has to be the last one.
|
|
"""
|
|
|
|
# create http connection with necessary authentication
|
|
urlPath="/api/0.6/changeset/create"
|
|
|
|
# set http request's header
|
|
header=QHttpRequestHeader("PUT",urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
header.setContentType("text/xml; charset=utf-8")
|
|
|
|
# connect http response signals to appropriate functions
|
|
self.connect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpChangesetCreationReqFinished)
|
|
|
|
# get user comment on upload actions
|
|
userComment=self.commentTextEdit.toPlainText()
|
|
userComment=self.escape_html(userComment)
|
|
userCommentBytes=userComment.toUtf8()
|
|
|
|
# create http request's body (create XML with info about uploaded way)
|
|
requestXml=QString("<osm>\n<changeset>\n<tag k=\"created_by\" v=\"QGIS OSM v"+OsmPlugin.versionNumber()+"\"/>\n<tag k=\"comment\" v=\""+userCommentBytes.data()+"\"/>\n</changeset>\n</osm>")
|
|
|
|
# send prepared request
|
|
requestBytes=requestXml.toAscii()
|
|
httpSessionId=self.http.request(header, requestBytes)
|
|
|
|
# remember http connection object
|
|
self.qhttp_map[httpSessionId]=1
|
|
|
|
|
|
def __closeChangeset(self):
|
|
"""Function sends request for closing opened OSM changeset.
|
|
|
|
Changeset creation has to be first upload action.
|
|
Changeset closing has to be the last one.
|
|
"""
|
|
|
|
# create http connection with necessary authentication
|
|
urlPath = QString("/api/0.6/changeset/%1/close").arg(self.changesetId)
|
|
|
|
# set http request's header
|
|
header = QHttpRequestHeader("PUT",urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
header.setContentLength(0)
|
|
|
|
# connect http response signals to appropriate functions
|
|
self.connect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpChangesetClosingReqFinished)
|
|
self.changesetId=None
|
|
|
|
# send prepared request
|
|
httpSessionId=self.http.request(header)
|
|
|
|
# remember http connection object
|
|
self.qhttp_map[httpSessionId]=1
|
|
|
|
|
|
def enableUploadButton(self):
|
|
"""Function finds out if it is possible to enable upload button.
|
|
If yes, function just enables it, else it disables it.
|
|
|
|
Following condition has to be satisfied to enable upload button:
|
|
-There has to be something to upload.
|
|
-Both user login and password has to be filled.
|
|
"""
|
|
|
|
user=self.userLineEdit.text()
|
|
passwd=self.passwdLineEdit.text()
|
|
|
|
if not self.finished and user<>"" and passwd<>"" and self.cntActionsAll>2:
|
|
self.uploadButton.setEnabled(True)
|
|
else:
|
|
self.uploadButton.setEnabled(False)
|
|
|
|
|
|
def __finishUpload(self):
|
|
"""This is function that runs after all edit actions changes are uploaded to OSM server.
|
|
|
|
It clears edit changes tables, hide progress bar, clear statistics,
|
|
clear undo/redo data, etc.
|
|
"""
|
|
|
|
self.finished = True
|
|
self.progressDialog.hide()
|
|
|
|
# delete database records with status='R' (Remove)
|
|
self.dbm.removeUselessRecords()
|
|
|
|
# show info that there are no more actions to upload
|
|
self.zeroStatistics()
|
|
self.showStatistics()
|
|
|
|
# disable dialog items
|
|
self.userLineEdit.setEnabled(False)
|
|
self.passwdLineEdit.setEnabled(False)
|
|
self.chkShowPasswd.setEnabled(False)
|
|
|
|
self.accept()
|
|
|
|
# clear undo/redo
|
|
self.ur.clear()
|
|
|
|
|
|
def cancelUpload(self,errMessage=None):
|
|
"""Function aborts the whole upload operation.
|
|
In errMessage function gets the reason of upload canceling (error message).
|
|
|
|
If OSM changeset was already opened while uploading, HTTP request to close it is send immediately.
|
|
The HTTP connection of uploader is aborted, progress dialog is closed and the whole upload dialog is rejected.
|
|
|
|
If there is some errMessage parameter, the message is shown to user.
|
|
|
|
@param errMessage message to tell user after canceling upload
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if self.changesetId:
|
|
|
|
urlPath=QString("/api/0.6/changeset/%1/close").arg(self.changesetId)
|
|
header=QHttpRequestHeader("PUT",urlPath,1,1)
|
|
header.setValue("Host", self.urlHost)
|
|
header.setValue("Connection", "keep-alive")
|
|
httpSessionId=self.http.request(header)
|
|
|
|
self.httpRequestAborted=True
|
|
self.http.abort()
|
|
|
|
self.uploadButton.setEnabled(True)
|
|
self.disconnect(self.progressDialog, SIGNAL("canceled()"), self.__cancelProgressDlg)
|
|
self.progressDialog.close()
|
|
self.reject()
|
|
|
|
if errMessage and errMessage<>"":
|
|
QMessageBox.information(self,self.tr("OSM Upload"),errMessage)
|
|
|
|
|
|
def __cancelProgressDlg(self):
|
|
"""This functions catches the signal emitted when clicking on Cancel button of progress dialog.
|
|
It aborts opened HTTP connection and closes upload dialog.
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
self.httpRequestAborted=True
|
|
self.http.abort()
|
|
|
|
self.uploadButton.setEnabled(True)
|
|
self.reject()
|
|
|
|
|
|
##########################################################
|
|
# !!!!! HTTP RESPONSES FROM OPENSTREETMAP SERVER !!!!! #
|
|
##########################################################
|
|
|
|
|
|
def __httpNodeAdditionReqFinished(self, requestId, error):
|
|
"""Function is called when requestFinished(...) signal is emitted on global HTTP connection object
|
|
and "node adition" is current uploader's phase.
|
|
|
|
OSM server returns id that was assigned to uploaded node.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if not requestId in self.qhttp_map:
|
|
self.__examineResponse(requestId,error)
|
|
return
|
|
|
|
self.disconnect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpNodeAdditionReqFinished)
|
|
|
|
nodePseudoId=self.pseudoId_map[requestId]
|
|
del self.qhttp_map[requestId]
|
|
del self.pseudoId_map[requestId]
|
|
|
|
if error:
|
|
self.cancelUpload( self.tr("Node addition failed.") )
|
|
return
|
|
|
|
newNodeIdStr=QString(self.http.readAll().data())
|
|
newNodeId=(newNodeIdStr.toInt())[0]
|
|
|
|
# update node's identification in sqlite3 database
|
|
self.dbm.updateNodeId(nodePseudoId,newNodeId,self.userLineEdit.text())
|
|
self.dbm.recacheAffectedNow([(nodePseudoId,'Point'),(newNodeId,'Point')])
|
|
|
|
# call the next upload step
|
|
self.cntActionsInPhaseDone+=1
|
|
self.cntActionsDone+=1
|
|
self.__uploadStep()
|
|
|
|
|
|
def __httpNodeUpdateReqFinished(self, requestId, error):
|
|
"""Function is called when requestFinished(...) signal is emitted on global HTTP connection object
|
|
and "node updating" is current uploader's phase.
|
|
|
|
OSM server returns id of new node's version.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if not requestId in self.qhttp_map:
|
|
self.__examineResponse(requestId,error)
|
|
return
|
|
|
|
self.disconnect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpNodeUpdateReqFinished)
|
|
|
|
pointId=self.featureId_map[requestId]
|
|
del self.qhttp_map[requestId]
|
|
del self.featureId_map[requestId]
|
|
|
|
if error:
|
|
self.cancelUpload(self.tr("Node update failed."))
|
|
return
|
|
|
|
newVersionIdStr=QString(self.http.readAll().data())
|
|
newVersionId=(newVersionIdStr.toInt())[0]
|
|
|
|
self.dbm.updateVersionId(pointId,'node',newVersionId)
|
|
self.dbm.updateUser(pointId,'node',self.userLineEdit.text())
|
|
self.dbm.changePointStatus(pointId,'U','N')
|
|
self.dbm.recacheAffectedNow([(pointId,'Point')])
|
|
|
|
# call the next upload step
|
|
self.cntActionsInPhaseDone+=1
|
|
self.cntActionsDone+=1
|
|
self.__uploadStep()
|
|
|
|
|
|
def __httpNodeDeletionReqFinished(self, requestId, error):
|
|
"""Function is called when requestFinished(...) signal is emitted on global HTTP connection object
|
|
and "node deletion" is current uploader's phase.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if not requestId in self.qhttp_map:
|
|
self.__examineResponse(requestId,error)
|
|
return
|
|
|
|
self.disconnect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpNodeDeletionReqFinished)
|
|
|
|
pointId=self.featureId_map[requestId]
|
|
del self.qhttp_map[requestId]
|
|
del self.featureId_map[requestId]
|
|
|
|
if error:
|
|
self.cancelUpload(self.tr("Node deletion failed."))
|
|
return
|
|
|
|
self.dbm.removePointRecord(pointId)
|
|
self.dbm.recacheAffectedNow([(pointId,'Point')])
|
|
|
|
# call the next upload step
|
|
self.cntActionsInPhaseDone+=1
|
|
self.cntActionsDone+=1
|
|
self.__uploadStep()
|
|
|
|
|
|
def __httpWayAdditionReqFinished(self, requestId, error):
|
|
"""Function is called when requestFinished(...) signal is emitted on global HTTP connection object
|
|
and "way addition" is current uploader's phase.
|
|
|
|
OSM server returns id that was assigned to uploaded way.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if not requestId in self.qhttp_map:
|
|
self.__examineResponse(requestId,error)
|
|
return
|
|
|
|
self.disconnect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpWayAdditionReqFinished)
|
|
|
|
wayPseudoId = self.pseudoId_map[requestId]
|
|
del self.qhttp_map[requestId]
|
|
del self.pseudoId_map[requestId]
|
|
|
|
if error:
|
|
self.cancelUpload(self.tr("Way addition failed."))
|
|
return
|
|
|
|
newWayIdStr = QString(self.http.readAll().data())
|
|
newWayId = (newWayIdStr.toInt())[0]
|
|
|
|
# update way identification in sqlite3 database
|
|
self.dbm.updateWayId(wayPseudoId,newWayId,self.userLineEdit.text())
|
|
self.dbm.recacheAffectedNow([(wayPseudoId,'Line'),(newWayId,'Line'),(wayPseudoId,'Polygon'),(newWayId,'Polygon')])
|
|
|
|
# call the next upload step
|
|
self.cntActionsInPhaseDone+=1
|
|
self.cntActionsDone+=1
|
|
self.__uploadStep()
|
|
|
|
|
|
def __httpWayUpdateReqFinished(self, requestId, error):
|
|
"""Function is called when requestFinished(...) signal is emitted on global HTTP connection object
|
|
and "way updating" is current uploader's phase.
|
|
|
|
OSM server returns id of new way's version.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if not requestId in self.qhttp_map:
|
|
self.__examineResponse(requestId,error)
|
|
return
|
|
|
|
self.disconnect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpWayUpdateReqFinished)
|
|
|
|
wayId=self.featureId_map[requestId]
|
|
del self.qhttp_map[requestId]
|
|
del self.featureId_map[requestId]
|
|
|
|
if error:
|
|
self.cancelUpload(self.tr("Way update failed."))
|
|
return
|
|
|
|
newVersionIdStr=QString(self.http.readAll().data())
|
|
newVersionId=(newVersionIdStr.toInt())[0]
|
|
|
|
self.dbm.updateVersionId(wayId,'way',newVersionId)
|
|
self.dbm.updateUser(wayId,'way',self.userLineEdit.text())
|
|
self.dbm.changeWayStatus(wayId,'U','N')
|
|
self.dbm.recacheAffectedNow([(wayId,'Line'),(wayId,'Polygon')])
|
|
|
|
# call the next upload step
|
|
self.cntActionsInPhaseDone+=1
|
|
self.cntActionsDone+=1
|
|
self.__uploadStep()
|
|
|
|
|
|
def __httpWayDeletionReqFinished(self, requestId, error):
|
|
"""Function is called when requestFinished(...) signal is emitted on global HTTP connection object
|
|
and "way deletion" is current uploader's phase.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if not requestId in self.qhttp_map:
|
|
self.__examineResponse(requestId,error)
|
|
return
|
|
|
|
self.disconnect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpWayDeletionReqFinished)
|
|
|
|
wayId=self.featureId_map[requestId]
|
|
del self.qhttp_map[requestId]
|
|
del self.featureId_map[requestId]
|
|
|
|
if error:
|
|
self.cancelUpload(self.tr("Way deletion failed."))
|
|
return
|
|
|
|
self.dbm.removeWayRecord(wayId)
|
|
self.dbm.recacheAffectedNow([(wayId,'Line'),(wayId,'Polygon')])
|
|
|
|
# call the next upload step
|
|
self.cntActionsInPhaseDone+=1
|
|
self.cntActionsDone+=1
|
|
self.__uploadStep()
|
|
|
|
|
|
def __httpRelationAdditionReqFinished(self, requestId, error):
|
|
"""Function is called when requestFinished(...) signal is emitted on global HTTP connection object
|
|
and "relation addition" is current uploader's phase.
|
|
|
|
OSM server returns id that was assigned to uploaded relation.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if not requestId in self.qhttp_map:
|
|
self.__examineResponse(requestId,error)
|
|
return
|
|
|
|
self.disconnect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpRelationAdditionReqFinished)
|
|
|
|
relPseudoId = self.pseudoId_map[requestId]
|
|
del self.qhttp_map[requestId]
|
|
del self.pseudoId_map[requestId]
|
|
|
|
if error:
|
|
self.cancelUpload(self.tr("Relation addition failed."))
|
|
return
|
|
|
|
newRelIdStr=QString(self.http.readAll().data())
|
|
newRelId=(newRelIdStr.toInt())[0]
|
|
|
|
# update way identification in sqlite3 database
|
|
self.dbm.updateRelationId(relPseudoId,newRelId,self.userLineEdit.text())
|
|
|
|
# call the next upload step
|
|
self.cntActionsInPhaseDone+=1
|
|
self.cntActionsDone+=1
|
|
self.__uploadStep()
|
|
|
|
|
|
def __httpRelationUpdateReqFinished(self, requestId, error):
|
|
"""Function is called when requestFinished(...) signal is emitted on global HTTP connection object
|
|
and "relation updating" is current uploader's phase.
|
|
|
|
OSM server returns id of new relation's version.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if not requestId in self.qhttp_map:
|
|
self.__examineResponse(requestId,error)
|
|
return
|
|
|
|
self.disconnect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpRelationUpdateReqFinished)
|
|
|
|
relId=self.featureId_map[requestId]
|
|
del self.qhttp_map[requestId]
|
|
del self.featureId_map[requestId]
|
|
|
|
if error:
|
|
self.cancelUpload(self.tr("Relation update failed."))
|
|
return
|
|
|
|
newVersionIdStr=QString(self.http.readAll().data())
|
|
newVersionId=(newVersionIdStr.toInt())[0]
|
|
|
|
self.dbm.updateVersionId(relId,'relation',newVersionId)
|
|
self.dbm.updateUser(relId,'relation',self.userLineEdit.text())
|
|
self.dbm.changeRelationStatus(relId,'U','N')
|
|
|
|
# call the next upload step
|
|
self.cntActionsInPhaseDone+=1
|
|
self.cntActionsDone+=1
|
|
self.__uploadStep()
|
|
|
|
|
|
def __httpRelationDeletionReqFinished(self, requestId, error):
|
|
"""Function is called when requestFinished(...) signal is emitted on global HTTP connection object
|
|
and "relation deletion" is current uploader's phase.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if not requestId in self.qhttp_map:
|
|
self.__examineResponse(requestId,error)
|
|
return
|
|
|
|
self.disconnect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpRelationDeletionReqFinished)
|
|
|
|
relId=self.featureId_map[requestId]
|
|
del self.qhttp_map[requestId]
|
|
del self.featureId_map[requestId]
|
|
|
|
if error:
|
|
self.cancelUpload(self.tr("Relation deletion failed."))
|
|
return
|
|
|
|
self.dbm.removeRelationRecord(relId)
|
|
|
|
# call the next upload step
|
|
self.cntActionsInPhaseDone+=1
|
|
self.cntActionsDone+=1
|
|
self.__uploadStep()
|
|
|
|
|
|
def __httpChangesetCreationReqFinished(self, requestId, error):
|
|
"""Function is called when requestFinished(...) signal is emitted on global HTTP connection object
|
|
and "changeset creation" is current uploader's phase.
|
|
|
|
OSM server returns id that was assigned to created changeset.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if not requestId in self.qhttp_map:
|
|
self.__examineResponse(requestId,error)
|
|
return
|
|
|
|
self.disconnect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpChangesetCreationReqFinished)
|
|
|
|
if error:
|
|
self.cancelUpload(self.tr("Connection to OpenStreetMap server cannot be established. Please check your proxy settings, firewall settings and try again."))
|
|
return
|
|
|
|
del self.qhttp_map[requestId]
|
|
changesetIdStr=QString(self.http.readAll().data())
|
|
self.changesetId=(changesetIdStr.toInt())[0]
|
|
|
|
# call the next upload step
|
|
self.cntActionsInPhaseDone+=1
|
|
self.cntActionsDone+=1
|
|
self.__uploadStep()
|
|
|
|
|
|
def __httpChangesetClosingReqFinished(self, requestId, error):
|
|
"""Function is called when requestFinished(...) signal is emitted on global HTTP connection object
|
|
and "changeset closing" is current uploader's phase.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted:
|
|
return
|
|
|
|
if not requestId in self.qhttp_map:
|
|
self.__examineResponse(requestId,error)
|
|
return
|
|
|
|
self.disconnect(self.http, SIGNAL("requestFinished(int, bool)"), self.__httpChangesetClosingReqFinished)
|
|
|
|
if error:
|
|
self.cancelUpload(self.tr("Changeset closing failed."))
|
|
return
|
|
|
|
# call the next upload step
|
|
self.cntActionsInPhaseDone+=1
|
|
self.cntActionsDone+=1
|
|
self.__uploadStep()
|
|
|
|
|
|
def __readResponseHeader(self, responseHeader):
|
|
"""Function is called when responseHeaderReceived(...) signal is emitted
|
|
on global HTTP connection object.
|
|
|
|
If statusCode of responseHeader doesn't equal to 200, function cancels the whole connection.
|
|
|
|
@param responseHeader header of HTTP response from the OSM server
|
|
"""
|
|
|
|
if responseHeader.statusCode() != 200:
|
|
|
|
self.cancelUpload(self.tr("Upload process failed. OpenStreetMap server response: %1 - %2.")
|
|
.arg(responseHeader.reasonPhrase())
|
|
.arg(responseHeader.value("Error")))
|
|
|
|
|
|
def __authRequired(self,s,a,b):
|
|
"""Function is called when authenticationRequired(...) signal is emitted
|
|
on global HTTP connection object.
|
|
|
|
We are really not interested in function parameters - we just cancel the connection.
|
|
"""
|
|
|
|
self.cancelUpload(self.tr("Authentication failed. Please try again with correct login and password."))
|
|
|
|
|
|
def __setProxy(self):
|
|
"""Function sets proxy to HTTP connection of uploader.
|
|
|
|
HTTP connection object is not given in function parameter,
|
|
because it's global - accessible for the whole uploader.
|
|
"""
|
|
|
|
# getting and setting proxy information
|
|
settings=QSettings()
|
|
proxyHost=QString()
|
|
proxyUser=QString()
|
|
proxyPassword=QString()
|
|
proxyPort=0
|
|
proxyType=QNetworkProxy.NoProxy
|
|
proxyEnabled=settings.value("proxy/proxyEnabled",QVariant(0)).toBool()
|
|
|
|
if proxyEnabled:
|
|
|
|
proxyHost=settings.value("proxy/proxyHost",QVariant("")).toString()
|
|
proxyPort=settings.value("proxy/proxyPort",QVariant(8080)).toInt()[0]
|
|
proxyUser=settings.value("proxy/proxyUser",QVariant("")).toString()
|
|
proxyPassword=settings.value("proxy/proxyPassword",QVariant("")).toString()
|
|
proxyTypeString=settings.value("proxy/proxyType",QVariant("")).toString()
|
|
|
|
if proxyTypeString=="DefaultProxy":
|
|
proxyType=QNetworkProxy.DefaultProxy
|
|
elif proxyTypeString=="Socks5Proxy":
|
|
proxyType=QNetworkProxy.Socks5Proxy
|
|
elif proxyTypeString=="HttpProxy":
|
|
proxyType=QNetworkProxy.HttpProxy
|
|
elif proxyTypeString=="HttpCachingProxy":
|
|
proxyType=QNetworkProxy.HttpCachingProxy
|
|
elif proxyTypeString=="FtpCachingProxy":
|
|
proxyType=QNetworkProxy.FtpCachingProxy
|
|
|
|
self.proxy=QNetworkProxy()
|
|
self.proxy.setType(proxyType)
|
|
self.proxy.setHostName(proxyHost)
|
|
self.proxy.setPort(proxyPort)
|
|
self.reqSetProxy=self.http.__setProxy(self.proxy)
|
|
|
|
|
|
def __examineResponse(self,requestId,error):
|
|
"""If error occured function examines requestId and cancel upload for appropriate reason.
|
|
|
|
@param requestId identifier of http request
|
|
@param error True if error occured on given request; False otherwise
|
|
"""
|
|
|
|
if self.httpRequestAborted or not error:
|
|
return
|
|
|
|
if requestId==self.reqSetHost:
|
|
self.cancelUpload(self.tr("Setting host failed."))
|
|
|
|
elif requestId==self.reqSetUser:
|
|
self.cancelUpload(self.tr("Setting user and password failed."))
|
|
|
|
elif requestId==self.reqSetProxy:
|
|
self.cancelUpload(self.tr("Setting proxy failed."))
|
|
|
|
|
|
|