import logging from xml.etree.ElementTree import TreeBuilder, tostring import urllib import urlparse from zipfile import ZipFile from sextante.core.SextanteUtils import SextanteUtils logger = logging.getLogger("gsconfig.support") FORCE_DECLARED = "FORCE_DECLARED" ## The projection handling policy for layers that should use coordinates ## directly while reporting the configured projection to clients. This should be ## used when projection information is missing from the underlying datastore. FORCE_NATIVE = "FORCE_NATIVE" ## The projection handling policy for layers that should use the projection ## information from the underlying storage mechanism directly, and ignore the ## projection setting. REPROJECT = "REPROJECT" ## The projection handling policy for layers that should use the projection ## information from the underlying storage mechanism to reproject to the ## configured projection. def url(base, seg, query=None): """ Create a URL from a list of path segments and an optional dict of query parameters. """ seg = (urllib.quote(s.strip('/')) for s in seg) if query is None or len(query) == 0: query_string = '' else: query_string = "?" + urllib.urlencode(query) path = '/'.join(seg) + query_string adjusted_base = base.rstrip('/') + '/' return urlparse.urljoin(adjusted_base, path) def xml_property(path, converter = lambda x: x.text): def getter(self): if path in self.dirty: return self.dirty[path] else: if self.dom is None: self.fetch() node = self.dom.find(path) return converter(self.dom.find(path)) if node is not None else None def setter(self, value): self.dirty[path] = value def delete(self): self.dirty[path] = None return property(getter, setter, delete) def bbox(node): if node is not None: minx = node.find("minx") maxx = node.find("maxx") miny = node.find("miny") maxy = node.find("maxy") crs = node.find("crs") crs = crs.text if crs is not None else None if (None not in [minx, maxx, miny, maxy]): return (minx.text, maxx.text, miny.text, maxy.text, crs) else: return None else: return None def string_list(node): if node is not None: return [n.text for n in node.findall("string")] def attribute_list(node): if node is not None: return [n.text for n in node.findall("attribute/name")] def key_value_pairs(node): if node is not None: return dict((entry.attrib['key'], entry.text) for entry in node.findall("entry")) def write_string(name): def write(builder, value): builder.start(name, dict()) if (value is not None): builder.data(value) builder.end(name) return write def write_bool(name): def write(builder, b): builder.start(name, dict()) builder.data("true" if b else "false") builder.end(name) return write def write_bbox(name): def write(builder, b): builder.start(name, dict()) bbox_xml(builder, b) builder.end(name) return write def write_string_list(name): def write(builder, words): builder.start(name, dict()) for w in words: builder.start("string", dict()) builder.data(w) builder.end("string") builder.end(name) return write def write_dict(name): def write(builder, pairs): builder.start(name, dict()) for k, v in pairs.iteritems(): builder.start("entry", dict(key=k)) builder.data(v) builder.end("entry") builder.end(name) return write class ResourceInfo(object): def __init__(self): self.dom = None self.dirty = dict() def fetch(self): self.dom = self.catalog.get_xml(self.href) def clear(self): self.dirty = dict() def refresh(self): self.clear() self.fetch() def serialize(self, builder): # GeoServer will disable the resource if we omit the tag, # so force it into the dirty dict before writing if hasattr(self, "enabled"): self.dirty['enabled'] = self.enabled for k, writer in self.writers.items(): if k in self.dirty: writer(builder, self.dirty[k]) def message(self): builder = TreeBuilder() builder.start(self.resource_type, dict()) self.serialize(builder) builder.end(self.resource_type) msg = tostring(builder.close()) return msg def prepare_upload_bundle(name, data): """GeoServer's REST API uses ZIP archives as containers for file formats such as Shapefile and WorldImage which include several 'boxcar' files alongside the main data. In such archives, GeoServer assumes that all of the relevant files will have the same base name and appropriate extensions, and live in the root of the ZIP archive. This method produces a zip file that matches these expectations, based on a basename, and a dict of extensions to paths or file-like objects. The client code is responsible for deleting the zip archive when it's done.""" #we ut the zip file in the sextante temp dir, so it is deleted at the end. f = SextanteUtils.getTempFilename('zip') zip_file = ZipFile(f, 'w') for ext, stream in data.iteritems(): fname = "%s.%s" % (name, ext) if (isinstance(stream, basestring)): zip_file.write(stream, fname) else: zip_file.writestr(fname, stream.read()) zip_file.close() return f def atom_link(node): if 'href' in node.attrib: return node.attrib['href'] else: l = node.find("{http://www.w3.org/2005/Atom}link") return l.get('href') def atom_link_xml(builder, href): builder.start("atom:link", { 'rel': 'alternate', 'href': href, 'type': 'application/xml', 'xmlns:atom': 'http://www.w3.org/2005/Atom' }) builder.end("atom:link") def bbox_xml(builder, box): minx, maxx, miny, maxy, crs = box builder.start("minx", dict()) builder.data(minx) builder.end("minx") builder.start("maxx", dict()) builder.data(maxx) builder.end("maxx") builder.start("miny", dict()) builder.data(miny) builder.end("miny") builder.start("maxy", dict()) builder.data(maxy) builder.end("maxy") if crs is not None: builder.start("crs", {"class": "projected"}) builder.data(crs) builder.end("crs")