This repository has been archived by the owner on Mar 21, 2024. It is now read-only.
/
sldGen.py
executable file
·328 lines (296 loc) · 8.71 KB
/
sldGen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
Script for mapserver generating SLD including complete filters based on mapfile.
Prints result to commandline.
Jarle Johan Pedersen, Norwegian Mapping Authorities 2013
Still buggy. Please check/validate the results.
'''
import mapscript
import sys
import tempfile
from pyparsing import nestedExpr
import os
# sld-library should probably be used at a later point
#import sld
# Static lists
# SQL not equal
neList=['!=','ne']
# SQL less than
ltList=['<','lt']
# SQL greater than
gtList=['>','gt']
# SQL less than or equal
leList=['<=','le']
# SQL greater than or equal
geList=['>=','ge']
# Global variables. Need to be removed.
service=''
greyscale=False
filePerLayer=False
outputDir=''
#outputFile=''
def grayscaler(line):
colorIndex=line.find('#')
red_v=line[colorIndex+1:colorIndex+3]
blue_v=line[colorIndex+3:colorIndex+5]
green_v=line[colorIndex+5:colorIndex+7]
red_m=0.299
green_m=0.587
blue_m=0.114
grey=str(hex(int(int(red_v,16)*red_m+int(green_v,16)*green_m+int(blue_v,16)*blue_m))).replace('0x','')
if len(grey)<2:
grey='0'+grey
outputFile.write(line.replace('#'+red_v+blue_v+green_v,'#'+grey+grey+grey))
def nestLoopCol(nList,ll):
'''Secondary loop-handler'''
try:
for n in nList:
nestLoop(n,ll)
except Exception, e:
print e
pass
def nestLoop(nList, ll):
'''
Main loop for translating SQL to FES
'''
if isinstance(nList,str):
return
else:
if len(nList)>1:
if str(nList[1]) in ['and','or']:
ll+=1
tmpAndor='And'
if str(nList[1])=='or':
tmpAndor='Or'
outputFile.write(tabberAndor(ll) + '<ogc:' + tmpAndor + '>\n')
nestLoopCol(nList,ll)
outputFile.write(tabberAndor(ll) + '</ogc:' + tmpAndor + '>\n')
del nList[1]
else:
if len(nList)>1:
tmpOperator='PropertyIsEqualTo'
if (nList[1] in neList):
tmpOperator='PropertyIsNotEqualTo'
elif (nList[1] in gtList):
tmpOperator='PropertyIsGreaterThan'
elif (nList[1] in ltList):
tmpOperator='PropertyIsLessThan'
elif (nList[1] in geList):
tmpOperator='PropertyIsGreaterThanOrEqualTo'
elif (nList[1] in leList):
tmpOperator='PropertyIsLessThanOrEqualTo'
outputFile.write(tabber(ll+1) + '<ogc:' + tmpOperator + '>\n')
for n in range(0,3):
if n == 0:
outputFile.write(tabber(ll+2) + '<ogc:PropertyName>' + str(nList[n]).replace('"','').replace("'","") + '</ogc:PropertyName>\n')
elif n == 2:
try:
outputFile.write(tabber(ll+2) + '<ogc:Literal>' + str(nList[n]).replace("'","").replace('"','') + '</ogc:Literal>\n')
except:
outputFile.write(tabber(ll+2) + '<ogc:Literal></ogc:Literal>\n')
outputFile.write(tabber(ll+1) + '</ogc:' + tmpOperator + '>\n')
else:
nestLoopCol(nList,ll)
def tabberAndor(counter):
'''Handles tabulation of and/or'''
tmpTab=""
while counter>=0:
tmpTab+=' '
counter-=1
tmpTab+=''
return tmpTab
def tabber(counter):
'''Handles general tabulation'''
tmpTab=""
while counter>=0:
tmpTab+=' '
counter-=1
tmpTab+=' '
return tmpTab
def parenthesis(string):
'''
Refactors the SQL and inserts missing parenthesis
Starts nestLoop
'''
string=string.replace(' and ', ') and (')
string=string.replace('&&', ') and (')
string=string.replace(' AND ', ') and (')
string=string.replace(' or ', ') or (')
string=string.replace('||', ') or (')
string=string.replace(' OR ', ') or (')
string=string.replace('[', '')
string=string.replace(']', '')
string='('+string+')'
mathExpr=nestedExpr()
loopLevel=0
# if debug=='True':
# print string
outputFile.write('<ogc:Filter>\n')
nestLoop(mathExpr.parseString(string),0)
outputFile.write('</ogc:Filter>\n')
def expressionSplitter(c,e):
tmpString="("
for value in e.split('|'):
tmpString=tmpString+ '(' + c + ' = ' + value.replace('/','') + ') OR '
return tmpString.rstrip(' OR ') + ')'
def cfe(c,f,e):
'''Initiates parsing when classification is a combination of Classitem, Filter and Expression'''
if '[' not in e:
if e[0]=='/':
parenthesis('(('+ f.replace('"','') +') AND (' + expressionSplitter(c,e) + '))')
else:
parenthesis('(('+ f.replace('"','') +') AND (' + e + '))')
else:
fe(f,e)
def ce(c,e):
'''Initiates parsing when classification is a combination of Classitem and Expression'''
if '[' not in e:
if e[0]=='/':
parenthesis(expressionSplitter(c,e))
else:
parenthesis('(' + c + ' = ' + e + ')')
else:
expression(e)
def fe(f,e):
'''Initiates parsing when classification is a combination of Filter and Expresssion'''
parenthesis('((' + f.replace('"','') + ') AND (' + e + '))')
def f(f):
'''Initiates parsing when classification is only Filter'''
parenthesis('(' + f.replace('"','') + ')')
def expression(e):
'''Initiates parsing when classification is only Expression'''
parenthesis('(('+e+'))')
def layerWriter(layer, layerNr):
'''Writes and opens initial SLD as generated from mapserver'''
layerClassItem=''
filterString=''
layerClassNr=0
if layer.getFilterString() is not None:
filterString=layer.getFilterString()
if layer.classitem is not None:
layerClassItem=layer.classitem
tmpSldFile=tempfile.NamedTemporaryFile()
tmpSldFile.write(layer.generateSLD())
sldFile=open(tmpSldFile.name)
tmpSldFile.close()
ruleBool=False
for tmpKlasse in range(0,layer.numclasses):
layerClass=layer.getClass(layerClassNr)
if layerClass is None:
return
# Comment out this to get more layers. Might look ugly.
elif layerClass.numstyles == 0:
return
for line in sldFile:
layerClass=layer.getClass(layerClassNr)
if (line.startswith('<ogc:Filter')):
layerClassWriter(layer, filterString, layerClassItem, layerClassNr)
layerClassNr+=1
continue
elif line.startswith('<NamedLayer>'):
outputFile.write(line)
outputFile.write('<!--\n')
if layer.minscaledenom > 0:
outputFile.write('Hardcoded minscale for layer: ' + str(layer.minscaledenom) + '\n')
else:
outputFile.write('No minscale set for this layer\n')
if layer.maxscaledenom > 0:
outputFile.write('Hardcoded maxscale for layer: ' + str(layer.maxscaledenom) + '\n')
else:
outputFile.write('No maxscale set for this layer\n')
outputFile.write('-->\n')
elif (layerNr > 0) & (line.startswith('<StyledLayerDescriptor')) & (filePerLayer==False):
continue
elif line.startswith('</StyledLayerDescriptor') & (filePerLayer==False):
continue
elif greyscale and line.startswith('<CssParameter'):
if ('-width' in line) or ('-dasharray' in line) or ('font' in line) or ('opacity' in line):
outputFile.write(line)
continue
else:
grayscaler(line)
else:
outputFile.write(line)
def layerClassWriter(layer, filterString, layerClassItem, layerClassNr):
'''Selects the method needed for writing the class based on the classifications'''
layerClass=layer.getClass(layerClassNr)
if layerClass is None:
return
if layerClassItem is not '':
if filterString is not '':
cfe(layerClassItem, filterString,layerClass.getExpressionString())
else:
try:
ce(layerClassItem,layerClass.getExpressionString())
except:
pass
elif filterString is not '':
if layerClass.getExpressionString() is not None:
fe(filterString,layerClass.getExpressionString())
else:
f(filterString)
else:
if layerClass.getExpressionString() is not None:
try:
expression(layerClass.getExpressionString())
except Exception, ex:
print ex
pass
else:
return
# return
def loadMap(mapfile):
'''Loads mapfile'''
try:
map=mapscript.mapObj(mapfile)
return map
except Exception, e:
print(e)
sys.exit()
def run(mapfile):
'''Initiates SLD-generation per layer'''
global outputFile
map=loadMap(mapfile)
serviceName=map.name
nl=''
dbtype='postgis'
for layerNr in range(0,map.numlayers):
layer=map.getLayer(layerNr)
# if layer.type==4:
# continue
if filePerLayer:
outputFile=open(outputDir + '/' + layer.name + '.sld','w')
layerWriter(layer, layerNr)
outputFile.close()
else:
layerWriter(layer, layerNr)
if not filePerLayer:
outputFile.write('</StyledLayerDescriptor>')
# Fetching mapfile from commandline
if len(sys.argv) > 1:
mapfile=sys.argv[1]
if mapfile is not None:
if '-g' in sys.argv:
greyscale=True
if '-f' in sys.argv:
outputFile=open(sys.argv[sys.argv.index('-f')+1],'w')
run(mapfile)
outputFile.close()
elif '-fpl' in sys.argv:
filePerLayer=True
outputDir=sys.argv[sys.argv.index('-fpl')+1]
if not os.path.exists(outputDir):
os.makedirs(outputDir)
run(mapfile)
else:
print('')
print('Usage: ./sldGen.py $mapfile [[ -f $sldFile || -fpl $outputDir ] -g ]')
print('')
print('Required: (one or the other)')
print(' -f Filename to save sld to')
print(' -fpl Name of directory to save sld-files (one per NamedLayer)')
print('')
print('Optional:')
print(' -g Toggle greyscale')
print('')