Permalink
Browse files

Handle local overrides from a config file for datasources.

  • Loading branch information...
1 parent 4bb96f7 commit b7c9c34aa12fcb10368dcc79a6e12739e542f751 @ninowalker ninowalker committed Sep 26, 2010
Showing with 152 additions and 59 deletions.
  1. +8 −4 cascadenik-compile.py
  2. +2 −2 cascadenik/__init__.py
  3. +10 −5 cascadenik/compile.py
  4. +56 −25 cascadenik/sources.py
  5. +2 −2 example.cfg
  6. +4 −1 example_dscfg.mml
  7. +70 −20 test.py
View
@@ -28,8 +28,7 @@ def main(src_file, dest_file, **kwargs):
mmap = mapnik.Map(1, 1)
# allow [zoom] filters to work
mmap.srs = '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null'
- load_kwargs = {'move_local_files': kwargs.get('move_local_files', None),
- 'target_dir': dirname(realpath(dest_file))}
+ load_kwargs = dict([(k, v) for (k, v) in kwargs.items() if k in ('target_dir', 'move_local_files', 'verbose', 'datasources_local_cfg')])
cascadenik.load_map(mmap, src_file, **load_kwargs)
(handle, tmp_file) = tempfile.mkstemp(suffix='.xml', prefix='cascadenik-mapnik-')
@@ -48,7 +47,7 @@ def main(src_file, dest_file, **kwargs):
parser = optparse.OptionParser(usage="""%prog [options] <mml> <xml>""", version='%prog ' + cascadenik.VERSION)
-parser.set_defaults(move_local_files=False, no_cache=False, pretty=True, safe_urls=False, verbose=False)
+parser.set_defaults(move_local_files=False, no_cache=False, pretty=True, safe_urls=False, verbose=False, datasources_local_cfg=None)
#parser.add_option('-d', '--dir', dest='target_dir',
# help='Write file-based resources (symbols, shapefiles, etc) to this target directory (default: current working directory is used)')
@@ -79,9 +78,14 @@ def main(src_file, dest_file, **kwargs):
parser.add_option('--mapnik-version',dest='mapnik_version_string',
help='The Mapnik version to target (default is 0.7.1 if not able to be autodetected)')
+parser.add_option('-l' , '--locals', dest='datasources_local_cfg',
+ help='Use the specified .cfg file to provide local overrides to datasources and variables',
+ type="string")
+
+
if __name__ == '__main__':
(options, args) = parser.parse_args()
-
+
if not args:
parser.error('Please specify .mml and .xml files')
@@ -18,7 +18,7 @@
__all__ = ['compile','_compile','style','stylesheet_declarations']
-def load_map(map, input, target_dir=None, move_local_files=False):
+def load_map(map, input, target_dir=None, move_local_files=False, datasources_local_cfg=None, verbose=False):
"""
"""
- compile(input, target_dir=target_dir, move_local_files=move_local_files).to_mapnik(map)
+ compile(input, target_dir=target_dir, move_local_files=move_local_files, datasources_local_cfg=datasources_local_cfg, verbose=verbose).to_mapnik(map)
View
@@ -562,7 +562,7 @@ def fetch_embedded_or_remote_src(elem, base):
return None, None
-def expand_source_declarations(map_el, base):
+def expand_source_declarations(map_el, base, local_conf):
""" This provides mechanism for externalizing and sharing data sources. The datasource configs are
python files, and layers reference sections within that config:
@@ -575,7 +575,7 @@ def expand_source_declarations(map_el, base):
- ds = sources.DataSources()
+ ds = sources.DataSources(base, local_conf)
# build up the configuration
for spec in map_el.findall('DataSourcesConfig'):
@@ -584,8 +584,8 @@ def expand_source_declarations(map_el, base):
if not src_text:
continue
- ds.add_config(src_text, local_base)
-
+ ds.add_config(src_text, local_base)
+
# now transform the xml
# add in base datasources
@@ -1381,6 +1381,11 @@ def compile(src,**kwargs):
pretty:
If True, XML output will be fully indented (otherwise indenting is haphazard).
+
+ datasources_local_cfg:
+ If a file or URL, uses the config to override datasources or parameters (i.e. postgis_dbname)
+ defined in the map's canonical <DataSourcesConfig> entities. This is most useful in development,
+ whereby one redefines individual datasources, connection parameters, and/or local paths.
mapnik_version:
The Mapnik release to target for optimal stylesheet compatibility.
@@ -1469,7 +1474,7 @@ def compile(src,**kwargs):
base = src
- expand_source_declarations(map_el, base)
+ expand_source_declarations(map_el, base, kwargs.get('datasources_local_cfg'))
declarations = extract_declarations(map_el, base)
# a list of layers and a sequential ID generator
View
@@ -1,31 +1,56 @@
import ConfigParser
import StringIO
import mapnik
+import urlparse
+import urllib
class DataSources(object):
- def __init__(self):
+ def __init__(self, base, local_cfg):
self.templates = set([])
- self.parser = None
self.sources = {}
+ self.defaults = {}
+ self.local_cfg_data = None
+ self.local_cfg_url = None
+ self.finalized = False
+
+ # avoid circular import
+ import compile
+ self.msg = compile.msg
+
+ # if a local_cfg is provided, we want to get the defaults first, before reading any other
+ # configuration.
+ if local_cfg:
+ self.local_cfg_url = urlparse.urljoin(base, local_cfg)
+ self.msg("Using local datasource config: %s" % self.local_cfg_url)
+ self.set_local_cfg_data(urllib.urlopen(self.local_cfg_url).read().decode(compile.DEFAULT_ENCODING))
+
+ def set_local_cfg_data(self, data):
+ self.local_cfg_data = data
+ locals = OverrideConfigParser({})
+ locals.loads(data)
+ self.defaults = locals.defaults()
+
+ def finalize(self):
+ if self.local_cfg_data:
+ self.msg("Loading local config data: %s" % self.local_cfg_url)
+ self.add_config(self.local_cfg_data, self.local_cfg_url)
+ self.finalized = True
def get(self, name):
+ if not self.finalized:
+ self.finalize()
return self.sources.get(name)
def add_config(self, textdata, filename):
- data = StringIO.StringIO(textdata)
- data.seek(0)
- if self.parser:
- self.parser = ChainedConfigParser(self.parser)
- else:
- self.parser = ConfigParser.SafeConfigParser()
- self.parser.readfp(data)
+ parser = OverrideConfigParser(self.defaults)
+ parser.loads(textdata)
- for sect in self.parser.sections():
+ for sect in parser.sections():
options = {}
name = sect
- dtype = self.parser.get(sect,"type") if self.parser.has_option(sect, "type") else None
- template = self.parser.get(sect,"template") if self.parser.has_option(sect, "template") else None
- layer_srs = self.parser.get(sect,"layer_srs") if self.parser.has_option(sect, "layer_srs") else None
+ dtype = parser.get(sect,"type") if parser.has_option(sect, "type") else None
+ template = parser.get(sect,"template") if parser.has_option(sect, "template") else None
+ layer_srs = parser.get(sect,"layer_srs") if parser.has_option(sect, "layer_srs") else None
# this layer declares a template template
if template:
@@ -36,8 +61,8 @@ def add_config(self, textdata, filename):
layer_srs = self.sources[template].get('layer_srs', layer_srs)
else:
# TODO catch section missing errors
- dtype = self.parser.get(template, 'type')
- layer_srs = self.parser.get(template, 'layer_srs') if self.parser.has_option(template, 'layer_srs') else layer_srs
+ dtype = parser.get(template, 'type')
+ layer_srs = parser.get(template, 'layer_srs') if parser.has_option(template, 'layer_srs') else layer_srs
# handle the most common projections
if layer_srs and layer_srs.lower().startswith("epsg:"):
@@ -63,13 +88,13 @@ def add_config(self, textdata, filename):
opt_value = None
try:
if option_type == int:
- opt_value = self.parser.getint(sect,option)
+ opt_value = parser.getint(sect,option)
elif option_type == float:
- opt_value = self.parser.getfloat(sect,option)
+ opt_value = parser.getfloat(sect,option)
elif option_type == bool:
- opt_value = self.parser.getboolean(sect,option)
+ opt_value = parser.getboolean(sect,option)
else:
- opt_value = self.parser.get(sect,option)
+ opt_value = parser.get(sect,option)
except ConfigParser.NoOptionError:
pass
except ValueError, e:
@@ -145,10 +170,16 @@ def add_config(self, textdata, filename):
for v in XML_OPTIONS.values():
v.update(dict(type=str, estimate_extent=bool, extent=str))
-class ChainedConfigParser(ConfigParser.SafeConfigParser):
- def __init__(self, last):
- ConfigParser.SafeConfigParser.__init__(self)
- d = last.defaults()
- d.update(self._defaults)
- self._defaults = d
+class OverrideConfigParser(ConfigParser.SafeConfigParser):
+ def __init__(self, overrides):
+ ConfigParser.SafeConfigParser.__init__(self, overrides)
+ self.overrides = overrides
+
+ def loads(self, textdata):
+ data = StringIO.StringIO(textdata)
+ data.seek(0)
+ self.readfp(data)
+ def get(self, section, option):
+ return ConfigParser.SafeConfigParser.get(self, section, option, vars=self.overrides)
+
View
@@ -1,7 +1,7 @@
[DEFAULT]
-srsWGS84=+proj=latlong +ellps=WGS84 +datum=WGS84 +no_defs
+example_srs=+proj=latlong +ellps=WGS84 +datum=WGS84 +no_defs
[world_land]
type=shape
file=http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/110m/physical/110m-land.zip
-source_srs=%(srsWGS84)s
+source_srs=%(example_srs)s
View
@@ -7,10 +7,13 @@
<DataSourcesConfig src="example.cfg" />
<DataSourcesConfig><![CDATA[
+[DEFAULT]
+my_srs = epsg:4326
+
[world_admin0]
type=shape
file=http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/110m/cultural/110m-admin-0-countries.zip
-source_srs=%(srsWGS84)s
+layer_srs=%(my_srs)s
]]> </DataSourcesConfig>
<!-- Layers -->
Oops, something went wrong.

0 comments on commit b7c9c34

Please sign in to comment.