[QGIS Commit] r12292 - trunk/qgis/python/plugins/mapserver_export

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sun Nov 29 16:44:22 EST 2009


Author: rduivenvoorde
Date: 2009-11-29 16:44:21 -0500 (Sun, 29 Nov 2009)
New Revision: 12292

Modified:
   trunk/qgis/python/plugins/mapserver_export/__init__.py
   trunk/qgis/python/plugins/mapserver_export/mapserverexport.py
   trunk/qgis/python/plugins/mapserver_export/ms_export.py
Log:
changes needed to have a more intuitive way of choosing between: exporting the current loaded project, or a project browsed to by the user. Because postgis layers need more info (primary keys) we ask the user to load a project which holds postgis layers. But warn him if he cancelled.



Modified: trunk/qgis/python/plugins/mapserver_export/__init__.py
===================================================================
--- trunk/qgis/python/plugins/mapserver_export/__init__.py	2009-11-29 19:40:27 UTC (rev 12291)
+++ trunk/qgis/python/plugins/mapserver_export/__init__.py	2009-11-29 21:44:21 UTC (rev 12292)
@@ -24,7 +24,7 @@
 def description():
   return "Export a saved QGIS project file to a MapServer map file"
 def version(): 
-  return "Version 0.4.2" 
+  return "Version 0.4.3" 
 def qgisMinimumVersion(): 
   return "1.0"
 def authorName():

Modified: trunk/qgis/python/plugins/mapserver_export/mapserverexport.py
===================================================================
--- trunk/qgis/python/plugins/mapserver_export/mapserverexport.py	2009-11-29 19:40:27 UTC (rev 12291)
+++ trunk/qgis/python/plugins/mapserver_export/mapserverexport.py	2009-11-29 21:44:21 UTC (rev 12292)
@@ -36,7 +36,6 @@
     # Save reference to the QGIS interface
     self.iface = iface
 
-  # ----------------------------------------- #
   def setCurrentTheme(self, theThemeName):
     # Set icons to the current theme
     self.action.setIcon(self.getThemeIcon("mapserver_export.png"))
@@ -55,12 +54,10 @@
     else:
       return QIcon()
 
-
   def initGui(self):  
     # Create action that will start plugin configuration
     self.action = QAction(self.getThemeIcon("mapserver_export.png"), \
         "MapServer Export", self.iface.mainWindow())
-    #self.action.setWhatsThis("Configuration for Zoom To Point plugin")
     # connect the action to the run method
     QObject.connect(self.action, SIGNAL("activated()"), self.run) 
     QObject.connect(self.iface, SIGNAL("currentThemeChanged ( QString )"), self.setCurrentTheme)
@@ -68,18 +65,15 @@
     self.iface.addToolBarIcon(self.action)
     self.iface.addPluginToMenu("&MapServer Export...", self.action)
 
-
   def unload(self):
     # Remove the plugin menu item and icon
     self.iface.removePluginMenu("&MapServer Export...",self.action)
     self.iface.removeToolBarIcon(self.action)
 
-
   # run method that performs all the real work
   def run(self): 
     # create and show the MapServerExport dialog 
     self.dlg = MapServerExportDialog()
-    
     # attach events to inputs and buttons
     QObject.connect(self.dlg.ui.btnChooseFile, SIGNAL("clicked()"), self.setMapFile)
     QObject.connect(self.dlg.ui.txtMapFilePath, SIGNAL("textChanged(QString)"), self.mapfileChanged)
@@ -90,12 +84,9 @@
     QObject.connect(self.dlg.ui.btnChooseHeaderFile, SIGNAL("clicked()"), self.setHeaderFile)
     QObject.connect(self.dlg.ui.btnChooseTemplateFile, SIGNAL("clicked()"), self.setTemplateFile)
     QObject.connect(self.dlg.ui.buttonBox, SIGNAL("accepted()"), self.ok_clicked)
-    
-    # qgs-project
-    # defaults to current instance
+    # qgs-project defaults to current instance
     project = QgsProject.instance()
     self.dlg.ui.txtQgisFilePath.setText(project.fileName())
-
     # get some settings from former successfull exports
     # defaults are defined in ms_export.py and set in mapserverexportdialog.py
     settings = QSettings()
@@ -115,64 +106,47 @@
     # MapServer URL (default value already set by dialog defaults)
     if settings.contains("/MapserverExport/mapserverUrl"):
       self.dlg.ui.txtMapServerUrl.setText(settings.value("/MapserverExport/mapserverUrl").toString())
-    
-    
     # set title or default to one if none available
     title = project.title()
     if title == "":
       title = "QGIS-MAP"
     self.dlg.ui.txtMapName.setText(title)
-
     # TODO: fetch units used from current project
     # QGIS: Meters, Feet, Degrees, UnknownUnit since 1.4 also: DecimalDegrees, DegreesMinutesSeconds, DegreesDecimalMinutes 	
     # Mapserver: UNITS [feet|inches|kilometers|meters|miles|dd]
-	
     self.dlg.show()
 
   def ok_clicked(self):
-    if self.checkMapFile():
+    # check if current project is saved or dirty
+    if not self.checkCurrentProject():
+      # abort because user apparently did not wat to save or Cancelled
+      return
+    if not self.checkMapFile():
+      print "Failed for Map file check, try again..."
+      return
+    else:
       self.saveMapFile()
-    else:
-      print "Failed for Map file check, try again..."
-      pass
 
   def toggleUseCurrentProject(self, boolUseCurrent):
     self.dlg.ui.txtQgisFilePath.setEnabled(not boolUseCurrent)
     self.dlg.ui.btnChooseProjectFile.setEnabled(not boolUseCurrent)
     if boolUseCurrent:
-      if self.dlg.ui.txtQgisFilePath.text().size() == 0:
-        # reload path of current project
-        self.dlg.ui.txtQgisFilePath.setText(QgsProject.instance().fileName())
-        # check if current project is saved and/or dirty? Nope: will be done when Ok clicked
+      #if self.dlg.ui.txtQgisFilePath.text().size() == 0:
+      # reload path of current project
+      self.dlg.ui.txtQgisFilePath.setText(QgsProject.instance().fileName())
     else:  
       # open dialog to choose project file
       self.setProjectFile()
     
-    
 
-  def saveMapFile(self):
-    # get the settings from the dialog and export the map file  
-    print "Creating exporter using %s and %s" % (self.dlg.ui.txtQgisFilePath.text(), self.dlg.ui.txtMapFilePath.text())
-    if self.dlg.ui.txtQgisFilePath.text().size() == 0:
-      saveAsFileName = QFileDialog.getSaveFileName(self.dlg,
-                    "Please choose to save QGis project file as...",
-                    ".",
-                    "QGis files (*.qgs)",
-                    "Filter list for selecting files from a dialog box")
-      # Check that a file was selected
-      if saveAsFileName.size() == 0:
-        QMessageBox.warning(self.dlg, "Not saved!", "QGis project file not saved because no file name was given")
-        return
-      else:
-        self.dlg.ui.txtQgisFilePath.setText(saveAsFileName)
-        
+  def saveMapFile(self):   
+    # get the settings from the dialog and export the map file
+    print "Creating exporter using '%s' and '%s'" % (self.dlg.ui.txtQgisFilePath.text(), self.dlg.ui.txtMapFilePath.text())
     exporter = Qgis2Map(unicode(self.dlg.ui.txtMapFilePath.text()))
-    
     # Parse qgis project file and check success
     if not(exporter.setQgsProject(self.dlg.ui.txtQgisFilePath.text())):
-      QMessageBox.warning(self.dlg, "Not saved!", "File not saved because no valid qgis project file was given.")
+      QMessageBox.warning(self.dlg, "No Map file export!", "Map file not exported because no valid qgis project file was given.")
       return
-    
     self.dlg.hide()
     print "Setting options"
     exporter.setOptions(
@@ -233,21 +207,100 @@
     self.dlg.ui.txtMapFilePath.setText(mapFileName)
     # Check if map file exists and we should overwrite it
     if QFile(mapFileName).exists():
-      if QMessageBox.Cancel == QMessageBox.question(self.dlg, "Overwrite?",
+      if QMessageBox.Cancel == QMessageBox.question(self.dlg, "Overwrite excisting Map file?",
                     "Map file \"" + mapFileName + "\" already exists. \nShould we overwrite it?",
                     QMessageBox.Yes, QMessageBox.Cancel):
         return False
-    # mapfile ok, extension ok, ok to overwrite  
+    # mapfile ok, extension ok, overwrite  ok
     return True
-
+    
+  # check if current project is saved and or dirty (has modifications)
+  def checkCurrentProject(self, forUnload=False):
+    project = QgsProject.instance()
+    # question: save project on loading export dialog?
+    if project.isDirty():
+      msg = "Save project to \"" + project.fileName() + "\" before exporting?\nOnly the last saved version of your project will be exported."
+      if project.fileName()=="":
+        msg = "Please save project before exporting.\nOnly saved projects can be exported."
+      if forUnload:
+        msg = "Save project first?\nAfter saving, this project will be unloaded."
+      shouldSave = QMessageBox.question(self.dlg, "Save?", msg,
+                    QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
+      if shouldSave == QMessageBox.Yes:
+        if project.fileName().size() == 0:
+          # project has not yet been saved:
+          saveAsFileName = QFileDialog.getSaveFileName(self.dlg,
+                      "Save QGIS Project file as...", ".",
+                      "QGIS Project Files (*.qgs)", "Filter list for selecting files from a dialog box")
+          # Check that a file was selected
+          if saveAsFileName.size() == 0:
+            QMessageBox.warning(self.dlg, "Not saved!", "QGis project file not saved because no file name was given.")
+            # fall back to using current project if available
+            self.dlg.ui.txtQgisFilePath.setText(project.fileName())
+          else:
+            if not saveAsFileName.trimmed().endsWith('.qgs'):
+              saveAsFileName += '.qgs'
+            self.dlg.ui.txtQgisFilePath.setText(saveAsFileName)
+            project.setFileName(saveAsFileName)
+            project.write()
+        else:
+          project.write()
+        #project ok now
+        return True
+      elif shouldSave == QMessageBox.No and forUnload:
+        # unloading a non saved project: just leave ...
+        return True
+      elif shouldSave == QMessageBox.No:
+        # users does not want to save project, but has to because only saved projects can be exported
+        return False
+      elif shouldSave == QMessageBox.Cancel:
+        # user cancelled
+        return False
+    else: 
+      # project saved and not dirty
+      return True
+      
   def setMapFile(self):
     mapFileName = QFileDialog.getSaveFileName(self.dlg, "Name for the map file", \
       self.dlg.ui.txtMapFilePath.text(), "MapServer map files (*.map);;All files (*.*)","Filter list for selecting files from a dialog box")
     self.dlg.ui.txtMapFilePath.setText(mapFileName)
 
   def setProjectFile(self):
-    qgisProjectFile = QFileDialog.getOpenFileName(self.dlg, "Choose a QGIS Project file", \
-      ".", "QGIS Project Files (*.qgs);;All files (*.*)", "Filter list for selecting files from a dialog box")
+    # check if it's needed to save current project, will return False if user cancelled
+    if not self.checkCurrentProject(True):
+      return
+    qgisProjectFile = QFileDialog.getOpenFileName(self.dlg, "Choose a QGIS Project", \
+      ".", "QGIS Project Files (*.qgs);;", "Filter list for selecting files from a dialog box")
+    if not qgisProjectFile:
+      # cancelled: check checkBoxCurrentProject again 
+      self.dlg.ui.checkBoxCurrentProject.setChecked(True)
+      self.dlg.ui.txtQgisFilePath.setEnabled(False)
+      return
+    try:
+      # reading a nog qgs or not existing file results in qgis crash
+      # QgsProject.instance().read(QFileInfo(qgisProjectFile))
+      # we try to open the file first to see if it can be parsed...
+      exporter = Qgis2Map(unicode(self.dlg.ui.txtMapFilePath.text()))
+      if exporter.setQgsProject(qgisProjectFile):
+        # project file OK !!
+        pass
+      if exporter.projectHasPostgisLayers():
+        loadProject = QMessageBox.question(self.dlg, "Load project?", 
+            "The project you selected holds one or more postgis layers. \nTo be able to export a valid DATA string in the map file,\nthe project should be loaded into QGIS. \nNot loading can result in non valid DATA strings in map file.\nSo, should we load it into qgis?",
+            QMessageBox.Yes, QMessageBox.No)
+        if loadProject == QMessageBox.Yes:
+          QgsProject.instance().read(QFileInfo(qgisProjectFile))
+        else:
+          # postgis, but user refuses to load it, just go and see what happens
+          pass
+      else:
+        # NO postgis, go
+        pass
+    except QgsException, err:
+      QMessageBox.information(self.dlg, "Error reading or loading the selected project file", str(err))
+      self.dlg.ui.checkBoxCurrentProject.setChecked(True)
+      self.dlg.ui.txtQgisFilePath.setEnabled(False)
+      return
     self.dlg.ui.txtQgisFilePath.setText(qgisProjectFile)
 
   def setTemplateFile(self):

Modified: trunk/qgis/python/plugins/mapserver_export/ms_export.py
===================================================================
--- trunk/qgis/python/plugins/mapserver_export/ms_export.py	2009-11-29 19:40:27 UTC (rev 12291)
+++ trunk/qgis/python/plugins/mapserver_export/ms_export.py	2009-11-29 21:44:21 UTC (rev 12292)
@@ -168,13 +168,30 @@
     self.header = header.encode('utf-8')
     self.footer = footer.encode('utf-8')
     #print units, image, mapname, width, height, template, header, footer
-
     self.dump               = bool2str[dump]
     self.force              = bool2str[force]
     self.antialias          = bool2str[antialias]
     self.partials           = bool2str[partials]
     self.exportLayersOnly   = exportLayersOnly
 
+  # method to check the project file for the exitence of postgis layers
+  # if so it should be loaded in qgis before exporting, because a connection
+  # to the database is needed to determine primary keys etc etc
+  def projectHasPostgisLayers(self):
+    # get the list of maplayer nodes
+    maplayers = self.qgs.getElementsByTagName("maplayer")
+    for lyr in maplayers:
+      try:
+        providerString = lyr.getElementsByTagName("provider")[0].childNodes[0].nodeValue.encode('utf-8')
+      except:
+        print "ERROR getting provider string from layer"
+        # if providerString is null
+        providerString = ''
+      if providerString == 'postgres':
+        #print  "POSTGIS LAYER !!"
+        return True
+    return False
+      
 
   ## All real work happens here by calling methods to write the
   ## various sections of the map file
@@ -205,11 +222,11 @@
         self.writeLegendSection()
         logmsg += "Wrote legend section\n"
         print " --- python : legend section done"
-
         # write the WEB section
         print " --- python : web section "
-        self.writeWebSection()
+        webMsg = self.writeWebSection()
         logmsg += "Wrote web section\n"
+        logmsg += webMsg
         print " --- python : web section done"
 
     # write the LAYER sections
@@ -351,6 +368,7 @@
 
   # Write the WEB section of the map file
   def writeWebSection(self):
+    resultMsg = ""
     self.outFile.write("  # Web interface definition. Only the template parameter\n")
     self.outFile.write("  # is required to display a map. See MapServer documentation\n")
     self.outFile.write("  WEB\n")
@@ -369,6 +387,10 @@
     self.outFile.write("    # WMS server settings\n")
     self.outFile.write("    METADATA\n")
     self.outFile.write("      'ows_title'           '" + self.mapName + "'\n")
+    # if mapserverurl is still defaults.mapServerUrl, give warning
+    if defaults.mapServerUrl==self.mapServerUrl:
+      resultMsg += " ! MapServer url still default value: '" +  defaults.mapServerUrl + \
+            "'?\n  Be sure there is a valid mapserverurl in the 'ows_onlineresource'.\n"
     self.outFile.write("      'ows_onlineresource'  '" + self.mapServerUrl + "?" + "map" + "=" + self.mapFile + "'\n")
     self.outFile.write("      'ows_srs'             'EPSG:" + epsg + "'\n")
     self.outFile.write("    END\n\n")
@@ -389,15 +411,14 @@
     if self.footer != "":
       self.outFile.write("    FOOTER '" + self.footer + "'\n")
     self.outFile.write("  END\n\n")
+    return resultMsg
 
   # Write the map layers - we have to defer writing to disk so we
   # can invert the order of the layes, since they are opposite in QGIS 
   # compared to mapserver
   def writeMapLayers(self):
-    # get the list of legend nodes so the layers can be written in the
-    # proper order
     resultMsg = ''
-    
+    # get the list of legend nodes so the layers can be written in the proper order
     legend_nodes = self.qgs.getElementsByTagName("legendlayer")
     self.z_order = list()
     for legend_node in legend_nodes:
@@ -428,9 +449,12 @@
       elif lyr.getAttribute("type").encode('utf-8') == 'raster':  
         layer_def += "    TYPE " + lyr.getAttribute("type").encode('utf-8').upper() + "\n"
 
-      # Uses default value from the gui
+      # Use (global) default value from the gui
       layer_def += "    DUMP " + self.dump + "\n"
-      
+      # id dump = true: add TEMPLATE to be able to use getFeatureInfoRequests
+      if self.dump=="true":
+        layer_def += "    TEMPLATE fooOnlyForWMSGetFeatureInfo\n"
+        
       # Set min/max scales
       if lyr.getAttribute('hasScaleBasedVisibilityFlag').encode('utf-8') == 1:
         layer_def += "    MINSCALE " + lyr.getAttribute('minimumScale').encode('utf-8') + "\n"
@@ -450,26 +474,28 @@
         providerString = ''
 
       if providerString == 'postgres':
-
         # it's a postgis layer
         uri = QgsDataSourceURI(dataString)
-
         layer_def += "    CONNECTIONTYPE postgis\n"
-        layer_def += "    CONNECTION \"" + uri.connectionInfo() + "\"\n"
+        connectionInfo = str(uri.connectionInfo())
+        # if connectionInfo does NOT contain a password, warn user:
+        if connectionInfo.find("password")<0:
+          resultMsg += " ! No password in connection string for postgres layer '" +  layer_name + \
+            "' \n  Add it, or make sure mapserver can connect to postgres.\n"  
+        layer_def += "    CONNECTION \"" + connectionInfo + "\"\n"
         # EvdP: it seems that the uri.geometryColumn() is quoted automaticly by PostGIS.
         # To prevent double quoting, we don't quote here.
         # Now we are unable to process uri.geometryColumn()s with special characters (uppercase... etc.)
         #layer_def += "    DATA '\"" + uri.geometryColumn() + "\" FROM " + uri.quotedTablename() + "'\n"
         #layer_def += "    DATA '" + uri.geometryColumn() + " FROM " + uri.quotedTablename() + "'\n"
-
         layer_id = lyr.getElementsByTagName("id")[0].childNodes[0].nodeValue.encode("utf-8")
         # TODO: check if this project is actually loaded in QGis
         # only in loaded project files it's possible to determine the primary key of a postgis table
         uniqueId = self.getPrimaryKey(layer_id, uri.table())
         # %tablename% is returned when no uniqueId is found: inform user
         if uniqueId.find("%") >= 0:
-            resultMsg += "  ! No primary key found for postgres layer '" +  layer_name + \
-            "' \n    Make sure you edit the mapfile and change the DATA-string \n    containing '" + uniqueId + "'\n"
+            resultMsg += " ! No primary key found for postgres layer '" +  layer_name + \
+            "' \n  Make sure you edit the mapfile and change the DATA-string \n    containing '" + uniqueId + "'\n"
         epsg = self.getEpsg(lyr)
         
         layer_def += "    DATA '" + uri.geometryColumn() + " FROM " + uri.quotedTablename() + " USING UNIQUE " + uniqueId + " USING srid=" + epsg + "'\n"
@@ -608,16 +634,19 @@
     This is obviously a lousy solution at best.
 
     This script requires the project you export to be open in QGis!!
+    
+    This method will return either the primary key of this table, or
+    the string %tablename% in case we're not able to find it...
     """
 
     mlr = QgsMapLayerRegistry.instance()
     layers = mlr.mapLayers()
     if not QString(layerId) in layers:
-      # layerId of this postgis layer NOT in the layerlist... probably 
-      # the project is not loaded in qgis (to be able to find the primary
-      # key, one should load the project in QGis
-      raise Exception("ERROR: layer not found in project layers.... \nThis happens with postgis layers in a project which \nis not loaded in QGis.\nDid you load this project into QGis? \nIf not please load project first, and then export it to mapserver.")
-
+      # layerId of this postgis layer NOT in the layerlist... 
+      # probably the project is not loaded in qgis 
+      #raise Exception("ERROR: layer not found in project layers.... \nThis happens with postgis layers in a project which \nis not loaded in QGis.\nDid you load this project into QGis? \nIf not please load project first, and then export it to mapserver.")
+      return str("%" + tableName + "_id%")
+      
     layer = layers[QString(layerId)]
     dataProvider = layer.dataProvider()
     fields = dataProvider.fields()



More information about the QGIS-commit mailing list