ms_export.py

please test this ms_export.py - Richard Duivenvoorde, 2010-07-02 04:40 AM

Download (46.7 KB)

 
1
#***************************************************************************
2
#    ms_export.py
3
#    --------------------------------------
4
#   Date                 : Sun Sep 16 12:33:46 AKDT 2007
5
#   Copyright            : (C) 2008 by Gary E. Sherman
6
#   Email                : sherman at mrcc dot com
7
#***************************************************************************
8
#*                                                                         *
9
#*   This program is free software; you can redistribute it and/or modify  *
10
#*   it under the terms of the GNU General Public License as published by  *
11
#*   the Free Software Foundation; either version 2 of the License, or     *
12
#*   (at your option) any later version.                                   *
13
#*                                                                         *
14
#***************************************************************************/
15
# This class exports a QGIS project file to a mapserver .map file.
16
# All the work is done in the writeMapFile method. The msexport binary
17
# presents a Qt based GUI that collects the needed information for this
18
# script. 
19
# Matthew Perry contributed major portions of this work.
20
# Adapted by Erik van de Pol
21
#
22
# CHANGES SHOULD NOT BE MADE TO THE writeMapFile METHOD UNLESS YOU
23
# ARE CHANGING THE QgsMapserverExport CLASS AND YOU KNOW WHAT YOU ARE
24
# DOING
25

    
26
from xml.dom import minidom
27
from string import *
28
import platform
29
from qgis.core import QgsDataSourceURI
30
from qgis.core import QgsMapLayerRegistry
31
#from qgis.core import QgsProject
32
from PyQt4.QtCore import QString
33
from PyQt4.QtCore import QVariant
34

    
35

    
36
# symbol map
37
qgis2map_symbol = {
38
  "hard:circle"               : "circle",
39
  "hard:triangle"             : "triangle",
40
  "hard:equilateral_triangle" : "equilateral-triangle",
41
  "hard:rectangle"            : "square",
42
  "hard:regular_star"         : "star",
43
  "hard:diamond"              : "diamond"
44
}
45

    
46
# alignment/position map
47
qgis2map_aligment2position = {
48
  "center"     :   "cc",
49
  "above"      :   "uc",
50
  "right"      :   "cr",
51
  "below"      :   "lc",
52
  "left"       :   "cl",
53
  "aboveright" :   "ur",
54
  "belowright" :   "lr",
55
  "belowleft"  :   "ll",
56
  "aboveleft"  :   "ul"
57
}
58

    
59
# the keys are fonts that must be available in QGis
60
# the values in this dictionary must correspond to 
61
# the fonts in the file denoted by the FONTSET in the mapfile
62
#
63
# "MS Shell Dlg 2" is the default label font in QGis.
64
# The automatic mapping to "Tahoma" is correct for Windows 2000, Windows XP,
65
# Windows Server 2003, Windows Vista and Windows 7.
66
# See: http://msdn.microsoft.com/en-us/library/dd374112%28VS.85%29.aspx
67
qgis2map_fontset = {
68
  "Arial"           : "arial",
69
  "Courier"         : "courier",
70
  "Georgia"         : "georgia",
71
  "Times New Roman" : "times",
72
  "Trebuchet MS"    : "trebuchet_ms",
73
  "Verdana"         : "verdana",
74
  "Tahoma"          : "tahoma",
75
  "MS Shell Dlg 2"  : "tahoma"
76
}
77
# Note that tahoma-italic and tahoma-bold-italic do not exist.
78
# Therefore a mapping to the corresponding verdana-variants is made
79
# in the fonts file pointed to by the fontsPath. Feel free to map to another font there.
80

    
81
bool2str = {True: "true", False: "false"}
82

    
83
# This is a magic number now. Rationale?
84
symbolSizeMultiplier = 3.5
85

    
86

    
87
class Qgis2MapDefaults: pass
88

    
89
defaults = Qgis2MapDefaults()
90

    
91
defaults.fontsPath = "./fonts/fonts.txt"
92
defaults.symbolsPath = "./symbols/symbols.txt"
93
if platform.system() == "Windows":
94
  defaults.mapServerUrl = "http://my.host.com/cgi-bin/mapserv.exe"
95
else:
96
  defaults.mapServerUrl = "http://localhost/cgi-bin/mapserv"
97
defaults.width = "100"
98
defaults.height = "100"
99

    
100
defaults.dump = True
101
defaults.force = True
102
defaults.partials = True
103
defaults.antialias = True
104

    
105

    
106

    
107
class Qgis2Map:
108
  def __init__(self, mapFile):
109
    self.mapFile = mapFile
110
    # init the other members that are not set by the constructor
111
    self.mapServerUrl = defaults.mapServerUrl
112
    self.fontsPath = defaults.fontsPath
113
    self.symbolsPath = defaults.symbolsPath
114
    self.units = ''
115
    self.imageType = ''
116
    self.mapName = ''
117
    self.width = defaults.width
118
    self.height = defaults.height
119
    self.minimumScale = ''
120
    self.maximumScale = ''
121
    self.template = ''
122
    self.header = ''
123
    self.footer = ''
124
    self.dump = bool2str[defaults.dump]
125
    self.force = bool2str[defaults.force]
126
    self.antialias = bool2str[defaults.antialias]
127
    self.partials = bool2str[defaults.partials]
128
    self.symbolQueue = {}
129

    
130
  def setQgsProject(self, projectFileName):
131
    try:
132
      self.projectFileName = projectFileName 
133
      # create the DOM
134
      self.qgs = minidom.parse(unicode(self.projectFileName))
135
      return True
136
    except:
137
      return False
138

    
139
  # Set the options collected from the GUI
140
  def setOptions(self, msUrl, units, image, mapname, width, height, template, header, footer, dump, force, antialias, partials, exportLayersOnly, fontsPath, symbolsPath):
141
    if msUrl.encode('utf-8') != "":
142
      self.mapServerUrl = msUrl.encode('utf-8')
143

    
144
    if fontsPath.encode('utf-8') != "":
145
      self.fontsPath = fontsPath.encode('utf-8')
146

    
147
    if symbolsPath.encode('utf-8') != "":
148
      self.symbolsPath = symbolsPath.encode('utf-8')
149

    
150
    if width.encode('utf-8') != "":
151
      self.width = width.encode('utf-8')
152

    
153
    if height.encode('utf-8') != "":
154
      self.height = height.encode('utf-8')
155

    
156
    self.units = units.encode('utf-8')
157
    self.imageType = image.encode('utf-8')
158
    self.mapName = mapname.encode('utf-8')
159

    
160
    #self.minimumScale = minscale
161
    #self.maximumScale = maxscale
162
    # TEMPLATE is needed for getFeatureInfo requests in WMS:
163
    # always set someting ...
164
    template = template.encode('utf-8')
165
    if template == "":
166
      template = "fooOnlyForWMSGetFeatureInfo"
167
    self.template = template
168
    self.header = header.encode('utf-8')
169
    self.footer = footer.encode('utf-8')
170
    #print units, image, mapname, width, height, template, header, footer
171
    self.dump               = bool2str[dump]
172
    self.force              = bool2str[force]
173
    self.antialias          = bool2str[antialias]
174
    self.partials           = bool2str[partials]
175
    self.exportLayersOnly   = exportLayersOnly
176

    
177
  # method to check the project file for the exitence of postgis layers
178
  # if so it should be loaded in qgis before exporting, because a connection
179
  # to the database is needed to determine primary keys etc etc
180
  def projectHasPostgisLayers(self):
181
    # get the list of maplayer nodes
182
    maplayers = self.qgs.getElementsByTagName("maplayer")
183
    for lyr in maplayers:
184
      try:
185
        providerString = lyr.getElementsByTagName("provider")[0].childNodes[0].nodeValue.encode('utf-8')
186
      except:
187
        print "ERROR getting provider string from layer"
188
        # if providerString is null
189
        providerString = ''
190
      if providerString == 'postgres':
191
        #print  "POSTGIS LAYER !!"
192
        return True
193
    return False
194
      
195

    
196
  ## All real work happens here by calling methods to write the
197
  ## various sections of the map file
198
  def writeMapFile(self):
199
    # open the output file
200
    print "creating the map file"
201
    self.outFile = open(self.mapFile, 'w')
202
    logmsg = "Starting\n"
203
    
204
    if self.exportLayersOnly == False:
205
        # write the general map and web settings
206
        print " --- python : map section "
207
        self.writeMapSection()
208
        logmsg +=  "Wrote map section\n"
209
        print " --- python : map section done"
210
        # write the projection section
211
        print " --- python : proj section "
212
        self.writeProjectionSection()
213
        logmsg += "Wrote projection section\n"
214
        print " --- python : proj section done"
215
        # write the output format section
216
        print " --- python : outputformat section "
217
        self.writeOutputFormat()
218
        logmsg += "Wrote output format section\n"
219
        print " --- python : outputformat section done"
220
        # write the legend section
221
        print " --- python : legend section"
222
        self.writeLegendSection()
223
        logmsg += "Wrote legend section\n"
224
        print " --- python : legend section done"
225
        # write the WEB section
226
        print " --- python : web section "
227
        webMsg = self.writeWebSection()
228
        logmsg += "Wrote web section\n"
229
        logmsg += webMsg
230
        print " --- python : web section done"
231

    
232
    # write the LAYER sections
233
    print " --- python : layer section "
234
    layersMsg = self.writeMapLayers()
235
    logmsg += "Wrote map layers\n"
236
    logmsg += layersMsg
237
    print " --- python : layer section done"
238

    
239
    if self.exportLayersOnly == False:
240
        # we use an external synbol set instead
241
        # write the symbol defs section
242
        # must happen after layers so we can build a symbol queue
243
        #print " --- python : symbol section "
244
        #self.writeSymbolSection()
245
        #logmsg += "Wrote symbol section\n"
246
        #print " --- python : symbol section done"
247

    
248
        # END and close the map file
249
        self.outFile.write("END")
250
        self.outFile.close()
251

    
252
    logmsg += "Map file completed for " + self.projectFileName + "\n"
253
    logmsg += "Map file saved as " + self.mapFile + "\n"
254
    if self.exportLayersOnly:
255
      logmsg += "\n> We only saved the LAYER portion of the map file. \nMerge this into an excisting map file to see it working\n"
256
    else:
257
      logmsg += "\n> If this mapfile is accessible by your mapserver, you\nshould be able to see the capabilities by firing this url:\n" + self.mapServerUrl + "?MAP="+self.mapFile+"&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities\n"
258
      logmsg += "\n> if this mapfile is accessible by your mapserver, you\nshould be able to see a map by firing this url:\n" + self.mapServerUrl + "?MAP="+self.mapFile+"&SERVICE=WMS&LAYERS=ALL&MODE=MAP\n"
259
    return logmsg
260

    
261
  # Write the general parts of the map section
262
  def writeMapSection(self):
263
    self.outFile.write("# Map file created from QGIS project file " + str(self.projectFileName).encode('utf-8') + "\n")
264
    self.outFile.write("# Edit this file to customize for your map interface\n")
265
    self.outFile.write("# (Created with PyQgis MapServer Export plugin)\n")
266
    self.outFile.write("MAP\n")
267
    self.outFile.write("  NAME \"" + self.mapName + "\"\n")
268
    self.outFile.write("  # Map image size\n")
269
    if self.width == '' or self.height == '':
270
      self.outFile.write("  SIZE 0 0\n") 
271
    else:
272
      self.outFile.write("  SIZE " + str(self.width) + " " + str(self.height) + "\n")
273
      
274
    self.outFile.write("  UNITS %s\n" % (self.units))
275
    self.outFile.write("\n")
276
    # extents
277
    self.outFile.write(self.getExtentString())
278

    
279
    self.outFile.write("  FONTSET '" + self.fontsPath + "'\n")
280
    # use of external symbol set
281
    self.outFile.write("  SYMBOLSET '" + self.symbolsPath + "'\n")
282

    
283
  def getExtentString(self):
284
    stringToAddTo = ""
285

    
286
    xmin = self.qgs.getElementsByTagName("xmin")
287
    stringToAddTo += "  EXTENT "
288
    stringToAddTo += xmin[0].childNodes[0].nodeValue.encode('utf-8')
289
    stringToAddTo += " "
290

    
291
    ymin = self.qgs.getElementsByTagName("ymin")
292
    stringToAddTo += ymin[0].childNodes[0].nodeValue.encode('utf-8')
293
    stringToAddTo += " "
294

    
295
    xmax = self.qgs.getElementsByTagName("xmax")
296
    stringToAddTo += xmax[0].childNodes[0].nodeValue.encode('utf-8')
297
    stringToAddTo += " "
298

    
299
    ymax = self.qgs.getElementsByTagName("ymax")
300
    stringToAddTo += ymax[0].childNodes[0].nodeValue.encode('utf-8')
301
    stringToAddTo += "\n"
302

    
303
    return stringToAddTo
304

    
305
  # Write the OUTPUTFORMAT section
306
  def writeOutputFormat(self):
307
    self.outFile.write("  # Background color for the map canvas -- change as desired\n")
308
    self.outFile.write("  IMAGECOLOR 255 255 255\n")
309
    self.outFile.write("  IMAGEQUALITY 95\n")
310
    self.outFile.write("  IMAGETYPE " + self.imageType + "\n")
311
    self.outFile.write("\n")
312
    self.outFile.write("  OUTPUTFORMAT\n")
313
    self.outFile.write("    NAME " + self.imageType + "\n")
314
    if self.imageType == 'agg':
315
        self.outFile.write("    DRIVER AGG/PNG\n")
316
        self.outFile.write("    IMAGEMODE RGB\n")
317
    else:
318
        self.outFile.write("    DRIVER 'GD/" + self.imageType.upper() + "'\n")
319
        self.outFile.write("    MIMETYPE 'image/" + lower(self.imageType) + "'\n")
320
        if self.imageType.lower() != "gif":
321
          self.outFile.write("    IMAGEMODE RGBA\n")
322
        self.outFile.write("    EXTENSION '" + lower(self.imageType) + "'\n")
323
    self.outFile.write("  END\n")
324
    
325

    
326
  # Write Projection section
327
  def writeProjectionSection(self):
328
    # Need to get the destination srs from one of the map layers since
329
    # the project file doesn't contain the epsg id or proj4 text for 
330
    # the map apart from that defined in each layer
331

    
332
    self.outFile.write("  PROJECTION\n")
333

    
334
    # Get the proj4 text from the first map layer's destination SRS
335
    destsrs = self.qgs.getElementsByTagName("destinationsrs")[0] 
336
    proj4Text = destsrs.getElementsByTagName("proj4")[0].childNodes[0].nodeValue.encode('utf-8') 
337
    # the proj4 text string needs to be reformatted to make mapserver happy
338
    self.outFile.write(self.formatProj4(proj4Text))
339

    
340
    self.outFile.write("  END\n\n")
341

    
342
  # Write the LEGEND section
343
  def writeLegendSection(self):
344
    self.outFile.write("  # Legend\n")
345
    self.outFile.write("  LEGEND\n")
346
    self.outFile.write("      IMAGECOLOR 255 255 255\n")
347
    self.outFile.write("    STATUS ON\n")
348
    self.outFile.write("    KEYSIZE 18 12\n")
349
    self.outFile.write("    LABEL\n")
350
    self.outFile.write("      TYPE BITMAP\n")
351
    self.outFile.write("      SIZE MEDIUM\n")
352
    self.outFile.write("      COLOR 0 0 89\n")
353
    self.outFile.write("    END\n")
354
    self.outFile.write("  END\n\n")
355

    
356
    # groups are ignored as of yet
357
    self.legendlayerfileNodesById = {}
358
    for legendlayerfileNode in self.qgs.getElementsByTagName("legend")[0].getElementsByTagName("legendlayerfile"):
359
      key = legendlayerfileNode.getAttribute("layerid").encode("utf-8")
360
      if (key != ""):
361
        self.legendlayerfileNodesById[key] = legendlayerfileNode
362

    
363
  # Write the symbol definitions
364
  def writeSymbolSection(self):
365
    for symbol in self.symbolQueue.keys():
366
      self.outFile.write( self.symbolQueue[symbol] )
367
      self.outFile.write( "\n" )
368

    
369
  # Write the WEB section of the map file
370
  def writeWebSection(self):
371
    resultMsg = ""
372
    self.outFile.write("  # Web interface definition. Only the template parameter\n")
373
    self.outFile.write("  # is required to display a map. See MapServer documentation\n")
374
    self.outFile.write("  WEB\n")
375
    self.outFile.write("    # Set IMAGEPATH to the path where MapServer should\n")
376
    self.outFile.write("    # write its output.\n")
377
    self.outFile.write("    IMAGEPATH '/tmp/'\n")
378
    self.outFile.write("\n")
379
    self.outFile.write("    # Set IMAGEURL to the url that points to IMAGEPATH\n")
380
    self.outFile.write("    # as defined in your web server configuration\n")
381
    self.outFile.write("    IMAGEURL '/tmp/'\n")
382
    self.outFile.write("\n")
383

    
384
    destsrs = self.qgs.getElementsByTagName("destinationsrs")[0]
385
    try:
386
      epsg = destsrs.getElementsByTagName("srid")[0].childNodes[0].nodeValue.encode("utf-8")
387
    except:
388
      # default to epsg
389
      epsg="4326"
390
    self.outFile.write("    # WMS server settings\n")
391
    self.outFile.write("    METADATA\n")
392
    self.outFile.write("      'ows_title'           '" + self.mapName + "'\n")
393
    # if mapserverurl is still defaults.mapServerUrl, give warning
394
    if defaults.mapServerUrl==self.mapServerUrl:
395
      resultMsg += " ! MapServer url still default value: '" +  defaults.mapServerUrl + \
396
            "'?\n  Be sure there is a valid mapserverurl in the 'ows_onlineresource'.\n"
397
    self.outFile.write("      'ows_onlineresource'  '" + self.mapServerUrl + "?" + "map" + "=" + self.mapFile + "'\n")
398
    self.outFile.write("      'ows_srs'             'EPSG:" + epsg + "'\n")
399
    self.outFile.write("    END\n\n")
400

    
401
    self.outFile.write("    #Scale range at which web interface will operate\n")
402
    if self.minimumScale != "":
403
      self.outFile.write("    MINSCALE " + self.minimumScale + "\n") 
404
    if self.maximumScale != "":
405
      self.outFile.write("    MAXSCALE " + self.maximumScale + "\n") 
406

    
407
    self.outFile.write("    # Template and header/footer settings\n")
408
    self.outFile.write("    # Only the template parameter is required to display a map. See MapServer documentation\n")
409
    
410
    if self.template != "":
411
      self.outFile.write("    TEMPLATE '" + self.template + "'\n")
412
    if self.header != "":
413
      self.outFile.write("    HEADER '" + self.header + "'\n")
414
    if self.footer != "":
415
      self.outFile.write("    FOOTER '" + self.footer + "'\n")
416
    self.outFile.write("  END\n\n")
417
    return resultMsg
418

    
419
  # Write the map layers - we have to defer writing to disk so we
420
  # can invert the order of the layes, since they are opposite in QGIS 
421
  # compared to mapserver
422
  def writeMapLayers(self):
423
    resultMsg = ''
424
    # get the layers from the legend to be able to determine the order later
425
    legend_layers = self.qgs.getElementsByTagName("legendlayerfile")
426
    self.layer_order = list()
427
    for legend_layer in legend_layers:
428
        self.layer_order.append(legend_layer.getAttribute("layerid").encode('utf-8'))
429
    # get the list of maplayer nodes
430
    maplayers = self.qgs.getElementsByTagName("maplayer")
431
    print "Processing ", len(maplayers), " layers"
432
    count = 0
433
    layer_list = dict()
434
    layer_names = []
435
    for lyr in maplayers:
436
      count += 1
437
      print "Processing layer ", count
438
      # The attributes of the maplayer tag contain the scale dependent settings,
439
      # visibility, and layer type
440
      layer_def = "  LAYER\n"
441
      # store name of the layer - replace space with underscore for wms compliance
442
      layer_name = lyr.getElementsByTagName("layername")[0].childNodes[0].nodeValue.encode('utf-8').replace("\"", "").replace(" ","_")
443
      # layername is not unique in qgis, store id of layer
444
      layer_id = lyr.getElementsByTagName("id")[0].childNodes[0].nodeValue.encode('utf-8')
445
      # first check to see if there is a name
446
      if len(layer_name) > 0:
447
        # WMS layernames should be unique, so
448
        # if the layer_name already excists in our layer_list of names:
449
        if layer_name in layer_names:
450
          # we give it the old name plus number
451
          layer_name = layer_name + str(count)
452
      else:
453
        # if no name for the layer, manufacture one
454
        layer_name = 'layer' + str(count)
455
      # store the name to be able to check for double names
456
      layer_names.append(layer_name)
457
      layer_def += "    NAME '%s'\n" % layer_name
458

    
459
      if lyr.getAttribute("type").encode('utf-8') == 'vector':  
460
        layer_def += "    TYPE " + lyr.getAttribute("geometry").encode('utf-8').upper() + "\n"
461
      elif lyr.getAttribute("type").encode('utf-8') == 'raster':  
462
        layer_def += "    TYPE " + lyr.getAttribute("type").encode('utf-8').upper() + "\n"
463

    
464
      # Use (global) default value from the gui
465
      layer_def += "    DUMP " + self.dump + "\n"
466
      # id dump = true: add TEMPLATE to be able to use getFeatureInfoRequests
467
      if self.dump=="true":
468
        layer_def += "    TEMPLATE fooOnlyForWMSGetFeatureInfo\n"
469
        
470
      # Set min/max scales
471
      if lyr.getAttribute('hasScaleBasedVisibilityFlag').encode('utf-8') == 1:
472
        layer_def += "    MINSCALE " + lyr.getAttribute('minimumScale').encode('utf-8') + "\n"
473
        layer_def += "    MAXSCALE " + lyr.getAttribute('maximumScale').encode('utf-8') + "\n"
474

    
475
      layer_def += self.getExtentString()
476

    
477
      # data
478
      dataString = lyr.getElementsByTagName("datasource")[0].childNodes[0].nodeValue.encode('utf-8')
479

    
480
      # test if it is a postgis, grass or WMS layer
481
      # is there a better way to do this? probably.
482
      try:
483
        providerString = lyr.getElementsByTagName("provider")[0].childNodes[0].nodeValue.encode('utf-8')
484
      except:
485
        # if providerString is null
486
        providerString = ''
487

    
488
      if providerString == 'postgres':
489
        # it's a postgis layer
490
        uri = QgsDataSourceURI(dataString)
491
        layer_def += "    CONNECTIONTYPE postgis\n"
492
        connectionInfo = str(uri.connectionInfo())
493
        # if connectionInfo does NOT contain a password, warn user:
494
        if connectionInfo.find("password")<0:
495
          resultMsg += " ! No password in connection string for postgres layer '" +  layer_name + \
496
            "' \n  Add it, or make sure mapserver can connect to postgres.\n"  
497
        layer_def += "    CONNECTION \"" + connectionInfo + "\"\n"
498
        # EvdP: it seems that the uri.geometryColumn() is quoted automatically by PostGIS.
499
        # To prevent double quoting, we don't quote here.
500
        # Now we are unable to process uri.geometryColumn()s with special characters (uppercase... etc.)
501
        #layer_def += "    DATA '\"" + uri.geometryColumn() + "\" FROM " + uri.quotedTablename() + "'\n"
502
        #layer_def += "    DATA '" + uri.geometryColumn() + " FROM " + uri.quotedTablename() + "'\n"
503
        layer_id = lyr.getElementsByTagName("id")[0].childNodes[0].nodeValue.encode("utf-8")
504
        uniqueId = self.getPrimaryKey(layer_id, uri.table())
505
        # %tablename% is returned when no uniqueId is found: inform user
506
        if uniqueId.find("%") >= 0:
507
            resultMsg += " ! No primary key found for postgres layer '" +  layer_name + \
508
            "' \n  Make sure you edit the mapfile and change the DATA-string \n    containing '" + uniqueId + "'\n"
509
        epsg = self.getEpsg(lyr)
510
        
511
        layer_def += "    DATA '" + uri.geometryColumn() + " FROM " + uri.quotedTablename() + " USING UNIQUE " + uniqueId + " USING srid=" + epsg + "'\n"
512
        # don't write the filter keyword if there isn't one
513
        if uri.sql() != "":
514
          layer_def += "    FILTER ( " + uri.sql() + " )\n"
515

    
516
      elif providerString == 'wms' and lyr.getAttribute("type").encode('utf-8').upper() == 'RASTER':
517
        # it's a WMS layer 
518
        layer_def += "    CONNECTIONTYPE WMS\n"
519
        layer_def += "    CONNECTION '" + dataString + "'\n"
520
        rasterProp = lyr.getElementsByTagName("rasterproperties")[0]
521
        # loop thru wmsSubLayers  
522
        wmsSubLayers = rasterProp.getElementsByTagName('wmsSublayer')
523
        wmsNames = []
524
        wmsStyles = []
525
        for wmsLayer in wmsSubLayers: 
526
          wmsNames.append( wmsLayer.getElementsByTagName('name')[0].childNodes[0].nodeValue.encode('utf-8').replace("\"", "") )
527
          try: 
528
            wmsStyles.append( wmsLayer.getElementsByTagName('style')[0].childNodes[0].nodeValue.encode('utf-8') )
529
          except:
530
            wmsStyles.append( '' )
531
        # Create necesssary wms metadata
532
        format = rasterProp.getElementsByTagName('wmsFormat')[0].childNodes[0].nodeValue.encode('utf-8')
533
        layer_def += "    METADATA\n"
534
        layer_def += "      'ows_name' '" + ','.join(wmsNames) + "'\n"
535
        layer_def += "      'wms_server_version' '1.1.1'\n"
536
        try:
537
          #ct = lyr.getElementsByTagName('coordinatetransform')[0]
538
          #srs = ct.getElementsByTagName('sourcesrs')[0].getElementsByTagName('spatialrefsys')[0]
539
          #epsg = srs.getElementsByTagName('epsg')[0].childNodes[0].nodeValue.encode('utf-8')
540
          #layer_def += "      'wms_srs' 'EPSG:4326 EPSG:" + epsg + "'\n"
541
          layer_def += "      'ows_srs' 'EPSG:" + self.getEpsg(lyr) + "'\n"
542
          # TODO: add epsg to all METADATA tags??
543
        except:
544
          print "ERROR while trying to write ows_srs METADATA"
545
          pass
546
        layer_def += "      'wms_format' '" + format + "'\n"
547
        layer_def += "      'wms_style' '" + ','.join(wmsStyles) + "'\n"
548
        layer_def += "    END\n"
549

    
550
      else: 
551
        # its a standard ogr, gdal or grass layer
552
        layer_def += "    DATA '" + dataString + "'\n"
553
      
554
      # WMS settings for all layers
555
      layer_def += "    METADATA\n"
556
      layer_def += "      'ows_title' '" + layer_name + "'\n"
557
      layer_def += "    END\n"
558

    
559
      layer_def += "    STATUS OFF\n"
560

    
561
      # turn status in MapServer on or off based on visibility in QGis:
562
#      layer_id = lyr.getElementsByTagName("id")[0].childNodes[0].nodeValue.encode("utf-8")
563
#      legendLayerNode = self.legendlayerfileNodesById[layer_id]
564
#      if legendLayerNode.getAttribute("visible").encode("utf-8") == "1":
565
#        layer_def += "    STATUS ON\n"
566
#      else:
567
#        layer_def += "    STATUS OFF\n"
568

    
569
      opacity = int ( 100.0 * 
570
           float(lyr.getElementsByTagName("transparencyLevelInt")[0].childNodes[0].nodeValue.encode('utf-8')) / 255.0 ) 
571
      layer_def += "    TRANSPARENCY " + str(opacity) + "\n"
572

    
573
      layer_def += "    PROJECTION\n"
574
      
575
       # Get the destination srs for this layer and use it to create the projection section 
576
      destsrs = self.qgs.getElementsByTagName("destinationsrs")[0]  
577
      proj4Text = destsrs.getElementsByTagName("proj4")[0].childNodes[0].nodeValue.encode('utf-8')
578
      # TODO: you would think: take DATA-srs here, but often projected
579
      # shapefiles do not contain srs data ... If we want to do this
580
      # we should take make the map-srs the qgs-project srs first
581
      # instead of taking the srs of the first layer 
582
      # Get the data srs for this layer and use it to create the projection section 
583
      # datasrs = lyr.getElementsByTagName("srs")[0]  
584
      # proj4Text = datasrs.getElementsByTagName("proj4")[0].childNodes[0].nodeValue.encode('utf-8') 
585
      
586
      # the proj4 text string needs to be reformatted to make mapserver happy
587
      layer_def += self.formatProj4(proj4Text)
588
      layer_def += "    END\n"
589
      scaleDependent = lyr.getAttribute("hasScaleBasedVisibilityFlag").encode('utf-8')
590
      if scaleDependent == '1':
591
        # get the min and max scale settings
592
        minscale = lyr.getAttribute("minimumScale").encode('utf-8')
593
        maxscale = lyr.getAttribute("maximumScale").encode('utf-8')
594
        if minscale > '':
595
          layer_def += "    MINSCALE " + minscale + "\n"
596
        if maxscale > '':
597
          layer_def += "    MAXSCALE " + maxscale + "\n"
598
      # Check for label field (ie LABELITEM) and label status
599
      try:
600
        labelElements = lyr.getElementsByTagName("label")
601
        labelOn = '0'
602
        labelField = None
603
        # there are actually 3 different label-element in a layer element:
604
        for element in labelElements:
605
            labelParent = element.parentNode.localName
606
            if labelParent == 'maplayer':
607
                labelOn = element.childNodes[0].nodeValue.encode('utf-8')
608
            if labelParent == 'labelattributes':
609
                labelField = element.getAttribute('fieldname').encode('utf-8')
610
        if labelField != '' and labelField is not None and labelOn == "1" and labelOn is not None:
611
          layer_def += "    LABELITEM '" + labelField + "'\n"
612
      except:
613
        # no labels
614
        pass
615
    
616
      # write the CLASS section for rendering
617
      # First see if there is a single symbol renderer
618
      if lyr.getElementsByTagName("singlesymbol").length > 0:
619
        layer_def += self.simpleRenderer(lyr, lyr.getElementsByTagName("singlesymbol")[0].getElementsByTagName('symbol')[0] )
620
      elif lyr.getElementsByTagName("graduatedsymbol").length > 0:
621
        layer_def += self.graduatedRenderer(lyr, lyr.getElementsByTagName("graduatedsymbol")[0].getElementsByTagName('symbol')[0] )
622
      elif lyr.getElementsByTagName("continuoussymbol").length > 0:
623
        layer_def += self.continuousRenderer(lyr, lyr.getElementsByTagName("continuoussymbol")[0] )
624
      elif lyr.getElementsByTagName("uniquevalue").length > 0:
625
        layer_def += self.uniqueRenderer(lyr, lyr.getElementsByTagName("uniquevalue")[0].getElementsByTagName('symbol')[0] )
626

    
627
      # end of LAYER
628
      layer_def += "  END\n\n"
629

    
630
      # add the layer to the list with layer_id as key
631
      layer_list[layer_id] = layer_def
632
    # all layers have been processed, reverse the layer_order and write
633
    # output layer_def's in order as they appear in legend (as seen by user)
634
    self.layer_order.reverse()
635
    for layerid in self.layer_order:
636
      self.outFile.write(layer_list[layerid])
637
    return resultMsg
638

    
639

    
640
  def getEpsg(self, lyr):
641
    try:
642
      srs = lyr.getElementsByTagName('srs')[0].getElementsByTagName('spatialrefsys')[0]
643
      return srs.getElementsByTagName('srid')[0].childNodes[0].nodeValue.encode('utf-8')
644
    except:  
645
      #Use 4326 as a sensible default if the above fails
646
      return "4326"
647

    
648

    
649
  def getPrimaryKey(self, layerId, tableName):
650
    """
651
    Since we have no Python bindings for "src\providers\postgres\qgspostgresprovider.cpp"
652
    we approximate the primary key by finding an integer type field containing "fid" or "id"
653
    This is obviously a lousy solution at best.
654

655
    This script requires the project you export to be open in QGis!!
656
    
657
    This method will return either the primary key of this table, or
658
    the string %tablename% in case we're not able to find it...
659
    """
660

    
661
    mlr = QgsMapLayerRegistry.instance()
662
    layers = mlr.mapLayers()
663
    if not QString(layerId) in layers:
664
      # layerId of this postgis layer NOT in the layerlist... 
665
      # probably the project is not loaded in qgis 
666
      #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.")
667
      return str("%" + tableName + "_id%")
668
      
669
    layer = layers[QString(layerId)]
670
    dataProvider = layer.dataProvider()
671
    fields = dataProvider.fields()
672

    
673
    intTypes = [QVariant.Int, QVariant.LongLong, QVariant.UInt, QVariant.ULongLong]
674
    
675
    integerFields = []
676
    for id, field in fields.iteritems():
677
      if field.type() in intTypes:
678
        integerFields.append(id)
679

    
680
    # fid end
681
    fidIntegerFields = []
682
    for id in integerFields:
683
      if fields[id].name().endsWith("fid"):
684
        fidIntegerFields.append(id)
685

    
686
    if len(fidIntegerFields) == 1:
687
      return str(fields[fidIntegerFields[0]].name())
688

    
689
    # fid start
690
    fidIntegerFields[:] = []
691
    for id in integerFields:
692
      if fields[id].name().startsWith("fid"):
693
        fidIntegerFields.append(id)
694

    
695
    if len(fidIntegerFields) == 1:
696
      return str(fields[fidIntegerFields[0]].name())
697

    
698
    # id end
699
    idIntegerFields = []
700
    for id in integerFields:
701
      if fields[id].name().endsWith("id"):
702
        idIntegerFields.append(id)
703

    
704
    if len(idIntegerFields) == 1:
705
      return str(fields[idIntegerFields[0]].name())
706

    
707
    # id start
708
    idIntegerFields[:] = []
709
    for id in integerFields:
710
      if fields[id].name().startsWith("id"):
711
        idIntegerFields.append(id)
712

    
713
    if len(idIntegerFields) == 1:
714
      return str(fields[idIntegerFields[0]].name())
715

    
716
    # if we arrive here we have ambiguous or no primary keys
717
    #print "Error: Could not find primary key from field type and field name information.\n"
718

    
719
    # using a mapfile pre-processor, the proper id field can be substituted in the following:
720
    return str("%" + tableName + "_id%")
721

    
722

    
723
  # Get RGB code from a XML color node, returning string like '45 124 255'
724
  def getRgbFromNode(self, symbolNode, elementName):
725
    if symbolNode == None or symbolNode.getElementsByTagName(elementName).length == 0:
726
        return ''
727
    colorNode = symbolNode.getElementsByTagName(elementName)[0]
728
    return colorNode.getAttribute('red').encode('utf-8') + ' ' + colorNode.getAttribute('green').encode('utf-8') + ' ' + colorNode.getAttribute('blue').encode('utf-8')
729

    
730

    
731
  # Get a size (multiplied by symbolSizeMultiplier) from given sizeNode's child with elementName 
732
  def getSizeStringFromNode(self, symbolNode, elementName):
733
    if symbolNode == None or symbolNode.getElementsByTagName(elementName).length == 0:
734
        return ''
735
    size = float(symbolNode.getElementsByTagName(elementName)[0].childNodes[0].nodeValue.encode('utf-8'))
736
    symbolSize = size * symbolSizeMultiplier
737
    return str(symbolSize)
738

    
739

    
740
  def getSymbolProperty(self, symbolNode, elementName):
741
    if symbolNode == None or symbolNode.getElementsByTagName(elementName).length == 0:
742
        return ''
743
    return str(symbolNode.getElementsByTagName(elementName)[0].childNodes[0].nodeValue.encode('utf-8'))
744

    
745

    
746
  def writeClassStyleContent(self, symbolNode, geometry):
747
    outlinecolor = self.getRgbFromNode(symbolNode, 'outlinecolor')
748
    fillcolor    = self.getRgbFromNode(symbolNode, 'fillcolor')
749
    pointsize    = self.getSizeStringFromNode(symbolNode, 'pointsize')
750
    outlinewidth = self.getSizeStringFromNode(symbolNode, 'outlinewidth')
751
    fillpattern  = self.getSymbolProperty(symbolNode, 'fillpattern');
752

    
753
    class_def = "       STYLE\n"
754
    # for POINT by SYMBOL and SIZE
755
    if geometry == 'POINT':
756
        # use the point symbol map to lookup the mapserver symbol type
757
        symbol = self.msSymbol( geometry, symbolNode )
758
        class_def += "         SYMBOL " + symbol + " \n"
759
        class_def += "         SIZE " + pointsize + " \n"
760
    # for LINE and POLYGON size define by WIDTH
761
    else:
762
        class_def += "         WIDTH " + outlinewidth + " \n"
763

    
764
    # for LINE defined by COLOR from outlinecolor
765
    if geometry == 'LINE':
766
        class_def += "         COLOR " + outlinecolor + "\n"
767
    # for POLYGON and POINT defined by COLOR from fillcolor and OUTLINECOLOR from outlinecolor
768
    else:
769
        class_def += "         OUTLINECOLOR " + outlinecolor + "\n"
770
        if 'NoBrush' != fillpattern:
771
            class_def += "         COLOR " + fillcolor + "\n"
772

    
773
    class_def += "       END\n"
774
    return class_def
775

    
776

    
777
  # Simple renderer ouput
778
  # We need the layer node and symbol node
779
  def simpleRenderer(self, layerNode, symbolNode):
780
    # get the layers geometry type
781
    geometry = layerNode.getAttribute("geometry").encode('utf-8').upper()
782

    
783
    class_def = "    CLASS\n"
784

    
785
    class_def += "       NAME '" + layerNode.getElementsByTagName("layername")[0].childNodes[0].nodeValue.encode('utf-8').replace("\"", "") + "' \n"
786

    
787
    class_def += self.writeClassStyleContent(symbolNode, geometry)
788

    
789
    class_def += self.msLabel( layerNode ) 
790

    
791
    # end of CLASS  
792
    class_def += "    END\n"
793

    
794
    return class_def
795

    
796

    
797
  # Graduated symbol renderer output
798
  def graduatedRenderer(self, layerNode, symbolNode):
799
    # get the layers geometry type
800
    geometry = layerNode.getAttribute("geometry").encode('utf-8').upper()
801

    
802
    # get the renderer field for building up the classes
803
    classField = layerNode.getElementsByTagName('classificationattribute')[0].childNodes[0].nodeValue.encode('utf-8')  
804
    # write the render item
805
    class_def = "    CLASSITEM '" + classField + "'\n"
806

    
807
    # write the rendering info for each class
808
    classes = layerNode.getElementsByTagName('symbol')
809
    for cls in classes:
810
      class_def += "    CLASS\n"
811

    
812
      lower = cls.getElementsByTagName('lowervalue')[0].childNodes[0].nodeValue.encode('utf-8')
813
      upper = cls.getElementsByTagName('uppervalue')[0].childNodes[0].nodeValue.encode('utf-8')
814
    
815
      # If there's a label use it, otherwise autogenerate one
816
      try:
817
        label = cls.getElementsByTagName('label')[0].childNodes[0].nodeValue.encode('utf-8')
818
        class_def += "      NAME '" + label + "'\n"
819
      except: 
820
        class_def += "      NAME '" + lower + " < " + classField + " < " + upper + "'\n"
821

    
822
      class_def += "      EXPRESSION ( ([" + classField + "] >= " + lower + ") AND ([" + classField + "] <= " + upper + ") )\n"
823

    
824
      class_def += self.writeClassStyleContent(cls, geometry)
825

    
826
      # label
827
      class_def += self.msLabel( layerNode ) 
828

    
829
      # end of CLASS  
830
      class_def += "    END\n"
831

    
832
    return class_def
833

    
834

    
835
  # Continuous symbol renderer output
836
  def continuousRenderer(self, layerNode, symbolNode):
837
    # get the layers geometry type
838
    geometry = layerNode.getAttribute("geometry").encode('utf-8').upper()
839

    
840
    # get the renderer field for building up the classes
841
    classField = layerNode.getElementsByTagName('classificationattribute')[0].childNodes[0].nodeValue.encode('utf-8')  
842

    
843
    # write the rendering info for each class
844
    class_def = "    CLASS\n"
845

    
846
    # Class name irrelevant for color ramps since mapserver can't render their legend
847
    #self.outFile.write("      NAME '" + classField + "'\n")
848

    
849
    # color
850
    lower = symbolNode.getElementsByTagName('lowestsymbol')[0].getElementsByTagName('symbol')[0]
851
    upper = symbolNode.getElementsByTagName('highestsymbol')[0].getElementsByTagName('symbol')[0]
852
    lowerColor = lower.getElementsByTagName('outlinecolor')[0]
853
    upperColor = upper.getElementsByTagName('outlinecolor')[0]
854

    
855
    # outline color
856
    outlineNode = lower.getElementsByTagName('outlinecolor')[0]
857

    
858
    class_def += "      STYLE\n"
859
    
860
    # The first and last color of the ramp ( r g b r g b )
861
    class_def += "        COLORRANGE " + lowerColor.getAttribute('red').encode('utf-8') + " " + lowerColor.getAttribute('green').encode('utf-8') + " " + lowerColor.getAttribute('blue').encode('utf-8') + " " + upperColor.getAttribute('red').encode('utf-8') + " " + upperColor.getAttribute('green').encode('utf-8') + " " + upperColor.getAttribute('blue').encode('utf-8') + "\n"
862

    
863
    # The range of values over which to ramp the colors
864
    class_def += "        DATARANGE " + lower.getElementsByTagName('lowervalue')[0].childNodes[0].nodeValue.encode('utf-8') + ' ' + upper.getElementsByTagName('lowervalue')[0].childNodes[0].nodeValue.encode('utf-8') + '\n'
865

    
866
    class_def += "        RANGEITEM '" + classField + "'\n"
867

    
868

    
869
    # upper and lower have same size
870
    outlinecolor = self.getRgbFromNode(upper, 'outlinecolor')
871
    fillcolor    = self.getRgbFromNode(upper, 'fillcolor')
872
    outlinewidth = self.getSizeStringFromNode(upper, 'outlinewidth')
873

    
874
    size = float(lower.getElementsByTagName('pointsize')[0].childNodes[0].nodeValue.encode('utf-8'))
875
    pointsize = 2 * size * symbolSizeMultiplier
876

    
877

    
878
    # for POINT by SYMBOL and SIZE
879
    if geometry == 'POINT':
880
        # use the point symbol map to lookup the mapserver symbol type
881
        symbol = self.msSymbol( geometry, symbolNode )
882
        class_def += "         SYMBOL " + symbol + " \n"
883
        class_def += "         SIZE " + str(pointsize) + " \n"
884
    # for LINE and POLYGON size define by WIDTH
885
    else:
886
        class_def += "         WIDTH " + outlinewidth + " \n"
887

    
888
    # for LINE defined by COLOR from outlinecolor
889
    if geometry == 'LINE':
890
        class_def += "         COLOR " + outlinecolor + "\n"
891
    # for POLYGON and POINT defined by COLOR from fillcolor and OUTLINECOLOR from outlinecolor
892
    else:
893
        class_def += "         COLOR " + fillcolor + "\n"
894

    
895
    class_def += "      END\n"
896
    
897
    # only outlines for polygons
898
    outline = symbolNode.getElementsByTagName('polygonoutline')
899
    if geometry == 'POLYGON' and outline is not None and '1' == outline[0].childNodes[0].nodeValue.encode('utf-8'):
900
      class_def += "     STYLE\n"
901
      class_def += "       WIDTH 1 " + "\n"
902
      class_def += "       OUTLINECOLOR 0 0 0" + "\n"
903
      class_def += "     END\n"
904

    
905
    # label
906
    class_def +=  self.msLabel( layerNode )
907

    
908
    # end of CLASS  
909
    class_def += "    END\n"
910

    
911
    return class_def
912

    
913

    
914
  # Unique value renderer output
915
  def uniqueRenderer(self, layerNode, symbolNode):
916
    # get the renderer field for building up the classes
917
    classField = layerNode.getElementsByTagName('classificationattribute')[0].childNodes[0].nodeValue.encode('utf-8')  
918

    
919
    # get the layers geometry type
920
    geometry = layerNode.getAttribute("geometry").encode('utf-8').upper()
921
    
922
    # write the render item
923
    class_def = "    CLASSITEM '" + classField + "'\n"
924

    
925
    # write the rendering info for each class
926
    classes = layerNode.getElementsByTagName('symbol')
927
    for cls in classes:
928
      class_def += "    CLASS\n"
929

    
930
      try:
931
        lower = cls.getElementsByTagName('lowervalue')[0].childNodes[0].nodeValue.encode('utf-8')
932
      except IndexError:
933
        # set to blank in the case where the field used for rendering has no value
934
        lower = ""
935

    
936
      # If there's a label use it, otherwise autogenerate one
937
      try:
938
        label = cls.getElementsByTagName('label')[0].childNodes[0].nodeValue.encode('utf-8')
939
        class_def += '      NAME "' + label + '"\n'
940
      except:
941
        class_def += '      NAME "' + classField + ' = ' + lower + '" \n'
942

    
943
      class_def += '      EXPRESSION "' + lower + '" \n'
944

    
945
      class_def += self.writeClassStyleContent(cls, geometry)
946

    
947
      # label
948
      class_def +=  self.msLabel( layerNode )
949

    
950
      # end of CLASS  
951
      class_def += "    END\n"
952

    
953
    return class_def
954

    
955

    
956
  # Utility method to format a proj4 text string into mapserver format
957
  def formatProj4(self, proj4text):
958
    parms = proj4text.split(" ")
959
    ret = ""
960
    for p in parms:
961
      p = p.replace("+","")
962
      ret = ret + "    '" + p + "'\n"
963
    return ret
964

    
965

    
966
  def getProj4(self, proj4text):
967
    """Returns the proj4 string as a dictionary with key value pairs."""
968
    parms = proj4text.split(" ")
969
    ret = {}
970
    for p in parms:
971
      p = p.replace("+","")
972
      keyValue = p.split("=")
973

    
974
      key = keyValue[0]
975
      
976
      value = ""
977
      try:    value = keyValue[1]
978
      except: value = ""
979

    
980
      if key != "":
981
        ret[key] = value
982
    return ret
983

    
984

    
985
  # Determines the symbol name and adds it to the symbol queue
986
  def msSymbol(self, geometry, symbolNode):
987
    # contains the same markup for a layer regardless of type
988
    # so we infer a symbol type based on the geometry
989
    symbolName = ''
990
    symbol = '0'
991

    
992
    if geometry == 'POLYGON':
993
      #symbol = '0'
994
      pass
995
    elif geometry == 'LINE':
996
      #symbol = '0'
997
      pass
998
    elif geometry == 'POINT':
999
      try:
1000
        symbolName = qgis2map_symbol[symbolNode.getElementsByTagName('pointsymbol')[0].childNodes[0].nodeValue.encode('utf-8')]
1001
      except:
1002
        symbolName = "circle"
1003
      # make sure it's double quoted
1004
      symbol = "\"" + symbolName + "\""
1005

    
1006
# a symbol set in an external symbol.txt file is used; see comment on top of this file
1007
#    if symbolName == 'CIRCLE':
1008
#      self.symbolQueue['CIRCLE'] = """
1009
#      #Circle symbol
1010
#      SYMBOL
1011
#        NAME 'CIRCLE'
1012
#        TYPE ellipse
1013
#        FILLED true
1014
#        POINTS
1015
#          1 1
1016
#        END
1017
#      END """
1018
#
1019
#    if symbolName == 'TRIANGLE':
1020
#      self.symbolQueue['TRIANGLE'] = """
1021
#      SYMBOL
1022
#        NAME "TRIANGLE"
1023
#        TYPE vector
1024
#        FILLED true
1025
#        POINTS
1026
#          0 1
1027
#         .5 0
1028
#          1 1
1029
#          0 1
1030
#        END
1031
#      END """
1032

    
1033
    return symbol
1034

    
1035
  # Label block creation
1036
  def msLabel(self, layerNode):
1037
    # currently a very basic bitmap font
1038
    labelNode = layerNode.getElementsByTagName('labelattributes')[0]
1039
    #labelField = labelNode.getElementsByTagName('label')[0].getAttribute('field').encode('utf-8')
1040
    # why was the attribute 'field' and not 'fieldname'?
1041
    labelField = labelNode.getElementsByTagName('label')[0].getAttribute('fieldname').encode('utf-8')
1042
    if labelField != '' and labelField is not None:
1043
      labelBlock  = "     LABEL \n"
1044

    
1045
      # see comment at 'qgis2ms_fontset'
1046
      fontQgis = labelNode.getElementsByTagName('family')[0].getAttribute('name').encode('utf-8')
1047
      fontMs = ""
1048
      try:
1049
        fontMs = qgis2map_fontset[fontQgis]
1050
      except:
1051
        # we default to the first font in the fontset, if any are present
1052
        if len(qgis2map_fontset) > 0:
1053
          try:
1054
            fontMs = qgis2map_fontset["MS Shell Dialog 2"]
1055
          except:
1056
            sortedKeys = qgis2map_fontset.keys()
1057
            sortedKeys.sort()
1058
            fontMs = qgis2map_fontset[sortedKeys[0]]
1059
        else:
1060
          fontMs = ""
1061
          
1062
      bold = bool(int(labelNode.getElementsByTagName('bold')[0].getAttribute('on').encode("utf-8")))
1063
      italic = bool(int(labelNode.getElementsByTagName('italic')[0].getAttribute('on').encode("utf-8")))
1064

    
1065
      # "-bold" and "-italic" must correspond with the fontset file
1066
      # font can be both bold and italic
1067
      labelBlock += "      FONT " + fontMs
1068
      if bold:   labelBlock += "-bold"
1069
      if italic: labelBlock += "-italic" 
1070
      labelBlock += "\n"
1071

    
1072
      labelBlock += "      TYPE truetype\n"
1073

    
1074
      size = self.getFieldName(labelNode, 'size')
1075
      if size == "":
1076
        sizeNode = labelNode.getElementsByTagName('size')[0]
1077
        units = sizeNode.getAttribute("units").encode("utf-8")
1078
        sizeValue = int(sizeNode.getAttribute("value").encode("utf-8"))
1079
        # we must convert to px for use in the mapfile
1080
        sizePx = 11 # default
1081
        sizePx = sizeValue
1082
        #if units == "pt":   sizePx = int(sizeValue / 0.75)
1083
        # TODO: find appropriate conversion metric from map units to pixels
1084
        #elif unit == "mu":
1085
        #proj4Elem = labelNode.parentNode.getElementsByTagName("proj4")[0].childNodes[0]
1086
        #proj4str = proj4Elem.nodeValue.encode('utf-8')
1087
        #proj4Dict = self.getProj4(proj4str)
1088
        #for i,j in proj4Dict.iteritems():
1089
          #labelBlock += str(i) + ":: " + str(j) + "\n"
1090
        #
1091
        #sizePx = ?????  proj4Dict["units"] ??
1092
        # non-used unit types:
1093
        #elif unit == "em": sizePx = int(size * 16.0)
1094
        #elif unit == "px": sizePx = size
1095
        size = str(sizePx)
1096
      labelBlock += "      SIZE " + size + "\n"
1097

    
1098
      color = self.getFieldName(labelNode, 'color')
1099
      if color == "":
1100
        colorNode = labelNode.getElementsByTagName('color')[0]
1101
        r = int(colorNode.getAttribute("red"))
1102
        g = int(colorNode.getAttribute("green"))
1103
        b = int(colorNode.getAttribute("blue"))
1104
        color = str(r) + " " + str(g) + " " + str(b)
1105
      labelBlock += "      COLOR " + color + "\n"
1106
 
1107
      # Include label angle if specified
1108
      # Note that angles only work for truetype fonts
1109
      angle = self.getFieldName(labelNode, 'angle')
1110
      if angle == "":
1111
        angle = labelNode.getElementsByTagName('angle')[0].getAttribute('value').encode('utf-8')
1112
      labelBlock += "      ANGLE " + angle + "\n"
1113
     
1114
      # Include label buffer if specified
1115
      # Note that the buffer has different meaning in qgis vs mapserver
1116
      # mapserver just adds blank space around the label while
1117
      # qgis uses a fill color around the label
1118
      # Note that buffer only works for truetype fonts
1119
      buffer = labelNode.getElementsByTagName('buffersize')[0].getAttribute('value').encode('utf-8')
1120
      bufferon = labelNode.getElementsByTagName('bufferenabled')[0].getAttribute('on').encode('utf-8')
1121
      buffercolor= self.getRgbFromNode(labelNode, 'buffercolor')
1122
      print buffercolor
1123

    
1124
      if bufferon is not None and '1' == bufferon:
1125
        # not sure if we should use this BUFFER
1126
        #labelBlock += "      BUFFER " + buffer + "\n"
1127
        labelBlock += "      BACKGROUNDCOLOR " + buffercolor + "\n"
1128

    
1129
      # alignment in QGis corresponds to position in MapServer
1130
      alignment = labelNode.getElementsByTagName('alignment')[0].getAttribute('value').encode('utf-8')
1131
      try:
1132
        labelBlock += "      POSITION " + qgis2map_aligment2position[alignment] + "\n"
1133
      except:
1134
        # default to center if we encounter a nonsensical value
1135
        labelBlock += "      POSITION cc\n"
1136

    
1137
      #values from the gui:
1138
      labelBlock += "      FORCE " + self.force + "\n"
1139
      labelBlock += "      ANTIALIAS " + self.antialias + "\n"
1140
      labelBlock += "      PARTIALS " + self.partials + "\n"
1141

    
1142
      labelBlock += "     END \n"
1143
      return labelBlock
1144
    else:
1145
      return ''
1146

    
1147

    
1148
  def getFieldName(self, parentNode, nodeName):
1149
    """ Returns the fieldname-attribute-value of a nodeName with a given parentNode
1150
    as a string surrounded by brackets ('[' and ']') or
1151
    an empty string if the fieldname-attribute does not exist."""
1152
    try:
1153
      fieldname = parentNode.getElementsByTagName(nodeName)[0].getAttribute('fieldname').encode('utf-8')
1154
      if fieldname != "":
1155
        return "[" + fieldname + "]"
1156
      else:
1157
        return ""
1158
    except:
1159
      return ""
1160

    
1161