# -*- coding: utf-8 -*- """ *************************************************************************** OsmSaveDlg.py --------------------- Date : August 2009 Copyright : (C) 2009 by Martin Dobias Email : wonder dot sk at gmail dot com *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * *************************************************************************** """ __author__ = 'Martin Dobias' __date__ = 'August 2009' __copyright__ = '(C) 2009, Martin Dobias' # This will get replaced with a git SHA1 when you do a git archive __revision__ = '$Format:%H$' # -*- coding: utf-8 -*- """@package OsmSaveDlg This module is used to save OSM data into XML file. Of course, user is asked where to save the current data first. """ from ui_OsmSaveDlg import Ui_OsmSaveDlg from PyQt4.QtCore import * from PyQt4.QtGui import * from PyQt4.QtXml import * from sip import unwrapinstance from qgis.core import QgsVectorLayer, QgsMapLayerRegistry import sqlite3 class OsmSaveDlg(QDialog, Ui_OsmSaveDlg): """This class provides all structures and methods necessary for current OSM data saving. Saving is done to XML file. After XML file selection and confirming the dialog, process is started. """ def __init__(self, plugin): """The constructor. @param plugin is pointer to instance of OSM Plugin """ QDialog.__init__(self, plugin.iface.mainWindow()) self.setupUi(self) self.plugin=plugin self.ur=plugin.undoredo self.dbm=plugin.dbm self.progressDialog = QProgressDialog(self) self.progressDialog.setModal(True) self.progressDialog.setAutoClose(False) # variables for identifiers of all objects that will be saved to output file self.nodeIds=set() # connecting dialog and progressbar signals QObject.connect(self.browseOSMButton,SIGNAL("clicked()"),self.showSaveFileDialog) QObject.connect(self.buttonBox,SIGNAL("accepted()"),self.onOK) QObject.connect(self.progressDialog, SIGNAL("canceled()"), self.cancelSaving) def cancelSaving(self): """Function stops the whole OSM Saving process. Destination file is closed and removed. """ # writing into output file was canceled, file must be enclosed self.outFile.close() # end removed self.outFile.remove() self.outFile=None # close the whole Save OSM dialog self.close() def showSaveFileDialog(self): """Function opens dialog for selecting XML file. Only files with extension .osm can be selected. Default directory for file selection is remembered/reload. """ settings = QSettings() lastDir = settings.value("/OSM_Plugin/lastDir", QVariant(QString())).toString() # display file open dialog and get absolute path to selected file fileSelected = QFileDialog.getSaveFileName(self, self.tr("Choose an Open Street Map file"),lastDir,self.tr("OSM Files (*.osm)") ); # insert OSM file path into line edit control if not fileSelected.isNull(): self.OSMFileEdit.setText(fileSelected) # remember directory fi = QFileInfo(fileSelected) settings.setValue("/OSM_Plugin/lastDir", QVariant(fi.path()) ) def onOK(self): """Function is called after clicking on OK button of OSM Save dialog. It performs all actions necessary for OSM data saving. """ # prepare data self.fname=self.OSMFileEdit.text() self.outFile=QFile(self.fname) if not self.outFile.open(QIODevice.WriteOnly): QMessageBox.information(self,self.tr("Save OSM to file"),self.tr("Unable to save the file %1: %2.") .arg(self.fname).arg(self.outFile.errorString())) self.outFile=None return points=self.chkPoints.isChecked() lines=self.chkLines.isChecked() polys=self.chkPolygons.isChecked() rels=self.chkRelations.isChecked() tags=self.chkTags.isChecked() self.xml=QXmlStreamWriter(self.outFile) self.xml.setCodec(QTextCodec.codecForName("utf-8")) self.xml.setAutoFormatting(True) c=self.dbm.getConnection().cursor() cntPoints=cntLines=cntPolys=cntRels=0 c.execute("select count(*) from node") for rec in c: cntPoints=rec[0] c.execute("select count(*) from way where closed=0") for rec in c: cntLines=rec[0] c.execute("select count(*) from way where closed=1") for rec in c: cntPolys=rec[0] c.execute("select count(*) from relation") for rec in c: cntRels=rec[0] self.xml.writeStartDocument() self.xml.writeStartElement("osm") self.xml.writeAttribute("version","0.6") self.xml.writeAttribute("generator","OpenStreetMap server") self.progressDialog.setWindowTitle(self.tr("Save OSM to file")) self.progressDialog.setLabelText(self.tr("Initializing...")) self.progressDialog.setMaximum(1) self.progressDialog.setValue(0) self.progressDialog.show() # element dataExtent=self.plugin.canvas.extent() self.xml.writeEmptyElement("bounds") self.xml.writeAttribute("minlat",str(dataExtent.yMinimum())) self.xml.writeAttribute("minlon",str(dataExtent.xMinimum())) self.xml.writeAttribute("maxlat",str(dataExtent.yMaximum())) self.xml.writeAttribute("maxlon",str(dataExtent.xMaximum())) # todo: uid and changeset attributes are not compulsory! support for them in future! if points: self.progressDialog.setLabelText(self.tr("Saving nodes...")) self.progressDialog.setMaximum(cntPoints) self.progressDialog.setValue(0) i=0 c.execute("select n.id,n.lat,n.lon,v.version_id,n.user,n.timestamp from node n, version v \ where n.status<>'R' and n.u=1 and v.object_id=n.id and v.object_type='node' \ and n.lat>=:minLat AND n.lat<=:maxLat AND n.lon>=:minLon AND n.lon<=:maxLon" ,{"minLat":dataExtent.yMinimum(),"maxLat":dataExtent.yMaximum() ,"minLon":dataExtent.xMinimum(),"maxLon":dataExtent.xMaximum()}) for (nid,lat,lon,ver,usr,tms) in c: anyTags=False tagList=[] self.nodeIds.add(nid) if tags: tagList=self.dbm.getFeatureTags(nid,'Point') if len(tagList)>0: anyTags=True if anyTags: self.xml.writeStartElement("node") else: self.xml.writeEmptyElement("node") self.xml.writeAttribute("id",str(nid)) self.xml.writeAttribute("lat",str(lat)) self.xml.writeAttribute("lon",str(lon)) self.xml.writeAttribute("version",str(ver)) if usr<>"": self.xml.writeAttribute("user",usr) self.xml.writeAttribute("visible","true") self.xml.writeAttribute("timestamp",tms) if anyTags: for r in tagList: self.xml.writeEmptyElement("tag") self.xml.writeAttribute("k",r[0]) self.xml.writeAttribute("v",r[1]) self.xml.writeEndElement() i=i+1 self.progressDialog.setValue(i) if lines: self.progressDialog.setLabelText(self.tr("Saving lines...")) self.progressDialog.setMaximum(cntLines) self.progressDialog.setValue(0) i=0 c.execute("select w.id,v.version_id,w.user,w.timestamp from way w,version v \ where w.closed=0 and w.status<>'R' and w.u=1 and v.object_id=w.id and v.object_type='way' \ and (((w.max_lat between :minLat and :maxLat) or (w.min_lat between :minLat and :maxLat) or (w.min_lat<:minLat and w.max_lat>:maxLat)) \ and ((w.max_lon between :minLon and :maxLon) or (w.min_lon between :minLon and :maxLon) or (w.min_lon<:minLon and w.max_lon>:maxLon)))" ,{"minLat":dataExtent.yMinimum(),"maxLat":dataExtent.yMaximum() ,"minLon":dataExtent.xMinimum(),"maxLon":dataExtent.xMaximum()}) for (lid,ver,usr,tms) in c: geom=self.dbm.getFeatureGeometry(lid,'Line') if not geom.intersects(dataExtent): continue self.xml.writeStartElement("way") self.xml.writeAttribute("id",str(lid)) self.xml.writeAttribute("visible","true") self.xml.writeAttribute("timestamp",tms) self.xml.writeAttribute("version",str(ver)) if usr<>"": self.xml.writeAttribute("user",usr) d=self.dbm.getConnection().cursor() d.execute("select node_id from way_member where way_id=:wayId",{"wayId":lid}) for r in d: self.xml.writeEmptyElement("nd") self.xml.writeAttribute("ref",str(r[0])) d.close() if tags: tagList=self.dbm.getFeatureTags(lid,'Line') for r in tagList: self.xml.writeEmptyElement("tag") self.xml.writeAttribute("k",r[0]) self.xml.writeAttribute("v",r[1]) self.xml.writeEndElement() d=self.dbm.getConnection().cursor() d.execute("select node_id from way_member where way_id=:wayId",{"wayId":lid}) for r in d: if r[0] not in self.nodeIds: e=self.dbm.getConnection().cursor() e.execute("select n.id,n.lat,n.lon,v.version_id,n.user,n.timestamp from node n, version v \ where n.id=:nid",{"nid":r[0]}) for nodeRec in e: nid=nodeRec[0] lat=nodeRec[1] lon=nodeRec[2] ver=nodeRec[3] usr=nodeRec[4] tms=nodeRec[5] e.close() anyTags=False tagList=[] self.nodeIds.add(nid) if tags: tagList=self.dbm.getFeatureTags(nid,'Point') if len(tagList)>0: anyTags=True if anyTags: self.xml.writeStartElement("node") else: self.xml.writeEmptyElement("node") self.xml.writeAttribute("id",str(nid)) self.xml.writeAttribute("lat",str(lat)) self.xml.writeAttribute("lon",str(lon)) self.xml.writeAttribute("version",str(ver)) if usr<>"": self.xml.writeAttribute("user",usr) self.xml.writeAttribute("visible","true") self.xml.writeAttribute("timestamp",tms) if anyTags: for r in tagList: self.xml.writeEmptyElement("tag") self.xml.writeAttribute("k",r[0]) self.xml.writeAttribute("v",r[1]) self.xml.writeEndElement() d.close() i=i+1 self.progressDialog.setValue(i) if polys: self.progressDialog.setLabelText(self.tr("Saving polygons...")) self.progressDialog.setMaximum(cntPolys) self.progressDialog.setValue(0) i=0 c.execute("select w.id,v.version_id,w.user,w.timestamp from way w,version v \ where w.closed=1 and w.status<>'R' and w.u=1 and v.object_id=w.id and v.object_type='way' \ and (((w.max_lat between :minLat and :maxLat) or (w.min_lat between :minLat and :maxLat) or (w.min_lat<:minLat and w.max_lat>:maxLat)) \ and ((w.max_lon between :minLon and :maxLon) or (w.min_lon between :minLon and :maxLon) or (w.min_lon<:minLon and w.max_lon>:maxLon)))" ,{"minLat":dataExtent.yMinimum(),"maxLat":dataExtent.yMaximum() ,"minLon":dataExtent.xMinimum(),"maxLon":dataExtent.xMaximum()}) for (pid,ver,usr,tms) in c: geom=self.dbm.getFeatureGeometry(pid,'Polygon') if not geom.intersects(dataExtent): continue self.xml.writeStartElement("way") self.xml.writeAttribute("id",str(pid)) self.xml.writeAttribute("visible","true") self.xml.writeAttribute("timestamp",tms) self.xml.writeAttribute("version",str(ver)) if usr<>"": self.xml.writeAttribute("user",usr) d=self.dbm.getConnection().cursor() d.execute("select node_id from way_member where way_id=:wayId",{"wayId":pid}) first=None for r in d: self.xml.writeEmptyElement("nd") self.xml.writeAttribute("ref",str(r[0])) if first==None: first=r[0] d.close() self.xml.writeEmptyElement("nd") self.xml.writeAttribute("ref",str(first)) if tags: tagList=self.dbm.getFeatureTags(pid,'Polygon') for r in tagList: self.xml.writeEmptyElement("tag") self.xml.writeAttribute("k",r[0]) self.xml.writeAttribute("v",r[1]) self.xml.writeEndElement() d=self.dbm.getConnection().cursor() d.execute("select node_id from way_member where way_id=:wayId",{"wayId":pid}) for r in d: if r[0] not in self.nodeIds: e=self.dbm.getConnection().cursor() e.execute("select n.id,n.lat,n.lon,v.version_id,n.user,n.timestamp from node n, version v \ where n.id=:nid",{"nid":r[0]}) for nodeRec in e: nid=nodeRec[0] lat=nodeRec[1] lon=nodeRec[2] ver=nodeRec[3] usr=nodeRec[4] tms=nodeRec[5] e.close() anyTags=False tagList=[] self.nodeIds.add(nid) if tags: tagList=self.dbm.getFeatureTags(nid,'Point') if len(tagList)>0: anyTags=True if anyTags: self.xml.writeStartElement("node") else: self.xml.writeEmptyElement("node") self.xml.writeAttribute("id",str(nid)) self.xml.writeAttribute("lat",str(lat)) self.xml.writeAttribute("lon",str(lon)) self.xml.writeAttribute("version",str(ver)) if usr<>"": self.xml.writeAttribute("user",usr) self.xml.writeAttribute("visible","true") self.xml.writeAttribute("timestamp",tms) if anyTags: for r in tagList: self.xml.writeEmptyElement("tag") self.xml.writeAttribute("k",r[0]) self.xml.writeAttribute("v",r[1]) self.xml.writeEndElement() d.close() i=i+1 self.progressDialog.setValue(i) if rels: self.progressDialog.setLabelText(self.tr("Saving relations...")) self.progressDialog.setMaximum(cntRels) self.progressDialog.setValue(0) i=0 c.execute("select r.id,v.version_id,r.user,r.timestamp from relation r,version v \ where r.status<>'R' and r.u=1 and v.object_id=r.id and v.object_type='relation' \ and ( \ exists ( \ select 1 from node n, relation_member rm \ where rm.relation_id=r.id and n.status<>'R' and n.u=1 and rm.member_id=n.id and rm.member_type='node' \ and n.lat>=:minLat and n.lat<=:maxLat and n.lon>=:minLon and n.lon<=:maxLon ) \ or exists ( \ select 1 from way w, relation_member rm \ where rm.relation_id=r.id and w.status<>'R' and w.u=1 and rm.member_id=w.id and rm.member_type='way' \ and (((w.max_lat between :minLat and :maxLat) or (w.min_lat between :minLat and :maxLat) or (w.min_lat<:minLat and w.max_lat>:maxLat)) \ and ((w.max_lon between :minLon and :maxLon) or (w.min_lon between :minLon and :maxLon) or (w.min_lon<:minLon and w.max_lon>:maxLon))) \ ))" ,{"minLat":dataExtent.yMinimum(),"maxLat":dataExtent.yMaximum() ,"minLon":dataExtent.xMinimum(),"maxLon":dataExtent.xMaximum()}) for (rid,ver,usr,tms) in c: self.xml.writeStartElement("relation") self.xml.writeAttribute("id",str(rid)) self.xml.writeAttribute("visible","true") self.xml.writeAttribute("timestamp",tms) self.xml.writeAttribute("version",str(ver)) if usr<>"": self.xml.writeAttribute("user",usr) d=self.dbm.getConnection().cursor() d.execute("select member_id,member_type,role from relation_member where relation_id=:relId",{"relId":rid}) for r in d: self.xml.writeEmptyElement("member") self.xml.writeAttribute("type",r[1]) self.xml.writeAttribute("ref",str(r[0])) self.xml.writeAttribute("role",r[2]) d.close() if tags: tagList=self.dbm.getFeatureTags(rid,'Relation') for r in tagList: self.xml.writeEmptyElement("tag") self.xml.writeAttribute("k",r[0]) self.xml.writeAttribute("v",r[1]) self.xml.writeEndElement() i=i+1 self.progressDialog.setValue(i) self.xml.writeEndElement() # osm self.xml.writeEndDocument() c.close() self.disconnect(self.progressDialog, SIGNAL("canceled()"), self.cancelSaving) self.progressDialog.close() # writing into output file was finished, file can be enclosed if self.outFile and self.outFile.exists(): self.outFile.close() self.close()