mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-27 00:33:48 -05:00
278 lines
7.6 KiB
Python
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')
|