278 lines
7.6 KiB
Python

# -*- coding: utf-8 -*-
"""
***************************************************************************
support.py
---------------------
Date : November 2012
Copyright : (C) 2012 by David Winslow
Email : dwins at opengeo dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************
"""
__author__ = 'David Winslow'
__date__ = 'November 2012'
__copyright__ = '(C) 2012, David Winslow'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import logging
from xml.etree.ElementTree import TreeBuilder, tostring
import urllib
import urlparse
from zipfile import ZipFile
from processing.tools.system import *
logger = logging.getLogger('gsconfig.support')
# 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_DECLARED = 'FORCE_DECLARED'
# The projection handling policy for layers that should use the
# projection information from the underlying storage mechanism directly,
# and ignore the projection setting.
FORCE_NATIVE = 'FORCE_NATIVE'
# The projection handling policy for layers that should use the
# projection information from the underlying storage mechanism to
# reproject to the configured projection.
REPROJECT = 'REPROJECT'
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 <enabled>
# 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 put the zip file in the Processing temp dir, so it is
# deleted at the end.
f = 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')