forked from openstreetmap/mapnik-stylesheets
-
Notifications
You must be signed in to change notification settings - Fork 0
/
generate_xml.py
executable file
·206 lines (170 loc) · 7.17 KB
/
generate_xml.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
#!/usr/bin/env python
import os
import re
import sys
import glob
import optparse
import platform
import tempfile
__version__ = '0.1.0'
REASONABLE_DEFAULTS = {
'epsg':'900913', # default osm2pgsql import srid
'world_boundaries':'world_boundaries', # relative path
'symbols':'symbols', # relative path
'prefix':'planet_osm', # default osm2pgsql table prefix
'extent':'-20037508,-19929239,20037508,19929239', # world in merc
'inc':'inc/*.template', # search path for inc templates to parse
'estimate_extent':'false',
'extent':'-20037508,-19929239,20037508,19929239',
}
def color_text(color, text):
if os.name == 'nt':
return text
return "\033[9%sm%s\033[0m" % (color,text)
class Params:
def __init__(self,params,accept_none):
self.params = params
self.accept_none = accept_none
self.missing = []
def blend_with_env(self,opts):
d = {}
for p in self.params:
env_var_name = 'MAPNIK_%s' % p.upper()
# first pull from passed options...
if not opts.get(p) is None:
d[p] = opts[p]
# then try to pull from environment settings
elif not os.environ.get(env_var_name) is None:
d[p] = os.environ[env_var_name]
# then assign any reasonable default values...
elif p in REASONABLE_DEFAULTS.keys():
d[p] = REASONABLE_DEFAULTS[p]
# if --accept-none is passed then we assume
# its a paramater that mapnik likely does not need
# and will ignore if it is an empty string (e.g. db values)
elif self.accept_none:
d[p] = ''
else:
self.missing.append(p)
return d
def generate_help_text(var,default):
if var == 'host':
return 'Set postgres database host %s' % default
elif var == 'port':
return 'Set postgres database host %s' % default
return "Set value of '%s' %s" % (var,default)
def serialize(xml,options):
try:
import mapnik
except:
try:
import mapnik2 as mapnik
except:
sys.exit(color_text(1,'Error: saving xml requires Mapnik python bindings to be installed'))
m = mapnik.Map(1,1)
if options.from_string:
mapnik.load_map_from_string(m,xml,True)
else:
mapnik.load_map(m,xml,True)
if options.output:
mapnik.save_map(m,options.output)
else:
if hasattr(mapnik,'mapnik_version') and mapnik.mapnik_version() >= 700:
print mapnik.save_map_to_string(m)
else:
sys.exit(color_text(1,'Minor error: printing XML to stdout requires Mapnik >=0.7.0, please provide a second argument to save the output to a file'))
def validate(params,parser):
if not os.path.exists(params['world_boundaries']):
parser.error("Directory '%s' used for param '%s' not found" % (params['world_boundaries'],'world_boundaries'))
supported_srs = [900913,4326]
if not int(params['epsg']) in supported_srs:
parser.error('Sorry only supported projections are: %s' % supported_srs)
if not params['estimate_extent'] == 'false':
params['extent'] = ''
# set up parser...
parser = optparse.OptionParser(usage="""%prog [template xml] <output xml> <parameters>
Full help:
$ %prog -h (or --help for possible options)
Read 'osm.xml' and modify '/inc' files in place, pass dbname and user, accept empty string for other options
$ %prog --dbname osm --user postgres --accept-none
Read template, save output xml, and pass variables as options
$ %prog osm.xml my_osm.xml --dbname spain --user postgres --host ''""", version='%prog ' + __version__)
if __name__ == '__main__':
# custom parse of includes directory if supplied...
if '--inc' in sys.argv:
idx = sys.argv.index('--inc')
if not len(sys.argv) > (idx+1) or '--' in sys.argv[idx+1]:
parser.error("--inc argument requires a path to a directory as an argument")
else:
search_path = os.path.join(sys.argv[idx+1],'*.template')
else:
search_path = REASONABLE_DEFAULTS['inc']
inc_dir = os.path.dirname(search_path)
parser.add_option('--inc', dest='inc', help="Includes dir (default: '%s')" % inc_dir )
parser.add_option('--accept-none', dest='accept_none', action='store_true', help="Interpret lacking value as unneeded")
if not os.path.exists(inc_dir):
parser.error("The 'inc' path you gave is bogus!")
# get all the includes
includes = glob.glob(search_path)
if not includes:
parser.error("Can't find include templates, please provide search path using '--inc' , currently using '%s'" % search_path)
p = re.compile('.*%\((\w+)\)s.*')
text = ''
for xml in includes:
text += file(xml,'rb').read()
# find all variables in template includes
matches = p.findall(text)
if not matches:
parser.error(color_text(1,"Can't properly parse out variables in include templates.\nMake sure they are all wrapped like '%(variable)s'"))
# look ahead and build up --help text...
p = Params(matches,accept_none=False)
blended = p.blend_with_env({})
c_opts = []
for var in matches:
if not var in c_opts:
msg = "(default: '%(" + var + ")s')"
if var in blended:
default = msg % blended
else:
default = ''#msg % {var:'None'}
help = generate_help_text(var,default)
parser.add_option('--%s' % var, dest=var,help=generate_help_text(var,default))
c_opts.append(var)
# now, actually run the tool...
(options, args) = parser.parse_args()
p = Params(matches,options.accept_none)
blended = p.blend_with_env(options.__dict__)
help_text = "\n\nNote: use --accept-none to pass blank values for other parameters "
if p.missing:
parser.error(color_text(1,"\nPlease provide the following parameters values (or set as env variables):\n%s" % ''.join([" --%s 'value' " % v for v in p.missing]) + help_text))
validate(blended,parser)
for xml in includes:
template = file(xml,'rb')
new_name = xml.replace('.template','')
new_file = file(new_name,'wb')
try:
new_file.write(template.read() % blended)
except ValueError, e:
parser.error(color_text(1,"\n%s (found in %s)" % (e,xml)))
template.close()
new_file.close()
options.output = None
options.from_string = False
template_xml = None
# accepting XML as stream...
if not sys.stdin.isatty():
template_xml = sys.stdin.read()
options.from_string = True
if len(args) > 0:
options.output = args[0]
elif len(args) == 0:
template_xml = None
options.output = None
else:
template_xml = args[0]
if len(args) > 1:
options.output = args[1]
if template_xml:
serialize(template_xml,options)
else:
print 'Include files written successfully! Pass the osm.xml file as an argument if you want to serialize a new version or test reading the XML'