QGIS/python/plugins/mapserver_export/mapserverexport.py
2012-12-10 00:12:07 +01:00

333 lines
16 KiB
Python
Executable File

"""
/***************************************************************************
MapServerExport - A QGIS plugin to export a saved project file
to a MapServer map file
-------------------
begin : 2008-01-07
copyright : (C) 2008 by Gary E.Sherman
email : sherman at mrcc.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. *
* *
***************************************************************************/
/* Adapted by Erik van de Pol */
"""
# Import the PyQt and QGIS libraries
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from xml.dom import minidom
from qgis.core import *
# Initialize Qt resources from file resources.py
import resources_rc
# Import the code for the dialog
from mapserverexportdialog import MapServerExportDialog
# Import the ms_export script that does the real work
from ms_export import *
class MapServerExport:
def __init__(self, iface):
# 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"))
def getThemeIcon(self, theName):
# get the icon from the best available theme
myCurThemePath = QgsApplication.activeThemePath() + "/plugins/" + theName;
myDefThemePath = QgsApplication.defaultThemePath() + "/plugins/" + theName;
myQrcPath = ":/plugins/mapserver_export/" + theName;
if QFile.exists(myCurThemePath):
return QIcon(myCurThemePath)
elif QFile.exists(myDefThemePath):
return QIcon(myDefThemePath)
elif QFile.exists(myQrcPath):
return QIcon(myQrcPath)
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())
# connect the action to the run method
QObject.connect(self.action, SIGNAL("triggered()"), self.run)
QObject.connect(self.iface, SIGNAL("currentThemeChanged ( QString )"), self.setCurrentTheme)
# Add toolbar button and menu item
self.iface.addWebToolBarIcon(self.action)
self.iface.addPluginToWebMenu("&MapServer Export...", self.action)
def unload(self):
# Remove the plugin menu item and icon
self.iface.removePluginWebMenu("&MapServer Export...",self.action)
self.iface.removeWebToolBarIcon(self.action)
# run method that performs all the real work
def run(self):
# create and show the MapServerExport dialog
self.dlg = MapServerExportDialog(self.iface.mainWindow())
# 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)
QObject.connect(self.dlg.ui.btnChooseProjectFile, SIGNAL("clicked()"), self.setProjectFile)
QObject.connect(self.dlg.ui.chkExpLayersOnly, SIGNAL("clicked(bool)"), self.toggleLayersOnly)
QObject.connect(self.dlg.ui.checkBoxCurrentProject, SIGNAL("clicked(bool)"), self.toggleUseCurrentProject)
QObject.connect(self.dlg.ui.btnChooseFooterFile, SIGNAL("clicked()"), self.setFooterFile)
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)
QObject.connect(self.dlg.ui.buttonBox, SIGNAL("helpRequested()"), self.helpRequested)
# 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()
# map-file name and force mapfileChanged to enable/disable ok button
self.dlg.ui.txtMapFilePath.setText(settings.value("/MapserverExport/mapfileName", QVariant("")).toString ())
self.mapfileChanged(self.dlg.ui.txtMapFilePath.text())
# map width and height
if settings.contains("/MapserverExport/mapWidth"):
self.dlg.ui.txtMapWidth.setText(settings.value("/MapserverExport/mapWidth").toString ())
if settings.contains("/MapserverExport/mapHeight"):
self.dlg.ui.txtMapHeight.setText(settings.value("/MapserverExport/mapHeight").toString ())
# MapServer IMAGETYPE's [gif|png|jpeg|wbmp|gtiff|swf|userdefined]
self.dlg.ui.cmbMapImageType.addItems(QStringList(["agg","png","gif","jpeg","wbmp","gtiff","swf","userdefined"]))
if settings.contains("/MapserverExport/imageType"):
idx = self.dlg.ui.cmbMapImageType.findText(settings.value("/MapserverExport/imageType").toString ())
self.dlg.ui.cmbMapImageType.setCurrentIndex(idx)
# 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 helpRequested(self):
QgsContextHelp.run( "MapServerExport" )
def ok_clicked(self):
# 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()
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())
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())
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, "No Map file export!", "Map file not exported because no valid qgis project file was given.")
return
if exporter.projectHasNewSymbology():
QMessageBox.information(self.dlg, "New Symbology layer(s) found", "The project you selected holds layer(s) which use 'New Symbology'.\n\nCurrently this plugin is not able to handle this.\n\nPlease change symbology of these layer(s) to 'Old Symbology'.")
self.dlg.hide()
return
self.dlg.hide()
print "Setting options"
exporter.setOptions(
unicode(self.dlg.ui.txtMapServerUrl.text()),
unicode(self.dlg.ui.cmbMapUnits.itemData( self.dlg.ui.cmbMapUnits.currentIndex() ).toString()),
unicode(self.dlg.ui.cmbMapImageType.currentText()),
unicode(self.dlg.ui.txtMapName.text()),
unicode(self.dlg.ui.txtMapWidth.text()),
unicode(self.dlg.ui.txtMapHeight.text()),
unicode(self.dlg.ui.txtWebTemplate.text()),
unicode(self.dlg.ui.txtWebHeader.text()),
unicode(self.dlg.ui.txtWebFooter.text()),
self.dlg.ui.checkBoxDump.isChecked(),
self.dlg.ui.checkBoxForce.isChecked(),
self.dlg.ui.checkBoxAntiAlias.isChecked(),
self.dlg.ui.checkBoxPartials.isChecked(),
self.dlg.ui.chkExpLayersOnly.isChecked(),
unicode(self.dlg.ui.txtFontsetPath.text()),
unicode(self.dlg.ui.txtSymbolsetPath.text())
)
print "Calling writeMapFile"
try:
result = exporter.writeMapFile()
except Exception, err:
QMessageBox.information(self.dlg, "MapServer Export Error", str(err))
return
# ok succesfull: write some setting for a next session
settings = QSettings()
# mapfile name
settings.setValue("/MapserverExport/mapfileName", QVariant(self.dlg.ui.txtMapFilePath.text()))
# map width and heigth
settings.setValue("/MapserverExport/mapWidth", QVariant(self.dlg.ui.txtMapWidth.text()))
settings.setValue("/MapserverExport/mapHeight", QVariant(self.dlg.ui.txtMapHeight.text()))
# mapserver url
settings.setValue("/MapserverExport/mapserverUrl", QVariant(self.dlg.ui.txtMapServerUrl.text()))
# map ImageType
settings.setValue("/MapserverExport/imageType", QVariant(self.dlg.ui.cmbMapImageType.currentText()))
# show results
QMessageBox.information(self.dlg, "MapServer Export Results", result)
def mapfileChanged(self, text):
# Enable OK button
btnOk = self.dlg.ui.buttonBox.button(QDialogButtonBox.Ok)
if text.size() > 0:
btnOk.setEnabled(True)
else:
btnOk.setEnabled(False)
def checkMapFile(self):
# Check if map file name is provided
mapFileName = self.dlg.ui.txtMapFilePath.text()
if mapFileName.size() == 0:
QMessageBox.warning(self.dlg, "Not saved!", "Map file not saved because no file name was given")
return False
# Check/fix for .map extension (mapserver fails to read otherwise)
if not mapFileName.trimmed().endsWith('.map'):
mapFileName += '.map'
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 excisting Map file?",
"Map file \"" + mapFileName + "\" already exists. \nShould we overwrite it?",
QMessageBox.Yes, QMessageBox.Cancel):
return False
# 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):
# 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 non-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 Exception, 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):
templateFile = QFileDialog.getOpenFileName(self.dlg, "Choose the MapServer template file", \
".", "All files (*.*)", "Filter list for selecting files from a dialog box")
self.dlg.ui.txtWebTemplate.setText(templateFile)
def setHeaderFile(self):
headerFile = QFileDialog.getOpenFileName(self.dlg, "Choose the MapServer header file", \
".", "All files (*.*)", "Filter list for selecting files from a dialog box")
self.dlg.ui.txtWebHeader.setText(headerFile)
def setFooterFile(self):
footerFile = QFileDialog.getOpenFileName(self.dlg, "Choose the MapServer footer file", \
".", "All files (*.*)", "Filter list for selecting files from a dialog box")
self.dlg.ui.txtWebFooter.setText(footerFile)
def toggleLayersOnly(self, isChecked):
# disable other sections if only layer export is desired
self.dlg.ui.grpPaths.setEnabled(not isChecked)
self.dlg.ui.grpMap.setEnabled(not isChecked)