mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
added first draft of geoserver tools nviz is back in grass tools
This commit is contained in:
parent
a4e4999e57
commit
983535fa58
4
.gitignore
vendored
4
.gitignore
vendored
@ -45,4 +45,6 @@ qgis-test.ctest
|
||||
i18n/*.qm
|
||||
.project
|
||||
.pydevproject
|
||||
.idea
|
||||
.idea
|
||||
/python/plugins/sextante/resources_rc.py
|
||||
/python/plugins/sextante/about/ui_aboutdialogbase.py
|
||||
|
@ -16,6 +16,7 @@
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.servertools.GeoServerToolsAlgorithmProvider import GeoServerToolsAlgorithmProvider
|
||||
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
@ -130,6 +131,7 @@ class Sextante:
|
||||
Sextante.addProvider(GrassAlgorithmProvider())
|
||||
Sextante.addProvider(ScriptAlgorithmProvider())
|
||||
Sextante.addProvider(TauDEMAlgorithmProvider())
|
||||
Sextante.addProvider(GeoServerToolsAlgorithmProvider())
|
||||
Sextante.modeler.initializeSettings();
|
||||
#and initialize
|
||||
SextanteLog.startLogging()
|
||||
|
@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
DatabaseToolProvider.py
|
||||
---------------------
|
||||
Date : October 2012
|
||||
Copyright : (C) 2012 by Victor Olaya
|
||||
Email : volayaf at gmail 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__ = 'Victor Olaya'
|
||||
__date__ = 'October 2012'
|
||||
__copyright__ = '(C) 2012, Victor Olaya'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
from sextante.core.AlgorithmProvider import AlgorithmProvider
|
||||
from PyQt4 import QtGui
|
||||
import os
|
||||
|
||||
class DatabaseToolsAlgorithmProvider(AlgorithmProvider):
|
||||
|
||||
def __init__(self):
|
||||
AlgorithmProvider.__init__(self)
|
||||
self.alglist = []#PostGISSQL(), ImportIntoPostGIS(), CreateTable()]
|
||||
|
||||
def initializeSettings(self):
|
||||
AlgorithmProvider.initializeSettings(self)
|
||||
|
||||
|
||||
def unload(self):
|
||||
AlgorithmProvider.unload(self)
|
||||
|
||||
|
||||
def getName(self):
|
||||
return "database"
|
||||
|
||||
def getDescription(self):
|
||||
return "Database tools"
|
||||
|
||||
def getIcon(self):
|
||||
return QtGui.QIcon(os.path.dirname(__file__) + "/../images/postgis.png")
|
||||
|
||||
def _loadAlgorithms(self):
|
||||
self.algs = self.alglist
|
||||
|
||||
def supportsNonFileBasedOutput(self):
|
||||
return True
|
0
python/plugins/sextante/database/__init__.py
Normal file
0
python/plugins/sextante/database/__init__.py
Normal file
@ -16,6 +16,7 @@
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.parameters.ParameterString import ParameterString
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'August 2012'
|
||||
@ -34,6 +35,7 @@ class translate(GeoAlgorithm):
|
||||
|
||||
INPUT = "INPUT"
|
||||
OUTPUT = "OUTPUT"
|
||||
EXTRA = "EXTRA"
|
||||
|
||||
def getIcon(self):
|
||||
filepath = os.path.dirname(__file__) + "/icons/translate.png"
|
||||
@ -43,14 +45,18 @@ class translate(GeoAlgorithm):
|
||||
self.name = "translate"
|
||||
self.group = "Conversion"
|
||||
self.addParameter(ParameterRaster(translate.INPUT, "Input layer", False))
|
||||
self.addParameter(ParameterString(translate.EXTRA, "Additional creation parameters"))
|
||||
self.addOutput(OutputRaster(translate.OUTPUT, "Output layer"))
|
||||
|
||||
def processAlgorithm(self, progress):
|
||||
commands = ["gdal_translate"]
|
||||
commands.append("-of")
|
||||
out = self.getOutputValue(translate.OUTPUT)
|
||||
extra = self.getOutputValue(translate.EXTRA)
|
||||
commands.append(GdalUtils.getFormatShortNameFromFilename(out))
|
||||
commands.append(extra)
|
||||
commands.append(self.getParameterValue(translate.INPUT))
|
||||
commands.append(out)
|
||||
|
||||
|
||||
GdalUtils.runGdal(commands, progress)
|
||||
|
@ -16,6 +16,7 @@
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.grass.nviz import nviz
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'August 2012'
|
||||
@ -37,10 +38,6 @@ class GrassAlgorithmProvider(AlgorithmProvider):
|
||||
|
||||
def __init__(self):
|
||||
AlgorithmProvider.__init__(self)
|
||||
#=======================================================================
|
||||
# self.actions.append(DefineGrassRegionAction())
|
||||
# self.actions.append(DefineGrassRegionFromLayerAction())
|
||||
#=======================================================================
|
||||
self.createAlgsList() #preloading algorithms to speed up
|
||||
|
||||
def initializeSettings(self):
|
||||
@ -76,7 +73,7 @@ class GrassAlgorithmProvider(AlgorithmProvider):
|
||||
SextanteLog.addToLog(SextanteLog.LOG_ERROR, "Could not open GRASS algorithm: " + descriptionFile)
|
||||
except Exception,e:
|
||||
SextanteLog.addToLog(SextanteLog.LOG_ERROR, "Could not open GRASS algorithm: " + descriptionFile)
|
||||
#self.preloadedAlgs.append(nviz())
|
||||
self.preloadedAlgs.append(nviz())
|
||||
|
||||
def _loadAlgorithms(self):
|
||||
self.algs = self.preloadedAlgs
|
||||
|
@ -54,7 +54,6 @@ from sextante.outputs.OutputHTML import OutputHTML
|
||||
from sextante.outputs.OutputRaster import OutputRaster
|
||||
from sextante.outputs.OutputTable import OutputTable
|
||||
from sextante.outputs.OutputVector import OutputVector
|
||||
from sextante.outputs.OutputNumber import OutputNumber
|
||||
from sextante.parameters.ParameterString import ParameterString
|
||||
|
||||
class ParametersPanel(QtGui.QWidget):
|
||||
|
BIN
python/plugins/sextante/images/geoserver.png
Normal file
BIN
python/plugins/sextante/images/geoserver.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 744 B |
@ -16,6 +16,7 @@
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.outputs.OutputString import OutputString
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'August 2012'
|
||||
@ -436,6 +437,8 @@ class ModelerAlgorithm(GeoAlgorithm):
|
||||
return "output html"
|
||||
elif isinstance(out, OutputNumber):
|
||||
return "output number"
|
||||
elif isinstance(out, OutputString):
|
||||
return "output string"
|
||||
|
||||
|
||||
def getAsPythonCode(self):
|
||||
|
@ -64,10 +64,14 @@ class ModelerArrowItem(QtGui.QGraphicsLineItem):
|
||||
return self.myEndItem
|
||||
|
||||
def boundingRect(self):
|
||||
extra = (self.pen().width() + 20) / 2.0
|
||||
p1 = self.line().p1()
|
||||
p2 = self.line().p2()
|
||||
return QtCore.QRectF(p1, QtCore.QSizeF(p2.x() - p1.x(), p2.y() - p1.y())).normalized().adjusted(-extra, -extra, extra, extra)
|
||||
#this is a quick fix to avoid arrows not being drawn
|
||||
return QtCore.QRectF(0, 0, 4000,4000)
|
||||
#=======================================================================
|
||||
# extra = (self.pen().width() + 20) / 2.0
|
||||
# p1 = self.line().p1()
|
||||
# p2 = self.line().p2()
|
||||
# return QtCore.QRectF(p1, QtCore.QSizeF(p2.x() - p1.x(), p2.y() - p1.y())).normalized().adjusted(-extra, -extra, extra, extra)
|
||||
#=======================================================================
|
||||
|
||||
def shape(self):
|
||||
path = super(ModelerArrowItem, self).shape()
|
||||
@ -123,3 +127,4 @@ class ModelerArrowItem(QtGui.QGraphicsLineItem):
|
||||
|
||||
painter.drawLine(line)
|
||||
painter.drawPolygon(self.arrowHead)
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.parameters.ParameterCrs import ParameterCrs
|
||||
from sextante.outputs.OutputString import OutputString
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'August 2012'
|
||||
@ -317,6 +318,20 @@ class ModelerParametersDialog(QtGui.QDialog):
|
||||
for param in params:
|
||||
if isinstance(param, ParameterString):
|
||||
strings.append(AlgorithmAndParameter(AlgorithmAndParameter.PARENT_MODEL_ALGORITHM, param.name, "", param.description))
|
||||
|
||||
if self.algIndex is None:
|
||||
dependent = []
|
||||
else:
|
||||
dependent = self.model.getDependentAlgorithms(self.algIndex)
|
||||
dependent.append(self.algIndex)
|
||||
|
||||
i=0
|
||||
for alg in self.model.algs:
|
||||
if i not in dependent:
|
||||
for out in alg.outputs:
|
||||
if isinstance(out, OutputString):
|
||||
strings.append(AlgorithmAndParameter(i, out.name, alg.name, out.description))
|
||||
i+=1
|
||||
return strings
|
||||
|
||||
def getTableFields(self):
|
||||
|
@ -37,6 +37,7 @@ class ModelerScene(QtGui.QGraphicsScene):
|
||||
super(ModelerScene, self).__init__(parent)
|
||||
self.paramItems = []
|
||||
self.algItems = []
|
||||
self.setItemIndexMethod(QtGui.QGraphicsScene.NoIndex);
|
||||
|
||||
def getParameterPositions(self):
|
||||
pos = []
|
||||
@ -116,8 +117,7 @@ class ModelerScene(QtGui.QGraphicsScene):
|
||||
for sourceItem in sourceItems:
|
||||
arrow = ModelerArrowItem(sourceItem, self.algItems[iAlg])
|
||||
self.addItem(arrow)
|
||||
iAlg+=1
|
||||
|
||||
iAlg+=1
|
||||
|
||||
def mousePressEvent(self, mouseEvent):
|
||||
if (mouseEvent.button() != QtCore.Qt.LeftButton):
|
||||
|
@ -24,7 +24,6 @@ __copyright__ = '(C) 2012, Victor Olaya'
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
from sextante.core.SextanteUtils import SextanteUtils
|
||||
from sextante.core.SextanteUtils import mkdir
|
||||
from sextante.core.SextanteConfig import SextanteConfig
|
||||
|
||||
@ -37,8 +36,7 @@ class ModelerUtils:
|
||||
def modelsFolder():
|
||||
folder = SextanteConfig.getSetting(ModelerUtils.MODELS_FOLDER)
|
||||
if folder == None:
|
||||
#folder = os.path.join(os.path.dirname(__file__), "models")
|
||||
folder = SextanteUtils.userFolder() + os.sep + "models"
|
||||
folder = os.path.join(os.path.dirname(__file__), "models")
|
||||
mkdir(folder)
|
||||
|
||||
return folder
|
||||
|
75
python/plugins/sextante/modeler/models/watersheds.model
Normal file
75
python/plugins/sextante/modeler/models/watersheds.model
Normal file
@ -0,0 +1,75 @@
|
||||
NAME:Watersheds from DEM
|
||||
GROUP:[Sample models]
|
||||
PARAMETER:ParameterRaster|RASTERLAYER_DEM|DEM|False
|
||||
458.0,50.0
|
||||
PARAMETER:ParameterNumber|NUMBER_INITIATIONTHRESHOLD|Initiation Threshold|None|None|10000000.0
|
||||
257.0,403.0
|
||||
VALUE:HARDCODEDPARAMVALUE_INIT_VALUE_1===10000000
|
||||
VALUE:HARDCODEDPARAMVALUE_SPLIT_3===0
|
||||
VALUE:HARDCODEDPARAMVALUE_Method_0===0
|
||||
VALUE:HARDCODEDPARAMVALUE_INIT_METHOD_1===2
|
||||
VALUE:HARDCODEDPARAMVALUE_CALC_METHOD_4===0
|
||||
VALUE:HARDCODEDPARAMVALUE_CLASS_ID_3===0
|
||||
VALUE:HARDCODEDPARAMVALUE_STEP_0===1
|
||||
VALUE:HARDCODEDPARAMVALUE_MINLEN_1===10
|
||||
VALUE:HARDCODEDPARAMVALUE_DOLINEAR _0===True
|
||||
VALUE:HARDCODEDPARAMVALUE_MINSIZE_2===0
|
||||
VALUE:HARDCODEDPARAMVALUE_CLASS_ALL_3===1
|
||||
VALUE:HARDCODEDPARAMVALUE_LINEARTHRS_0===500.0
|
||||
VALUE:HARDCODEDPARAMVALUE_CONVERGENCE_0===1.0
|
||||
VALUE:HARDCODEDPARAMVALUE_DIV_CELLS_1===10
|
||||
ALGORITHM:saga:catchmentarea(parallel)
|
||||
260.0,172.0
|
||||
-1|RASTERLAYER_DEM
|
||||
None
|
||||
None
|
||||
None
|
||||
None
|
||||
-1|HARDCODEDPARAMVALUE_STEP_0
|
||||
-1|HARDCODEDPARAMVALUE_Method_0
|
||||
-1|HARDCODEDPARAMVALUE_DOLINEAR _0
|
||||
-1|HARDCODEDPARAMVALUE_LINEARTHRS_0
|
||||
None
|
||||
None
|
||||
-1|HARDCODEDPARAMVALUE_CONVERGENCE_0
|
||||
None
|
||||
None
|
||||
None
|
||||
None
|
||||
None
|
||||
None
|
||||
None
|
||||
None
|
||||
ALGORITHM:saga:channelnetwork
|
||||
447.0,291.0
|
||||
-1|RASTERLAYER_DEM
|
||||
None
|
||||
0|CAREA
|
||||
-1|HARDCODEDPARAMVALUE_INIT_METHOD_1
|
||||
-1|NUMBER_INITIATIONTHRESHOLD
|
||||
None
|
||||
-1|HARDCODEDPARAMVALUE_DIV_CELLS_1
|
||||
None
|
||||
-1|HARDCODEDPARAMVALUE_MINLEN_1
|
||||
None
|
||||
None
|
||||
None
|
||||
ALGORITHM:saga:watershedbasins
|
||||
730.0,182.0
|
||||
-1|RASTERLAYER_DEM
|
||||
1|CHNLNTWRK
|
||||
None
|
||||
-1|HARDCODEDPARAMVALUE_MINSIZE_2
|
||||
None
|
||||
ALGORITHM:saga:vectorisinggridclasses
|
||||
864.0,330.0
|
||||
2|BASINS
|
||||
-1|HARDCODEDPARAMVALUE_CLASS_ALL_3
|
||||
-1|HARDCODEDPARAMVALUE_CLASS_ID_3
|
||||
-1|HARDCODEDPARAMVALUE_SPLIT_3
|
||||
None
|
||||
ALGORITHM:ftools:export/addgeometrycolumns
|
||||
655.0,442.0
|
||||
3|POLYGONS
|
||||
-1|HARDCODEDPARAMVALUE_CALC_METHOD_4
|
||||
Watersheds
|
@ -16,7 +16,6 @@
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'August 2012'
|
||||
__copyright__ = '(C) 2012, Victor Olaya'
|
||||
@ -29,12 +28,13 @@ from sextante.outputs.OutputTable import OutputTable
|
||||
from sextante.outputs.OutputVector import OutputVector
|
||||
from sextante.outputs.OutputNumber import OutputNumber
|
||||
from sextante.outputs.OutputFile import OutputFile
|
||||
from sextante.outputs.OutputString import OutputString
|
||||
|
||||
class OutputFactory():
|
||||
|
||||
@staticmethod
|
||||
def getFromString(s):
|
||||
classes = [OutputRaster, OutputVector, OutputTable, OutputHTML, OutputNumber, OutputFile]
|
||||
classes = [OutputRaster, OutputVector, OutputTable, OutputHTML, OutputNumber, OutputFile, OutputString]
|
||||
for clazz in classes:
|
||||
if s.startswith(clazz().outputTypeName()):
|
||||
tokens = s[len(clazz().outputTypeName())+1:].split("|")
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
__init__.py
|
||||
OutputString.py
|
||||
---------------------
|
||||
Date : August 2012
|
||||
Copyright : (C) 2012 by Tim Sutton
|
||||
Email : tim at linfiniti dot com
|
||||
Date : October 2012
|
||||
Copyright : (C) 2012 by Victor Olaya
|
||||
Email : volayaf at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -17,22 +17,18 @@
|
||||
***************************************************************************
|
||||
"""
|
||||
|
||||
__author__ = 'Tim Sutton'
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'August 2012'
|
||||
__copyright__ = '(C) 2012, Tim Sutton'
|
||||
__copyright__ = '(C) 2012, Victor Olaya'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
def name():
|
||||
return "SEXTANTE example provider"
|
||||
def description():
|
||||
return "An example plugin that adds algorithms to SEXTANTE. Mainly created to guide developers in the process of creating plugins that add new capabilities to SEXTANTE"
|
||||
def version():
|
||||
return "Version 1.0"
|
||||
def icon():
|
||||
return "icon.png"
|
||||
def qgisMinimumVersion():
|
||||
return "1.0"
|
||||
def classFactory(iface):
|
||||
from sextanteexampleprovider.SextanteExampleProviderPlugin import SextanteExampleProviderPlugin
|
||||
return SextanteExampleProviderPlugin()
|
||||
from sextante.outputs.Output import Output
|
||||
|
||||
class OutputString(Output):
|
||||
|
||||
def __init__(self, name="", description=""):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.value = None
|
||||
self.hidden = True
|
@ -36,7 +36,7 @@ class PymorphAlgorithmProvider(AlgorithmProvider):
|
||||
|
||||
def __init__(self):
|
||||
AlgorithmProvider.__init__(self)
|
||||
#self.readAlgNames()
|
||||
self.activate = False
|
||||
self.createAlgsList()
|
||||
|
||||
def scriptsFolder(self):
|
||||
|
@ -88,9 +88,11 @@ class EditRScriptDialog(QtGui.QDialog):
|
||||
|
||||
def saveAlgorithm(self):
|
||||
if self.filename is None:
|
||||
self.filename = QtGui.QFileDialog.getSaveFileName(self, "Save Script", RUtils.RScriptsFolder(), "SEXTANTE R script (*.rsx)")
|
||||
self.filename = QtGui.QFileDialog.getSaveFileName(self, "Save Script", RUtils.RScriptsFolder(), "SEXTANTE R script (*.rsx)")
|
||||
|
||||
if self.filename:
|
||||
if not self.filename.endswith(".rsx"):
|
||||
self.filename += ".rsx"
|
||||
text = str(self.text.toPlainText())
|
||||
if self.alg is not None:
|
||||
self.alg.script = text
|
||||
|
@ -91,6 +91,8 @@ class EditScriptDialog(QtGui.QDialog):
|
||||
self.filename = QtGui.QFileDialog.getSaveFileName(self, "Save Script", ScriptUtils.scriptsFolder(), "Python scripts (*.py)")
|
||||
|
||||
if self.filename:
|
||||
if not self.filename.endswith(".py"):
|
||||
self.filename += ".py"
|
||||
text = str(self.text.toPlainText())
|
||||
if self.alg is not None:
|
||||
self.alg.script = text
|
||||
|
61
python/plugins/sextante/servertools/CreateMosaicDatastore.py
Normal file
61
python/plugins/sextante/servertools/CreateMosaicDatastore.py
Normal file
@ -0,0 +1,61 @@
|
||||
#===============================================================================
|
||||
# # -*- coding: utf-8 -*-
|
||||
#
|
||||
# """
|
||||
# ***************************************************************************
|
||||
# CreateMosaicDatastore.py
|
||||
# ---------------------
|
||||
# Date : October 2012
|
||||
# Copyright : (C) 2012 by Victor Olaya
|
||||
# Email : volayaf at gmail 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. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
# """
|
||||
# from sextante.core.LayerExporter import LayerExporter
|
||||
# from sextante.parameters.ParameterString import ParameterString
|
||||
# from sextante.servertools.GeoServerToolsAlgorithm import GeoServerToolsAlgorithm
|
||||
#
|
||||
# __author__ = 'Victor Olaya'
|
||||
# __date__ = 'October 2012'
|
||||
# __copyright__ = '(C) 2012, Victor Olaya'
|
||||
# # This will get replaced with a git SHA1 when you do a git archive
|
||||
# __revision__ = '$Format:%H$'
|
||||
#
|
||||
# from qgis.core import *
|
||||
# from sextante.parameters.ParameterVector import ParameterVector
|
||||
# from sextante.core.QGisLayers import QGisLayers
|
||||
# import os
|
||||
#
|
||||
# class CreateMosaicDatastore(GeoServerToolsAlgorithm):
|
||||
#
|
||||
# INPUT = "INPUT"
|
||||
# WORKSPACE = "WORKSPACE"
|
||||
#
|
||||
# def processAlgorithm(self, progress):
|
||||
# self.createCatalog()
|
||||
# input = self.getParameterValue(self.INPUT)
|
||||
# workspaceName = self.getParameterValue(self.WORKSPACE)
|
||||
# connection = {
|
||||
# 'shp': basepathname + '.shp',
|
||||
# 'shx': basepathname + '.shx',
|
||||
# 'dbf': basepathname + '.dbf',
|
||||
# 'prj': basepathname + '.prj'
|
||||
# }
|
||||
#
|
||||
# workspace = self.catalog.get_workspace(workspaceName)
|
||||
# self.catalog.create_featurestore(basefilename, connection, workspace)
|
||||
#
|
||||
#
|
||||
# def defineCharacteristics(self):
|
||||
# self.addcaddBaseParameters()
|
||||
# self.name = "Import into GeoServer"
|
||||
# self.group = "GeoServer management tools"
|
||||
# self.addParameter(ParameterVector(self.INPUT, "Layer to import", ParameterVector.VECTOR_TYPE_ANY))
|
||||
# self.addParameter(ParameterString(self.WORKSPACE, "Workspace"))
|
||||
#===============================================================================
|
56
python/plugins/sextante/servertools/CreateWorkspace.py
Normal file
56
python/plugins/sextante/servertools/CreateWorkspace.py
Normal file
@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
CreateWorkspace.py
|
||||
---------------------
|
||||
Date : October 2012
|
||||
Copyright : (C) 2012 by Victor Olaya
|
||||
Email : volayaf at gmail 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. *
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.servertools.GeoServerToolsAlgorithm import GeoServerToolsAlgorithm
|
||||
from sextante.outputs.OutputString import OutputString
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'October 2012'
|
||||
__copyright__ = '(C) 2012, Victor Olaya'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
from qgis.core import *
|
||||
from PyQt4 import QtGui
|
||||
from sextante.parameters.ParameterString import ParameterString
|
||||
|
||||
class CreateWorkspace(GeoServerToolsAlgorithm):
|
||||
|
||||
WORKSPACE = "WORKSPACE"
|
||||
WORKSPACEURI = "WORKSPACEURI"
|
||||
|
||||
def getIcon(self):
|
||||
return QtGui.QIcon(os.path.dirname(__file__) + "/../images/geoserver.png")
|
||||
|
||||
def processAlgorithm(self, progress):
|
||||
self.createCatalog()
|
||||
workspaceName = self.getParameterValue(self.WORKSPACE)
|
||||
workspaceUri = self.getParameterValue(self.WORKSPACEURI)
|
||||
self.catalog.create_workspace(workspaceName, workspaceUri)
|
||||
|
||||
|
||||
def defineCharacteristics(self):
|
||||
self.addBaseParameters()
|
||||
self.name = "Create workspace"
|
||||
self.group = "GeoServer management tools"
|
||||
self.addParameter(ParameterString(self.WORKSPACE, "Workspace"))
|
||||
self.addParameter(ParameterString(self.WORKSPACEURI, "Workspace URI"))
|
||||
self.addOutput(OutputString(self.WORKSPACE, "Workspace"))
|
||||
|
||||
|
56
python/plugins/sextante/servertools/DeleteDatastore.py
Normal file
56
python/plugins/sextante/servertools/DeleteDatastore.py
Normal file
@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
DeleteDatastore.py
|
||||
---------------------
|
||||
Date : October 2012
|
||||
Copyright : (C) 2012 by Victor Olaya
|
||||
Email : volayaf at gmail 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. *
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.servertools.GeoServerToolsAlgorithm import GeoServerToolsAlgorithm
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'October 2012'
|
||||
__copyright__ = '(C) 2012, Victor Olaya'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
from qgis.core import *
|
||||
from PyQt4 import QtGui
|
||||
from sextante.parameters.ParameterString import ParameterString
|
||||
|
||||
class DeleteDatastore(GeoServerToolsAlgorithm):
|
||||
|
||||
DATASTORE = "DATASTORE"
|
||||
WORKSPACE = "WORKSPACE"
|
||||
|
||||
def getIcon(self):
|
||||
return QtGui.QIcon(os.path.dirname(__file__) + "/../images/geoserver.png")
|
||||
|
||||
def processAlgorithm(self, progress):
|
||||
self.createCatalog()
|
||||
datastoreName = self.getParameterValue(self.DATASTORE)
|
||||
workspaceName = self.getParameterValue(self.WORKSPACE)
|
||||
ds = self.catalog.get_store(datastoreName, workspaceName)
|
||||
self.catalog.delete(ds, recurse=True)
|
||||
|
||||
|
||||
def defineCharacteristics(self):
|
||||
self.addBaseParameters()
|
||||
self.name = "Delete datastore"
|
||||
self.group = "GeoServer management tools"
|
||||
self.addParameter(ParameterString(self.DATASTORE, "Datastore name"))
|
||||
self.addParameter(ParameterString(self.WORKSPACE, "Workspace"))
|
||||
|
||||
|
||||
|
53
python/plugins/sextante/servertools/DeleteWorkspace.py
Normal file
53
python/plugins/sextante/servertools/DeleteWorkspace.py
Normal file
@ -0,0 +1,53 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
DeleteWorkspace.py
|
||||
---------------------
|
||||
Date : October 2012
|
||||
Copyright : (C) 2012 by Victor Olaya
|
||||
Email : volayaf at gmail 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. *
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.servertools.GeoServerToolsAlgorithm import GeoServerToolsAlgorithm
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'October 2012'
|
||||
__copyright__ = '(C) 2012, Victor Olaya'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
from qgis.core import *
|
||||
from PyQt4 import QtGui
|
||||
from sextante.parameters.ParameterString import ParameterString
|
||||
|
||||
class DeleteWorkspace(GeoServerToolsAlgorithm):
|
||||
|
||||
WORKSPACE = "WORKSPACE"
|
||||
|
||||
def getIcon(self):
|
||||
return QtGui.QIcon(os.path.dirname(__file__) + "/../images/geoserver.png")
|
||||
|
||||
def processAlgorithm(self, progress):
|
||||
self.createCatalog()
|
||||
workspaceName = self.getParameterValue(self.WORKSPACE)
|
||||
ws = self.catalog.get_workspace(workspaceName)
|
||||
self.catalog.delete(ws)
|
||||
|
||||
|
||||
def defineCharacteristics(self):
|
||||
self.addBaseParameters()
|
||||
self.name = "Delete workspace"
|
||||
self.group = "GeoServer management tools"
|
||||
self.addParameter(ParameterString(self.WORKSPACE, "Workspace"))
|
||||
|
||||
|
||||
|
@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
GeoserverToolsAlgorithm.py
|
||||
---------------------
|
||||
Date : October 2012
|
||||
Copyright : (C) 2012 by Victor Olaya
|
||||
Email : volayaf at gmail 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. *
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.parameters.ParameterString import ParameterString
|
||||
from sextante.servertools.geoserver.catalog import Catalog
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'October 2012'
|
||||
__copyright__ = '(C) 2012, Victor Olaya'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
from PyQt4 import QtGui
|
||||
from sextante.core.GeoAlgorithm import GeoAlgorithm
|
||||
|
||||
class GeoServerToolsAlgorithm(GeoAlgorithm):
|
||||
|
||||
URL = "URL"
|
||||
USER = "USER"
|
||||
PASSWORD = "PASSWORD"
|
||||
|
||||
def getIcon(self):
|
||||
return QtGui.QIcon(os.path.dirname(__file__) + "/../images/geoserver.png")
|
||||
|
||||
def addBaseParameters(self):
|
||||
self.addParameter(ParameterString(self.URL, "URL", "http://localhost:8080/geoserver/rest"))
|
||||
self.addParameter(ParameterString(self.USER, "User", "admin"))
|
||||
self.addParameter(ParameterString(self.PASSWORD, "Password", "geoserver"))
|
||||
|
||||
def createCatalog(self):
|
||||
url = self.getParameterValue(self.URL)
|
||||
user = self.getParameterValue(self.USER)
|
||||
password = self.getParameterValue(self.PASSWORD)
|
||||
self.catalog = Catalog(url, user, password)
|
||||
|
||||
|
@ -0,0 +1,63 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
GeoServerToolsAlgorithmProvider.py
|
||||
---------------------
|
||||
Date : October 2012
|
||||
Copyright : (C) 2012 by Victor Olaya
|
||||
Email : volayaf at gmail 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. *
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.servertools.CreateWorkspace import CreateWorkspace
|
||||
from sextante.servertools.ImportVectorIntoGeoServer import ImportVectorIntoGeoServer
|
||||
from sextante.servertools.ImportRasterIntoGeoServer import ImportRasterIntoGeoServer
|
||||
from sextante.servertools.DeleteWorkspace import DeleteWorkspace
|
||||
from sextante.servertools.DeleteDatastore import DeleteDatastore
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'October 2012'
|
||||
__copyright__ = '(C) 2012, Victor Olaya'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
from sextante.core.AlgorithmProvider import AlgorithmProvider
|
||||
from PyQt4 import QtGui
|
||||
import os
|
||||
|
||||
class GeoServerToolsAlgorithmProvider(AlgorithmProvider):
|
||||
|
||||
def __init__(self):
|
||||
AlgorithmProvider.__init__(self)
|
||||
self.alglist = [ImportVectorIntoGeoServer(), ImportRasterIntoGeoServer(),
|
||||
CreateWorkspace(), DeleteWorkspace(), DeleteDatastore()]#, CreateMosaicGeoserver(), StyleGeoserverLayer(), TruncateSeedGWC()]
|
||||
|
||||
def initializeSettings(self):
|
||||
AlgorithmProvider.initializeSettings(self)
|
||||
|
||||
|
||||
def unload(self):
|
||||
AlgorithmProvider.unload(self)
|
||||
|
||||
|
||||
def getName(self):
|
||||
return "geoserver"
|
||||
|
||||
def getDescription(self):
|
||||
return "Geoserver management tools"
|
||||
|
||||
def getIcon(self):
|
||||
return QtGui.QIcon(os.path.dirname(__file__) + "/../images/geoserver.png")
|
||||
|
||||
def _loadAlgorithms(self):
|
||||
self.algs = self.alglist
|
||||
|
||||
def supportsNonFileBasedOutput(self):
|
||||
return True
|
@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
ImportVectorIntoGeoServer.py
|
||||
---------------------
|
||||
Date : October 2012
|
||||
Copyright : (C) 2012 by Victor Olaya
|
||||
Email : volayaf at gmail 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. *
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.core.LayerExporter import LayerExporter
|
||||
from sextante.parameters.ParameterString import ParameterString
|
||||
from sextante.servertools.GeoServerToolsAlgorithm import GeoServerToolsAlgorithm
|
||||
from sextante.parameters.ParameterRaster import ParameterRaster
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'October 2012'
|
||||
__copyright__ = '(C) 2012, Victor Olaya'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
from qgis.core import *
|
||||
|
||||
class ImportRasterIntoGeoServer(GeoServerToolsAlgorithm):
|
||||
|
||||
INPUT = "INPUT"
|
||||
WORKSPACE = "WORKSPACE"
|
||||
NAME = "NAME"
|
||||
|
||||
|
||||
def exportRasterLayer(self, inputFilename):
|
||||
return inputFilename
|
||||
|
||||
|
||||
def processAlgorithm(self, progress):
|
||||
self.createCatalog()
|
||||
inputFilename = self.getParameterValue(self.INPUT)
|
||||
name = self.getParameterValue(self.NAME)
|
||||
workspaceName = self.getParameterValue(self.WORKSPACE)
|
||||
filename = self.exportRasterLayer(inputFilename)
|
||||
workspace = self.catalog.get_workspace(workspaceName)
|
||||
ds = self.catalog.create_coveragestore2(name, workspace)
|
||||
ds.data_url = "file:" + filename;
|
||||
self.catalog.save(ds)
|
||||
|
||||
|
||||
def defineCharacteristics(self):
|
||||
self.addBaseParameters()
|
||||
self.name = "Import raster into GeoServer"
|
||||
self.group = "GeoServer management tools"
|
||||
self.addParameter(ParameterRaster(self.INPUT, "Layer to import"))
|
||||
self.addParameter(ParameterString(self.WORKSPACE, "Workspace"))
|
||||
self.addParameter(ParameterString(self.NAME, "Store name"))
|
||||
|
@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
ImportVectorIntoGeoServer.py
|
||||
---------------------
|
||||
Date : October 2012
|
||||
Copyright : (C) 2012 by Victor Olaya
|
||||
Email : volayaf at gmail 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. *
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from sextante.core.LayerExporter import LayerExporter
|
||||
from sextante.parameters.ParameterString import ParameterString
|
||||
from sextante.servertools.GeoServerToolsAlgorithm import GeoServerToolsAlgorithm
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'October 2012'
|
||||
__copyright__ = '(C) 2012, Victor Olaya'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
from qgis.core import *
|
||||
from sextante.parameters.ParameterVector import ParameterVector
|
||||
from sextante.core.QGisLayers import QGisLayers
|
||||
import os
|
||||
|
||||
class ImportVectorIntoGeoServer(GeoServerToolsAlgorithm):
|
||||
|
||||
INPUT = "INPUT"
|
||||
WORKSPACE = "WORKSPACE"
|
||||
|
||||
def processAlgorithm(self, progress):
|
||||
self.createCatalog()
|
||||
inputFilename = self.getParameterValue(self.INPUT)
|
||||
layer = QGisLayers.getObjectFromUri(inputFilename)
|
||||
workspaceName = self.getParameterValue(self.WORKSPACE)
|
||||
filename = LayerExporter.exportVectorLayer(layer)
|
||||
basefilename = os.path.basename(filename)
|
||||
basepathname = os.path.dirname(filename) + os.sep + basefilename[:basefilename.find('.')]
|
||||
connection = {
|
||||
'shp': basepathname + '.shp',
|
||||
'shx': basepathname + '.shx',
|
||||
'dbf': basepathname + '.dbf',
|
||||
'prj': basepathname + '.prj'
|
||||
}
|
||||
|
||||
workspace = self.catalog.get_workspace(workspaceName)
|
||||
self.catalog.create_featurestore(basefilename, connection, workspace)
|
||||
|
||||
|
||||
def defineCharacteristics(self):
|
||||
self.addBaseParameters()
|
||||
self.name = "Import vector into GeoServer"
|
||||
self.group = "GeoServer management tools"
|
||||
self.addParameter(ParameterVector(self.INPUT, "Layer to import", ParameterVector.VECTOR_TYPE_ANY))
|
||||
self.addParameter(ParameterString(self.WORKSPACE, "Workspace"))
|
||||
|
0
python/plugins/sextante/servertools/__init__.py
Normal file
0
python/plugins/sextante/servertools/__init__.py
Normal file
504
python/plugins/sextante/servertools/geoserver/catalog.py
Normal file
504
python/plugins/sextante/servertools/geoserver/catalog.py
Normal file
@ -0,0 +1,504 @@
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from sextante.servertools.geoserver.layer import Layer
|
||||
from sextante.servertools.geoserver.store import coveragestore_from_index, datastore_from_index, \
|
||||
UnsavedDataStore, UnsavedCoverageStore
|
||||
from sextante.servertools.geoserver.style import Style
|
||||
from sextante.servertools.geoserver.support import prepare_upload_bundle, url
|
||||
from sextante.servertools.geoserver.layergroup import LayerGroup, UnsavedLayerGroup
|
||||
from sextante.servertools.geoserver.workspace import workspace_from_index, Workspace
|
||||
from os import unlink
|
||||
from xml.etree.ElementTree import XML
|
||||
from xml.parsers.expat import ExpatError
|
||||
from urlparse import urlparse
|
||||
from sextante.servertools import httplib2
|
||||
|
||||
logger = logging.getLogger("gsconfig.catalog")
|
||||
|
||||
class UploadError(Exception):
|
||||
pass
|
||||
|
||||
class ConflictingDataError(Exception):
|
||||
pass
|
||||
|
||||
class AmbiguousRequestError(Exception):
|
||||
pass
|
||||
|
||||
class FailedRequestError(Exception):
|
||||
pass
|
||||
|
||||
def _name(named):
|
||||
"""Get the name out of an object. This varies based on the type of the input:
|
||||
* the "name" of a string is itself
|
||||
* the "name" of None is itself
|
||||
* the "name" of an object with a property named name is that property -
|
||||
as long as it's a string
|
||||
* otherwise, we raise a ValueError
|
||||
"""
|
||||
if isinstance(named, basestring) or named is None:
|
||||
return named
|
||||
elif hasattr(named, 'name') and isinstance(named.name, basestring):
|
||||
return named.name
|
||||
else:
|
||||
raise ValueError("Can't interpret %s as a name or a configuration object" % named)
|
||||
|
||||
class Catalog(object):
|
||||
"""
|
||||
The GeoServer catalog represents all of the information in the GeoServer
|
||||
configuration. This includes:
|
||||
- Stores of geospatial data
|
||||
- Resources, or individual coherent datasets within stores
|
||||
- Styles for resources
|
||||
- Layers, which combine styles with resources to create a visible map layer
|
||||
- LayerGroups, which alias one or more layers for convenience
|
||||
- Workspaces, which provide logical grouping of Stores
|
||||
- Maps, which provide a set of OWS services with a subset of the server's
|
||||
Layers
|
||||
- Namespaces, which provide unique identifiers for resources
|
||||
"""
|
||||
|
||||
def __init__(self, service_url, username="admin", password="geoserver", disable_ssl_certificate_validation=False):
|
||||
self.service_url = service_url
|
||||
if self.service_url.endswith("/"):
|
||||
self.service_url = self.service_url.strip("/")
|
||||
self.http = httplib2.Http(
|
||||
disable_ssl_certificate_validation=disable_ssl_certificate_validation)
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.http.add_credentials(self.username, self.password)
|
||||
netloc = urlparse(service_url).netloc
|
||||
self.http.authorizations.append(
|
||||
httplib2.BasicAuthentication(
|
||||
(username, password),
|
||||
netloc,
|
||||
service_url,
|
||||
{},
|
||||
None,
|
||||
None,
|
||||
self.http
|
||||
))
|
||||
self._cache = dict()
|
||||
|
||||
def delete(self, config_object, purge=False, recurse=False):
|
||||
"""
|
||||
send a delete request
|
||||
XXX [more here]
|
||||
"""
|
||||
rest_url = config_object.href
|
||||
|
||||
#params aren't supported fully in httplib2 yet, so:
|
||||
params = []
|
||||
|
||||
# purge deletes the SLD from disk when a style is deleted
|
||||
if purge:
|
||||
params.append("purge=true")
|
||||
|
||||
# recurse deletes the resource when a layer is deleted.
|
||||
if recurse:
|
||||
params.append("recurse=true")
|
||||
|
||||
if params:
|
||||
rest_url = rest_url + "?" + "&".join(params)
|
||||
|
||||
headers = {
|
||||
"Content-type": "application/xml",
|
||||
"Accept": "application/xml"
|
||||
}
|
||||
response, content = self.http.request(rest_url, "DELETE", headers=headers)
|
||||
self._cache.clear()
|
||||
|
||||
if response.status == 200:
|
||||
return (response, content)
|
||||
else:
|
||||
raise FailedRequestError("Tried to make a DELETE request to %s but got a %d status code: \n%s" % (rest_url, response.status, content))
|
||||
|
||||
def get_xml(self, rest_url):
|
||||
logger.debug("GET %s", rest_url)
|
||||
|
||||
cached_response = self._cache.get(rest_url)
|
||||
|
||||
def is_valid(cached_response):
|
||||
return cached_response is not None and datetime.now() - cached_response[0] < timedelta(seconds=5)
|
||||
|
||||
def parse_or_raise(xml):
|
||||
try:
|
||||
return XML(xml)
|
||||
except (ExpatError, SyntaxError), e:
|
||||
msg = "GeoServer gave non-XML response for [GET %s]: %s"
|
||||
msg = msg % (rest_url, xml)
|
||||
raise Exception(msg, e)
|
||||
|
||||
if is_valid(cached_response):
|
||||
raw_text = cached_response[1]
|
||||
return parse_or_raise(raw_text)
|
||||
else:
|
||||
response, content = self.http.request(rest_url)
|
||||
if response.status == 200:
|
||||
self._cache[rest_url] = (datetime.now(), content)
|
||||
return parse_or_raise(content)
|
||||
else:
|
||||
raise FailedRequestError("Tried to make a GET request to %s but got a %d status code: \n%s" % (url, response.status, content))
|
||||
|
||||
def reload(self):
|
||||
reload_url = url(self.service_url, ['reload'])
|
||||
response = self.http.request(reload_url, "POST")
|
||||
self._cache.clear()
|
||||
return response
|
||||
|
||||
def save(self, obj):
|
||||
"""
|
||||
saves an object to the REST service
|
||||
|
||||
gets the object's REST location and the XML from the object,
|
||||
then POSTS the request.
|
||||
"""
|
||||
rest_url = obj.href
|
||||
message = obj.message()
|
||||
|
||||
headers = {
|
||||
"Content-type": "application/xml",
|
||||
"Accept": "application/xml"
|
||||
}
|
||||
logger.debug("%s %s", obj.save_method, obj.href)
|
||||
response = self.http.request(rest_url, obj.save_method, message, headers)
|
||||
headers, body = response
|
||||
self._cache.clear()
|
||||
if 400 <= int(headers['status']) < 600:
|
||||
raise FailedRequestError("Error code (%s) from GeoServer: %s" %
|
||||
(headers['status'], body))
|
||||
return response
|
||||
|
||||
def get_store(self, name, workspace=None):
|
||||
#stores = [s for s in self.get_stores(workspace) if s.name == name]
|
||||
if workspace is None:
|
||||
store = None
|
||||
for ws in self.get_workspaces():
|
||||
found = None
|
||||
try:
|
||||
found = self.get_store(name, ws)
|
||||
except:
|
||||
# don't expect every workspace to contain the named store
|
||||
pass
|
||||
if found:
|
||||
if store:
|
||||
raise AmbiguousRequestError("Multiple stores found named: " + name)
|
||||
else:
|
||||
store = found
|
||||
if not store:
|
||||
raise FailedRequestError("No store found named: " + name)
|
||||
return store
|
||||
else: # workspace is not None
|
||||
if isinstance(workspace, basestring):
|
||||
workspace = self.get_workspace(workspace)
|
||||
if workspace is None:
|
||||
return None
|
||||
logger.debug("datastore url is [%s]", workspace.datastore_url )
|
||||
ds_list = self.get_xml(workspace.datastore_url)
|
||||
cs_list = self.get_xml(workspace.coveragestore_url)
|
||||
datastores = [n for n in ds_list.findall("dataStore") if n.find("name").text == name]
|
||||
coveragestores = [n for n in cs_list.findall("coverageStore") if n.find("name").text == name]
|
||||
ds_len, cs_len = len(datastores), len(coveragestores)
|
||||
|
||||
if ds_len == 1 and cs_len == 0:
|
||||
return datastore_from_index(self, workspace, datastores[0])
|
||||
elif ds_len == 0 and cs_len == 1:
|
||||
return coveragestore_from_index(self, workspace, coveragestores[0])
|
||||
elif ds_len == 0 and cs_len == 0:
|
||||
raise FailedRequestError("No store found in " + str(workspace) + " named: " + name)
|
||||
else:
|
||||
raise AmbiguousRequestError(str(workspace) + " and name: " + name + " do not uniquely identify a layer")
|
||||
|
||||
def get_stores(self, workspace=None):
|
||||
if workspace is not None:
|
||||
if isinstance(workspace, basestring):
|
||||
workspace = self.get_workspace(workspace)
|
||||
ds_list = self.get_xml(workspace.datastore_url)
|
||||
cs_list = self.get_xml(workspace.coveragestore_url)
|
||||
datastores = [datastore_from_index(self, workspace, n) for n in ds_list.findall("dataStore")]
|
||||
coveragestores = [coveragestore_from_index(self, workspace, n) for n in cs_list.findall("coverageStore")]
|
||||
return datastores + coveragestores
|
||||
else:
|
||||
stores = []
|
||||
for ws in self.get_workspaces():
|
||||
a = self.get_stores(ws)
|
||||
stores.extend(a)
|
||||
return stores
|
||||
|
||||
def create_datastore(self, name, workspace=None):
|
||||
if isinstance(workspace, basestring):
|
||||
workspace = self.get_workspace(workspace)
|
||||
elif workspace is None:
|
||||
workspace = self.get_default_workspace()
|
||||
return UnsavedDataStore(self, name, workspace)
|
||||
|
||||
def create_coveragestore2(self, name, workspace = None):
|
||||
"""
|
||||
Hm we already named the method that creates a coverage *resource*
|
||||
create_coveragestore... time for an API break?
|
||||
"""
|
||||
if isinstance(workspace, basestring):
|
||||
workspace = self.get_workspace(workspace)
|
||||
elif workspace is None:
|
||||
workspace = self.get_default_workspace()
|
||||
return UnsavedCoverageStore(self, name, workspace)
|
||||
|
||||
def add_data_to_store(self, store, name, data, workspace=None, overwrite = False, charset = None):
|
||||
if isinstance(store, basestring):
|
||||
store = self.get_store(store, workspace=workspace)
|
||||
if workspace is not None:
|
||||
workspace = _name(workspace)
|
||||
assert store.workspace.name == workspace, "Specified store (%s) is not in specified workspace (%s)!" % (store, workspace)
|
||||
else:
|
||||
workspace = store.workspace.name
|
||||
store = store.name
|
||||
|
||||
if isinstance(data, dict):
|
||||
bundle = prepare_upload_bundle(name, data)
|
||||
else:
|
||||
bundle = data
|
||||
|
||||
params = dict()
|
||||
if overwrite:
|
||||
params["update"] = "overwrite"
|
||||
if charset is not None:
|
||||
params["charset"] = charset
|
||||
|
||||
message = open(bundle)
|
||||
headers = { 'Content-Type': 'application/zip', 'Accept': 'application/xml' }
|
||||
upload_url = url(self.service_url,
|
||||
["workspaces", workspace, "datastores", store, "file.shp"], params)
|
||||
|
||||
try:
|
||||
headers, response = self.http.request(upload_url, "PUT", message, headers)
|
||||
self._cache.clear()
|
||||
if headers.status != 201:
|
||||
raise UploadError(response)
|
||||
finally:
|
||||
unlink(bundle)
|
||||
|
||||
def create_featurestore(self, name, data, workspace=None, overwrite=False, charset=None):
|
||||
if not overwrite:
|
||||
try:
|
||||
store = self.get_store(name, workspace)
|
||||
msg = "There is already a store named " + name
|
||||
if workspace:
|
||||
msg += " in " + str(workspace)
|
||||
raise ConflictingDataError(msg)
|
||||
except FailedRequestError:
|
||||
# we don't really expect that every layer name will be taken
|
||||
pass
|
||||
|
||||
if workspace is None:
|
||||
workspace = self.get_default_workspace()
|
||||
workspace = _name(workspace)
|
||||
params = dict()
|
||||
if charset is not None:
|
||||
params['charset'] = charset
|
||||
ds_url = url(self.service_url,
|
||||
["workspaces", workspace, "datastores", name, "file.shp"], params)
|
||||
|
||||
# PUT /workspaces/<ws>/datastores/<ds>/file.shp
|
||||
headers = {
|
||||
"Content-type": "application/zip",
|
||||
"Accept": "application/xml"
|
||||
}
|
||||
if isinstance(data,dict):
|
||||
logger.debug('Data is NOT a zipfile')
|
||||
archive = prepare_upload_bundle(name, data)
|
||||
else:
|
||||
logger.debug('Data is a zipfile')
|
||||
archive = data
|
||||
message = open(archive)
|
||||
try:
|
||||
headers, response = self.http.request(ds_url, "PUT", message, headers)
|
||||
self._cache.clear()
|
||||
if headers.status != 201:
|
||||
raise UploadError(response)
|
||||
finally:
|
||||
unlink(archive)
|
||||
|
||||
def create_coveragestore(self, name, data, workspace=None, overwrite=False):
|
||||
if not overwrite:
|
||||
try:
|
||||
store = self.get_store(name, workspace)
|
||||
msg = "There is already a store named " + name
|
||||
if workspace:
|
||||
msg += " in " + str(workspace)
|
||||
raise ConflictingDataError(msg)
|
||||
except FailedRequestError:
|
||||
# we don't really expect that every layer name will be taken
|
||||
pass
|
||||
|
||||
if workspace is None:
|
||||
workspace = self.get_default_workspace()
|
||||
headers = {
|
||||
"Content-type": "image/tiff",
|
||||
"Accept": "application/xml"
|
||||
}
|
||||
|
||||
archive = None
|
||||
ext = "geotiff"
|
||||
|
||||
if isinstance(data, dict):
|
||||
archive = prepare_upload_bundle(name, data)
|
||||
message = open(archive)
|
||||
if "tfw" in data:
|
||||
headers['Content-type'] = 'application/archive'
|
||||
ext = "worldimage"
|
||||
elif isinstance(data, basestring):
|
||||
message = open(data)
|
||||
else:
|
||||
message = data
|
||||
|
||||
cs_url = url(self.service_url,
|
||||
["workspaces", workspace.name, "coveragestores", name, "file." + ext])
|
||||
|
||||
try:
|
||||
headers, response = self.http.request(cs_url, "PUT", message, headers)
|
||||
self._cache.clear()
|
||||
if headers.status != 201:
|
||||
raise UploadError(response)
|
||||
finally:
|
||||
if archive is not None:
|
||||
unlink(archive)
|
||||
|
||||
def get_resource(self, name, store=None, workspace=None):
|
||||
if store is not None:
|
||||
candidates = [s for s in self.get_resources(store) if s.name == name]
|
||||
if len(candidates) == 0:
|
||||
return None
|
||||
elif len(candidates) > 1:
|
||||
raise AmbiguousRequestError
|
||||
else:
|
||||
return candidates[0]
|
||||
|
||||
if workspace is not None:
|
||||
for store in self.get_stores(workspace):
|
||||
resource = self.get_resource(name, store)
|
||||
if resource is not None:
|
||||
return resource
|
||||
return None
|
||||
|
||||
for ws in self.get_workspaces():
|
||||
resource = self.get_resource(name, workspace=ws)
|
||||
if resource is not None:
|
||||
return resource
|
||||
return None
|
||||
|
||||
def get_resources(self, store=None, workspace=None):
|
||||
if isinstance(workspace, basestring):
|
||||
workspace = self.get_workspace(workspace)
|
||||
if isinstance(store, basestring):
|
||||
store = self.get_store(store, workspace)
|
||||
if store is not None:
|
||||
return store.get_resources()
|
||||
if workspace is not None:
|
||||
resources = []
|
||||
for store in self.get_stores(workspace):
|
||||
resources.extend(self.get_resources(store))
|
||||
return resources
|
||||
resources = []
|
||||
for ws in self.get_workspaces():
|
||||
resources.extend(self.get_resources(workspace=ws))
|
||||
return resources
|
||||
|
||||
def get_layer(self, name):
|
||||
try:
|
||||
lyr = Layer(self, name)
|
||||
lyr.fetch()
|
||||
return lyr
|
||||
except FailedRequestError:
|
||||
return None
|
||||
|
||||
def get_layers(self, resource=None):
|
||||
if isinstance(resource, basestring):
|
||||
resource = self.get_resource(resource)
|
||||
layers_url = url(self.service_url, ["layers.xml"])
|
||||
description = self.get_xml(layers_url)
|
||||
lyrs = [Layer(self, l.find("name").text) for l in description.findall("layer")]
|
||||
if resource is not None:
|
||||
lyrs = [l for l in lyrs if l.resource.href == resource.href]
|
||||
# TODO: Filter by style
|
||||
return lyrs
|
||||
|
||||
def get_layergroup(self, name=None):
|
||||
try:
|
||||
group_url = url(self.service_url, ["layergroups", name + ".xml"])
|
||||
group = self.get_xml(group_url)
|
||||
return LayerGroup(self, group.find("name").text)
|
||||
except FailedRequestError:
|
||||
return None
|
||||
|
||||
def get_layergroups(self):
|
||||
groups = self.get_xml("%s/layergroups.xml" % self.service_url)
|
||||
return [LayerGroup(self, g.find("name").text) for g in groups.findall("layerGroup")]
|
||||
|
||||
def create_layergroup(self, name, layers = (), styles = (), bounds = None):
|
||||
if any(g.name == name for g in self.get_layergroups()):
|
||||
raise ConflictingDataError("LayerGroup named %s already exists!" % name)
|
||||
else:
|
||||
return UnsavedLayerGroup(self, name, layers, styles, bounds)
|
||||
|
||||
def get_style(self, name):
|
||||
try:
|
||||
style_url = url(self.service_url, ["styles", name + ".xml"])
|
||||
dom = self.get_xml(style_url)
|
||||
return Style(self, dom.find("name").text)
|
||||
except FailedRequestError:
|
||||
return None
|
||||
|
||||
def get_styles(self):
|
||||
styles_url = url(self.service_url, ["styles.xml"])
|
||||
description = self.get_xml(styles_url)
|
||||
return [Style(self, s.find('name').text) for s in description.findall("style")]
|
||||
|
||||
def create_style(self, name, data, overwrite = False):
|
||||
if overwrite == False and self.get_style(name) is not None:
|
||||
raise ConflictingDataError("There is already a style named %s" % name)
|
||||
|
||||
headers = {
|
||||
"Content-type": "application/vnd.ogc.sld+xml",
|
||||
"Accept": "application/xml"
|
||||
}
|
||||
|
||||
if overwrite:
|
||||
style_url = url(self.service_url, ["styles", name + ".sld"])
|
||||
headers, response = self.http.request(style_url, "PUT", data, headers)
|
||||
else:
|
||||
style_url = url(self.service_url, ["styles"], dict(name=name))
|
||||
headers, response = self.http.request(style_url, "POST", data, headers)
|
||||
|
||||
self._cache.clear()
|
||||
if headers.status < 200 or headers.status > 299: raise UploadError(response)
|
||||
|
||||
def create_workspace(self, name, uri):
|
||||
xml = ("<namespace>"
|
||||
"<prefix>{name}</prefix>"
|
||||
"<uri>{uri}</uri>"
|
||||
"</namespace>").format(name=name, uri=uri)
|
||||
headers = { "Content-Type": "application/xml" }
|
||||
workspace_url = self.service_url + "/namespaces/"
|
||||
|
||||
headers, response = self.http.request(workspace_url, "POST", xml, headers)
|
||||
assert 200 <= headers.status < 300, "Tried to create workspace but got " + str(headers.status) + ": " + response
|
||||
self._cache.clear()
|
||||
return self.get_workspace(name)
|
||||
|
||||
def get_workspaces(self):
|
||||
description = self.get_xml("%s/workspaces.xml" % self.service_url)
|
||||
return [workspace_from_index(self, node) for node in description.findall("workspace")]
|
||||
|
||||
def get_workspace(self, name):
|
||||
candidates = [w for w in self.get_workspaces() if w.name == name]
|
||||
if len(candidates) == 0:
|
||||
return None
|
||||
elif len(candidates) > 1:
|
||||
raise AmbiguousRequestError()
|
||||
else:
|
||||
return candidates[0]
|
||||
|
||||
def get_default_workspace(self):
|
||||
return Workspace(self, "default")
|
||||
|
||||
def set_default_workspace(self):
|
||||
raise NotImplementedError()
|
132
python/plugins/sextante/servertools/geoserver/layer.py
Normal file
132
python/plugins/sextante/servertools/geoserver/layer.py
Normal file
@ -0,0 +1,132 @@
|
||||
from sextante.servertools.geoserver.support import ResourceInfo, xml_property, write_bool, url
|
||||
from sextante.servertools.geoserver.style import Style
|
||||
|
||||
class _attribution(object):
|
||||
def __init__(self, title, width, height):
|
||||
self.title = title
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
def _read_attribution(node):
|
||||
title = node.find("title")
|
||||
width = node.find("logoWidth")
|
||||
height = node.find("logoHeight")
|
||||
|
||||
if title is not None:
|
||||
title = title.text
|
||||
if width is not None:
|
||||
width = width.text
|
||||
if height is not None:
|
||||
height = height.text
|
||||
|
||||
return _attribution(title, width, height)
|
||||
|
||||
def _write_attribution(builder, attr):
|
||||
builder.start("attribution", dict())
|
||||
if attr.title is not None:
|
||||
builder.start("title", dict())
|
||||
builder.data(attr.title)
|
||||
builder.end("title")
|
||||
if attr.width is not None:
|
||||
builder.start("logoWidth", dict())
|
||||
builder.data(attr.width)
|
||||
builder.end("logoWidth")
|
||||
if attr.height is not None:
|
||||
builder.start("logoHeight", dict())
|
||||
builder.data(attr.height)
|
||||
builder.end("logoHeight")
|
||||
builder.end("attribution")
|
||||
|
||||
def _write_default_style(builder, name):
|
||||
builder.start("defaultStyle", dict())
|
||||
if name is not None:
|
||||
builder.start("name", dict())
|
||||
builder.data(name)
|
||||
builder.end("name")
|
||||
builder.end("defaultStyle")
|
||||
|
||||
|
||||
def _write_alternate_styles(builder, styles):
|
||||
builder.start("styles", dict())
|
||||
for s in styles:
|
||||
builder.start("style", dict())
|
||||
builder.start("name", dict())
|
||||
builder.data(s.name)
|
||||
builder.end("name")
|
||||
builder.end("style")
|
||||
builder.end("styles")
|
||||
|
||||
|
||||
class Layer(ResourceInfo):
|
||||
def __init__(self, catalog, name):
|
||||
super(Layer, self).__init__()
|
||||
self.catalog = catalog
|
||||
self.name = name
|
||||
|
||||
resource_type = "layer"
|
||||
save_method = "PUT"
|
||||
|
||||
@property
|
||||
def href(self):
|
||||
return url(self.catalog.service_url, ["layers", self.name + ".xml"])
|
||||
|
||||
@property
|
||||
def resource(self):
|
||||
if self.dom is None:
|
||||
self.fetch()
|
||||
name = self.dom.find("resource/name").text
|
||||
return self.catalog.get_resource(name)
|
||||
|
||||
def _get_default_style(self):
|
||||
if 'default_style' in self.dirty:
|
||||
return self.dirty['default_style']
|
||||
if self.dom is None:
|
||||
self.fetch()
|
||||
name = self.dom.find("defaultStyle/name")
|
||||
# aborted data uploads can result in no default style
|
||||
if name is not None:
|
||||
return self.catalog.get_style(name.text)
|
||||
else:
|
||||
return None
|
||||
|
||||
def _set_default_style(self, style):
|
||||
if isinstance(style, Style):
|
||||
style = style.name
|
||||
self.dirty["default_style"] = style
|
||||
|
||||
def _get_alternate_styles(self):
|
||||
if "alternate_styles" in self.dirty:
|
||||
return self.dirty["alternate_styles"]
|
||||
if self.dom is None:
|
||||
self.fetch()
|
||||
styles = self.dom.findall("styles/style/name")
|
||||
return [Style(self.catalog, s.text) for s in styles]
|
||||
|
||||
def _set_alternate_styles(self, styles):
|
||||
self.dirty["alternate_styles"] = styles
|
||||
|
||||
default_style = property(_get_default_style, _set_default_style)
|
||||
styles = property(_get_alternate_styles, _set_alternate_styles)
|
||||
|
||||
attribution_object = xml_property("attribution", _read_attribution)
|
||||
enabled = xml_property("enabled", lambda x: x.text == "true")
|
||||
|
||||
def _get_attr_text(self):
|
||||
return self.attribution_object.title
|
||||
|
||||
def _set_attr_text(self, text):
|
||||
self.dirty["attribution"] = _attribution(
|
||||
text,
|
||||
self.attribution_object.width,
|
||||
self.attribution_object.height
|
||||
)
|
||||
assert self.attribution_object.title == text
|
||||
|
||||
attribution = property(_get_attr_text, _set_attr_text)
|
||||
|
||||
writers = dict(
|
||||
attribution = _write_attribution,
|
||||
enabled = write_bool("enabled"),
|
||||
default_style = _write_default_style,
|
||||
alternate_styles = _write_alternate_styles
|
||||
)
|
84
python/plugins/sextante/servertools/geoserver/layergroup.py
Normal file
84
python/plugins/sextante/servertools/geoserver/layergroup.py
Normal file
@ -0,0 +1,84 @@
|
||||
from sextante.servertools.geoserver.support import ResourceInfo, bbox, write_bbox, \
|
||||
write_string, xml_property, url
|
||||
|
||||
def _maybe_text(n):
|
||||
if n is None:
|
||||
return None
|
||||
else:
|
||||
return n.text
|
||||
|
||||
def _layer_list(node):
|
||||
if node is not None:
|
||||
return [_maybe_text(n.find("name")) for n in node.findall("layer")]
|
||||
|
||||
def _style_list(node):
|
||||
if node is not None:
|
||||
return [_maybe_text(n.find("name")) for n in node.findall("style")]
|
||||
|
||||
def _write_layers(builder, layers):
|
||||
builder.start("layers", dict())
|
||||
for l in layers:
|
||||
builder.start("layer", dict())
|
||||
if l is not None:
|
||||
builder.start("name", dict())
|
||||
builder.data(l)
|
||||
builder.end("name")
|
||||
builder.end("layer")
|
||||
builder.end("layers")
|
||||
|
||||
def _write_styles(builder, styles):
|
||||
builder.start("styles", dict())
|
||||
for s in styles:
|
||||
builder.start("style", dict())
|
||||
if s is not None:
|
||||
builder.start("name", dict())
|
||||
builder.data(s)
|
||||
builder.end("name")
|
||||
builder.end("style")
|
||||
builder.end("styles")
|
||||
|
||||
class LayerGroup(ResourceInfo):
|
||||
"""
|
||||
Represents a layer group in geoserver
|
||||
"""
|
||||
|
||||
resource_type = "layerGroup"
|
||||
save_method = "PUT"
|
||||
|
||||
def __init__(self, catalog, name):
|
||||
super(LayerGroup, self).__init__()
|
||||
|
||||
assert isinstance(name, basestring)
|
||||
|
||||
self.catalog = catalog
|
||||
self.name = name
|
||||
|
||||
@property
|
||||
def href(self):
|
||||
return url(self.catalog.service_url, ["layergroups", self.name + ".xml"])
|
||||
|
||||
styles = xml_property("styles", _style_list)
|
||||
layers = xml_property("layers", _layer_list)
|
||||
bounds = xml_property("bounds", bbox)
|
||||
|
||||
writers = dict(
|
||||
name = write_string("name"),
|
||||
styles = _write_styles,
|
||||
layers = _write_layers,
|
||||
bounds = write_bbox("bounds")
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "<LayerGroup %s>" % self.name
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
class UnsavedLayerGroup(LayerGroup):
|
||||
save_method = "POST"
|
||||
def __init__(self, catalog, name, layers, styles, bounds):
|
||||
super(UnsavedLayerGroup, self).__init__(catalog, name)
|
||||
self.dirty.update(name = name, layers = layers, styles = styles, bounds = bounds)
|
||||
|
||||
@property
|
||||
def href(self):
|
||||
return "%s/layergroups?name=%s" % (self.catalog.service_url, self.name)
|
176
python/plugins/sextante/servertools/geoserver/resource.py
Normal file
176
python/plugins/sextante/servertools/geoserver/resource.py
Normal file
@ -0,0 +1,176 @@
|
||||
from sextante.servertools.geoserver.support import ResourceInfo, xml_property, write_string, bbox, \
|
||||
write_bbox, string_list, write_string_list, attribute_list, write_bool, url
|
||||
|
||||
def md_link(node):
|
||||
"""Extract a metadata link tuple from an xml node"""
|
||||
mimetype = node.find("type")
|
||||
mdtype = node.find("metadataType")
|
||||
content = node.find("content")
|
||||
if None in [mimetype, mdtype, content]:
|
||||
return None
|
||||
else:
|
||||
return (mimetype.text, mdtype.text, content.text)
|
||||
|
||||
def metadata_link_list(node):
|
||||
if node is not None:
|
||||
return [md_link(n) for n in node.findall("metadataLink")]
|
||||
|
||||
def write_metadata_link_list(name):
|
||||
def write(builder, md_links):
|
||||
builder.start(name, dict())
|
||||
for (mime, md_type, content_url) in md_links:
|
||||
builder.start("metadataLink", dict())
|
||||
builder.start("type", dict())
|
||||
builder.data(mime)
|
||||
builder.end("type")
|
||||
builder.start("metadataType", dict())
|
||||
builder.data(md_type)
|
||||
builder.end("metadataType")
|
||||
builder.start("content", dict())
|
||||
builder.data(content_url)
|
||||
builder.end("content")
|
||||
builder.end("metadataLink")
|
||||
builder.end("metadataLinks")
|
||||
return write
|
||||
|
||||
def featuretype_from_index(catalog, workspace, store, node):
|
||||
name = node.find("name")
|
||||
return FeatureType(catalog, workspace, store, name.text)
|
||||
|
||||
def coverage_from_index(catalog, workspace, store, node):
|
||||
name = node.find("name")
|
||||
return Coverage(catalog, workspace, store, name.text)
|
||||
|
||||
class FeatureType(ResourceInfo):
|
||||
resource_type = "featureType"
|
||||
save_method = "PUT"
|
||||
|
||||
def __init__(self, catalog, workspace, store, name):
|
||||
super(FeatureType, self).__init__()
|
||||
|
||||
assert isinstance(store, ResourceInfo)
|
||||
assert isinstance(name, basestring)
|
||||
|
||||
self.catalog = catalog
|
||||
self.workspace = workspace
|
||||
self.store = store
|
||||
self.name = name
|
||||
|
||||
@property
|
||||
def href(self):
|
||||
return url(self.catalog.service_url,
|
||||
["workspaces", self.workspace.name,
|
||||
"datastores", self.store.name,
|
||||
"featuretypes", self.name + ".xml"])
|
||||
|
||||
title = xml_property("title")
|
||||
abstract = xml_property("abstract")
|
||||
enabled = xml_property("enabled")
|
||||
native_bbox = xml_property("nativeBoundingBox", bbox)
|
||||
latlon_bbox = xml_property("latLonBoundingBox", bbox)
|
||||
projection = xml_property("srs")
|
||||
projection_policy = xml_property("projectionPolicy")
|
||||
keywords = xml_property("keywords", string_list)
|
||||
attributes = xml_property("attributes", attribute_list)
|
||||
metadata_links = xml_property("metadataLinks", metadata_link_list)
|
||||
|
||||
writers = dict(
|
||||
title = write_string("title"),
|
||||
abstract = write_string("abstract"),
|
||||
enabled = write_bool("enabled"),
|
||||
nativeBoundingBox = write_bbox("nativeBoundingBox"),
|
||||
latLonBoundingBox = write_bbox("latLonBoundingBox"),
|
||||
srs = write_string("srs"),
|
||||
projectionPolicy = write_string("projectionPolicy"),
|
||||
keywords = write_string_list("keywords"),
|
||||
metadataLinks = write_metadata_link_list("metadataLinks")
|
||||
)
|
||||
|
||||
class CoverageDimension(object):
|
||||
def __init__(self, name, description, dimension_range):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.dimension_range = dimension_range
|
||||
|
||||
def coverage_dimension(node):
|
||||
name = node.find("name")
|
||||
name = name.text if name is not None else None
|
||||
description = node.find("description")
|
||||
description = description.text if description is not None else None
|
||||
range_min = node.find("range/min")
|
||||
range_max = node.find("range/max")
|
||||
dimension_range = None
|
||||
if None not in [min, max]:
|
||||
dimension_range = float(range_min.text), float(range_max.text)
|
||||
if None not in [name, description]:
|
||||
return CoverageDimension(name, description, dimension_range)
|
||||
else:
|
||||
return None # should we bomb out more spectacularly here?
|
||||
|
||||
def coverage_dimension_xml(builder, dimension):
|
||||
builder.start("coverageDimension", dict())
|
||||
builder.start("name", dict())
|
||||
builder.data(dimension.name)
|
||||
builder.end("name")
|
||||
|
||||
builder.start("description", dict())
|
||||
builder.data(dimension.description)
|
||||
builder.end("description")
|
||||
|
||||
if dimension.range is not None:
|
||||
builder.start("range", dict())
|
||||
builder.start("min", dict())
|
||||
builder.data(str(dimension.range[0]))
|
||||
builder.end("min")
|
||||
builder.start("max", dict())
|
||||
builder.data(str(dimension.range[1]))
|
||||
builder.end("max")
|
||||
builder.end("range")
|
||||
|
||||
builder.end("coverageDimension")
|
||||
|
||||
class Coverage(ResourceInfo):
|
||||
def __init__(self, catalog, workspace, store, name):
|
||||
super(Coverage, self).__init__()
|
||||
self.catalog = catalog
|
||||
self.workspace = workspace
|
||||
self.store = store
|
||||
self.name = name
|
||||
|
||||
@property
|
||||
def href(self):
|
||||
return url(self.catalog.service_url,
|
||||
["workspaces", self.workspace.name,
|
||||
"coveragestores", self.store.name,
|
||||
"coverages", self.name + ".xml"])
|
||||
|
||||
resource_type = "coverage"
|
||||
save_method = "PUT"
|
||||
|
||||
title = xml_property("title")
|
||||
abstract = xml_property("abstract")
|
||||
enabled = xml_property("enabled")
|
||||
native_bbox = xml_property("nativeBoundingBox", bbox)
|
||||
latlon_bbox = xml_property("latLonBoundingBox", bbox)
|
||||
projection = xml_property("srs")
|
||||
projection_policy = xml_property("projectionPolicy")
|
||||
keywords = xml_property("keywords", string_list)
|
||||
request_srs_list = xml_property("requestSRS", string_list)
|
||||
response_srs_list = xml_property("responseSRS", string_list)
|
||||
supported_formats = xml_property("supportedFormats", string_list)
|
||||
metadata_links = xml_property("metadataLinks", metadata_link_list)
|
||||
|
||||
writers = dict(
|
||||
title = write_string("title"),
|
||||
abstract = write_string("abstract"),
|
||||
enabled = write_bool("enabled"),
|
||||
nativeBoundingBox = write_bbox("nativeBoundingBox"),
|
||||
latLonBoundingBox = write_bbox("latLonBoundingBox"),
|
||||
srs = write_string("srs"),
|
||||
projection_policy = write_string("projectionPolicy"),
|
||||
keywords = write_string_list("keywords"),
|
||||
metadataLinks = write_metadata_link_list("metadataLinks"),
|
||||
requestSRS = write_string_list("requestSRS"),
|
||||
responseSRS = write_string_list("responseSRS"),
|
||||
supportedFormats = write_string_list("supportedFormats")
|
||||
)
|
117
python/plugins/sextante/servertools/geoserver/store.py
Normal file
117
python/plugins/sextante/servertools/geoserver/store.py
Normal file
@ -0,0 +1,117 @@
|
||||
import sextante.servertools.geoserver.workspace as ws
|
||||
from sextante.servertools.geoserver.resource import featuretype_from_index, coverage_from_index
|
||||
from sextante.servertools.geoserver.support import ResourceInfo, xml_property, key_value_pairs, \
|
||||
write_bool, write_dict, write_string, url
|
||||
|
||||
def datastore_from_index(catalog, workspace, node):
|
||||
name = node.find("name")
|
||||
return DataStore(catalog, workspace, name.text)
|
||||
|
||||
def coveragestore_from_index(catalog, workspace, node):
|
||||
name = node.find("name")
|
||||
return CoverageStore(catalog, workspace, name.text)
|
||||
|
||||
class DataStore(ResourceInfo):
|
||||
resource_type = "dataStore"
|
||||
save_method = "PUT"
|
||||
|
||||
def __init__(self, catalog, workspace, name):
|
||||
super(DataStore, self).__init__()
|
||||
|
||||
assert isinstance(workspace, ws.Workspace)
|
||||
assert isinstance(name, basestring)
|
||||
self.catalog = catalog
|
||||
self.workspace = workspace
|
||||
self.name = name
|
||||
|
||||
@property
|
||||
def href(self):
|
||||
return url(self.catalog.service_url,
|
||||
["workspaces", self.workspace.name, "datastores", self.name + ".xml"])
|
||||
|
||||
enabled = xml_property("enabled", lambda x: x.text == "true")
|
||||
name = xml_property("name")
|
||||
connection_parameters = xml_property("connectionParameters", key_value_pairs)
|
||||
|
||||
writers = dict(enabled = write_bool("enabled"),
|
||||
name = write_string("name"),
|
||||
connectionParameters = write_dict("connectionParameters"))
|
||||
|
||||
|
||||
def get_resources(self):
|
||||
res_url = url(self.catalog.service_url,
|
||||
["workspaces", self.workspace.name, "datastores", self.name, "featuretypes.xml"])
|
||||
xml = self.catalog.get_xml(res_url)
|
||||
def ft_from_node(node):
|
||||
return featuretype_from_index(self.catalog, self.workspace, self, node)
|
||||
|
||||
return [ft_from_node(node) for node in xml.findall("featureType")]
|
||||
|
||||
class UnsavedDataStore(DataStore):
|
||||
save_method = "POST"
|
||||
|
||||
def __init__(self, catalog, name, workspace):
|
||||
super(UnsavedDataStore, self).__init__(catalog, workspace, name)
|
||||
self.dirty.update(dict(
|
||||
name=name, enabled=True, connectionParameters=dict()))
|
||||
|
||||
@property
|
||||
def href(self):
|
||||
path = [ "workspaces",
|
||||
self.workspace.name, "datastores"]
|
||||
query = dict(name=self.name)
|
||||
return url(self.catalog.service_url, path, query)
|
||||
|
||||
class CoverageStore(ResourceInfo):
|
||||
resource_type = 'coverageStore'
|
||||
save_method = "PUT"
|
||||
|
||||
def __init__(self, catalog, workspace, name):
|
||||
super(CoverageStore, self).__init__()
|
||||
|
||||
assert isinstance(workspace, ws.Workspace)
|
||||
assert isinstance(name, basestring)
|
||||
|
||||
self.catalog = catalog
|
||||
self.workspace = workspace
|
||||
self.name = name
|
||||
|
||||
@property
|
||||
def href(self):
|
||||
return url(self.catalog.service_url,
|
||||
["workspaces", self.workspace.name, "coveragestores", self.name + ".xml"])
|
||||
|
||||
enabled = xml_property("enabled", lambda x: x.text == "true")
|
||||
name = xml_property("name")
|
||||
url = xml_property("url")
|
||||
type = xml_property("type")
|
||||
|
||||
writers = dict(enabled = write_bool("enabled"),
|
||||
name = write_string("name"),
|
||||
url = write_string("url"),
|
||||
type = write_string("type"))
|
||||
|
||||
|
||||
def get_resources(self):
|
||||
res_url = url(self.catalog.service_url,
|
||||
["workspaces", self.workspace.name, "coveragestores", self.name, "coverages.xml"])
|
||||
|
||||
xml = self.catalog.get_xml(res_url)
|
||||
|
||||
def cov_from_node(node):
|
||||
return coverage_from_index(self.catalog, self.workspace, self, node)
|
||||
|
||||
return [cov_from_node(node) for node in xml.findall("coverage")]
|
||||
|
||||
class UnsavedCoverageStore(CoverageStore):
|
||||
save_method = "POST"
|
||||
|
||||
def __init__(self, catalog, name, workspace):
|
||||
super(UnsavedCoverageStore, self).__init__(catalog, workspace, name)
|
||||
self.dirty.update(name=name, enabled = True, type="GeoTIFF",
|
||||
url = "file:data/")
|
||||
|
||||
@property
|
||||
def href(self):
|
||||
return url(self.catalog.service_url,
|
||||
["workspaces", self.workspace.name, "coveragestores"], dict(name=self.name))
|
46
python/plugins/sextante/servertools/geoserver/style.py
Normal file
46
python/plugins/sextante/servertools/geoserver/style.py
Normal file
@ -0,0 +1,46 @@
|
||||
from sextante.servertools.geoserver.support import ResourceInfo, url, xml_property
|
||||
|
||||
class Style(ResourceInfo):
|
||||
def __init__(self, catalog, name):
|
||||
super(Style, self).__init__()
|
||||
assert isinstance(name, basestring)
|
||||
|
||||
self.catalog = catalog
|
||||
self.name = name
|
||||
self._sld_dom = None
|
||||
|
||||
@property
|
||||
def href(self):
|
||||
return url(self.catalog.service_url, ["styles", self.name + ".xml"])
|
||||
|
||||
def body_href(self):
|
||||
return url(self.catalog.service_url, ["styles", self.name + ".sld"])
|
||||
|
||||
filename = xml_property("filename")
|
||||
|
||||
def _get_sld_dom(self):
|
||||
if self._sld_dom is None:
|
||||
self._sld_dom = self.catalog.get_xml(self.body_href())
|
||||
return self._sld_dom
|
||||
|
||||
@property
|
||||
def sld_title(self):
|
||||
user_style = self._get_sld_dom().find("{http://www.opengis.net/sld}NamedLayer/{http://www.opengis.net/sld}UserStyle")
|
||||
title_node = user_style.find("{http://www.opengis.net/sld}Title")
|
||||
return title_node.text if title_node is not None else None
|
||||
|
||||
@property
|
||||
def sld_name(self):
|
||||
user_style = self._get_sld_dom().find("{http://www.opengis.net/sld}NamedLayer/{http://www.opengis.net/sld}UserStyle")
|
||||
name_node = user_style.find("{http://www.opengis.net/sld}Name")
|
||||
return name_node.text if name_node is not None else None
|
||||
|
||||
@property
|
||||
def sld_body(self):
|
||||
content = self.catalog.http.request(self.body_href())[1]
|
||||
return content
|
||||
|
||||
def update_body(self, body):
|
||||
headers = { "Content-Type": "application/vnd.ogc.sld+xml" }
|
||||
self.catalog.http.request(
|
||||
self.body_href(), "PUT", body, headers)
|
218
python/plugins/sextante/servertools/geoserver/support.py
Normal file
218
python/plugins/sextante/servertools/geoserver/support.py
Normal file
@ -0,0 +1,218 @@
|
||||
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 <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 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")
|
||||
|
5
python/plugins/sextante/servertools/geoserver/util.py
Normal file
5
python/plugins/sextante/servertools/geoserver/util.py
Normal file
@ -0,0 +1,5 @@
|
||||
# shapefile_and_friends = None
|
||||
# shapefile_plus_sidecars = shapefile_and_friends("test/data/states")
|
||||
|
||||
def shapefile_and_friends(path):
|
||||
return dict((ext, path + "." + ext) for ext in ['shx', 'shp', 'dbf', 'prj'])
|
33
python/plugins/sextante/servertools/geoserver/workspace.py
Normal file
33
python/plugins/sextante/servertools/geoserver/workspace.py
Normal file
@ -0,0 +1,33 @@
|
||||
from sextante.servertools.geoserver.support import xml_property, write_bool, ResourceInfo, url
|
||||
|
||||
def workspace_from_index(catalog, node):
|
||||
name = node.find("name")
|
||||
return Workspace(catalog, name.text)
|
||||
|
||||
class Workspace(ResourceInfo):
|
||||
resource_type = "workspace"
|
||||
|
||||
def __init__(self, catalog, name):
|
||||
super(Workspace, self).__init__()
|
||||
self.catalog = catalog
|
||||
self.name = name
|
||||
|
||||
@property
|
||||
def href(self):
|
||||
return url(self.catalog.service_url, ["workspaces", self.name + ".xml"])
|
||||
|
||||
@property
|
||||
def coveragestore_url(self):
|
||||
return url(self.catalog.service_url, ["workspaces", self.name, "coveragestores.xml"])
|
||||
|
||||
@property
|
||||
def datastore_url(self):
|
||||
return url(self.catalog.service_url, ["workspaces", self.name, "datastores.xml"])
|
||||
|
||||
enabled = xml_property("enabled", lambda x: x.lower() == 'true')
|
||||
writers = dict(
|
||||
enabled = write_bool("enabled")
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s @ %s" % (self.name, self.href)
|
1675
python/plugins/sextante/servertools/httplib2/__init__.py
Normal file
1675
python/plugins/sextante/servertools/httplib2/__init__.py
Normal file
File diff suppressed because it is too large
Load Diff
110
python/plugins/sextante/servertools/httplib2/iri2uri.py
Normal file
110
python/plugins/sextante/servertools/httplib2/iri2uri.py
Normal file
@ -0,0 +1,110 @@
|
||||
"""
|
||||
iri2uri
|
||||
|
||||
Converts an IRI to a URI.
|
||||
|
||||
"""
|
||||
__author__ = "Joe Gregorio (joe@bitworking.org)"
|
||||
__copyright__ = "Copyright 2006, Joe Gregorio"
|
||||
__contributors__ = []
|
||||
__version__ = "1.0.0"
|
||||
__license__ = "MIT"
|
||||
__history__ = """
|
||||
"""
|
||||
|
||||
import urlparse
|
||||
|
||||
|
||||
# Convert an IRI to a URI following the rules in RFC 3987
|
||||
#
|
||||
# The characters we need to enocde and escape are defined in the spec:
|
||||
#
|
||||
# iprivate = %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD
|
||||
# ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
|
||||
# / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
|
||||
# / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
|
||||
# / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
|
||||
# / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
|
||||
# / %xD0000-DFFFD / %xE1000-EFFFD
|
||||
|
||||
escape_range = [
|
||||
(0xA0, 0xD7FF ),
|
||||
(0xE000, 0xF8FF ),
|
||||
(0xF900, 0xFDCF ),
|
||||
(0xFDF0, 0xFFEF),
|
||||
(0x10000, 0x1FFFD ),
|
||||
(0x20000, 0x2FFFD ),
|
||||
(0x30000, 0x3FFFD),
|
||||
(0x40000, 0x4FFFD ),
|
||||
(0x50000, 0x5FFFD ),
|
||||
(0x60000, 0x6FFFD),
|
||||
(0x70000, 0x7FFFD ),
|
||||
(0x80000, 0x8FFFD ),
|
||||
(0x90000, 0x9FFFD),
|
||||
(0xA0000, 0xAFFFD ),
|
||||
(0xB0000, 0xBFFFD ),
|
||||
(0xC0000, 0xCFFFD),
|
||||
(0xD0000, 0xDFFFD ),
|
||||
(0xE1000, 0xEFFFD),
|
||||
(0xF0000, 0xFFFFD ),
|
||||
(0x100000, 0x10FFFD)
|
||||
]
|
||||
|
||||
def encode(c):
|
||||
retval = c
|
||||
i = ord(c)
|
||||
for low, high in escape_range:
|
||||
if i < low:
|
||||
break
|
||||
if i >= low and i <= high:
|
||||
retval = "".join(["%%%2X" % ord(o) for o in c.encode('utf-8')])
|
||||
break
|
||||
return retval
|
||||
|
||||
|
||||
def iri2uri(uri):
|
||||
"""Convert an IRI to a URI. Note that IRIs must be
|
||||
passed in a unicode strings. That is, do not utf-8 encode
|
||||
the IRI before passing it into the function."""
|
||||
if isinstance(uri ,unicode):
|
||||
(scheme, authority, path, query, fragment) = urlparse.urlsplit(uri)
|
||||
authority = authority.encode('idna')
|
||||
# For each character in 'ucschar' or 'iprivate'
|
||||
# 1. encode as utf-8
|
||||
# 2. then %-encode each octet of that utf-8
|
||||
uri = urlparse.urlunsplit((scheme, authority, path, query, fragment))
|
||||
uri = "".join([encode(c) for c in uri])
|
||||
return uri
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
def test_uris(self):
|
||||
"""Test that URIs are invariant under the transformation."""
|
||||
invariant = [
|
||||
u"ftp://ftp.is.co.za/rfc/rfc1808.txt",
|
||||
u"http://www.ietf.org/rfc/rfc2396.txt",
|
||||
u"ldap://[2001:db8::7]/c=GB?objectClass?one",
|
||||
u"mailto:John.Doe@example.com",
|
||||
u"news:comp.infosystems.www.servers.unix",
|
||||
u"tel:+1-816-555-1212",
|
||||
u"telnet://192.0.2.16:80/",
|
||||
u"urn:oasis:names:specification:docbook:dtd:xml:4.1.2" ]
|
||||
for uri in invariant:
|
||||
self.assertEqual(uri, iri2uri(uri))
|
||||
|
||||
def test_iri(self):
|
||||
""" Test that the right type of escaping is done for each part of the URI."""
|
||||
self.assertEqual("http://xn--o3h.com/%E2%98%84", iri2uri(u"http://\N{COMET}.com/\N{COMET}"))
|
||||
self.assertEqual("http://bitworking.org/?fred=%E2%98%84", iri2uri(u"http://bitworking.org/?fred=\N{COMET}"))
|
||||
self.assertEqual("http://bitworking.org/#%E2%98%84", iri2uri(u"http://bitworking.org/#\N{COMET}"))
|
||||
self.assertEqual("#%E2%98%84", iri2uri(u"#\N{COMET}"))
|
||||
self.assertEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}"))
|
||||
self.assertEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}")))
|
||||
self.assertNotEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}".encode('utf-8')))
|
||||
|
||||
unittest.main()
|
||||
|
||||
|
438
python/plugins/sextante/servertools/httplib2/socks.py
Normal file
438
python/plugins/sextante/servertools/httplib2/socks.py
Normal file
@ -0,0 +1,438 @@
|
||||
"""SocksiPy - Python SOCKS module.
|
||||
Version 1.00
|
||||
|
||||
Copyright 2006 Dan-Haim. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. Neither the name of Dan Haim nor the names of his contributors may be used
|
||||
to endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
|
||||
|
||||
|
||||
This module provides a standard socket-like interface for Python
|
||||
for tunneling connections through SOCKS proxies.
|
||||
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
Minor modifications made by Christopher Gilbert (http://motomastyle.com/)
|
||||
for use in PyLoris (http://pyloris.sourceforge.net/)
|
||||
|
||||
Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/)
|
||||
mainly to merge bug fixes found in Sourceforge
|
||||
|
||||
"""
|
||||
|
||||
import base64
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
|
||||
if getattr(socket, 'socket', None) is None:
|
||||
raise ImportError('socket.socket missing, proxy support unusable')
|
||||
|
||||
PROXY_TYPE_SOCKS4 = 1
|
||||
PROXY_TYPE_SOCKS5 = 2
|
||||
PROXY_TYPE_HTTP = 3
|
||||
PROXY_TYPE_HTTP_NO_TUNNEL = 4
|
||||
|
||||
_defaultproxy = None
|
||||
_orgsocket = socket.socket
|
||||
|
||||
class ProxyError(Exception): pass
|
||||
class GeneralProxyError(ProxyError): pass
|
||||
class Socks5AuthError(ProxyError): pass
|
||||
class Socks5Error(ProxyError): pass
|
||||
class Socks4Error(ProxyError): pass
|
||||
class HTTPError(ProxyError): pass
|
||||
|
||||
_generalerrors = ("success",
|
||||
"invalid data",
|
||||
"not connected",
|
||||
"not available",
|
||||
"bad proxy type",
|
||||
"bad input")
|
||||
|
||||
_socks5errors = ("succeeded",
|
||||
"general SOCKS server failure",
|
||||
"connection not allowed by ruleset",
|
||||
"Network unreachable",
|
||||
"Host unreachable",
|
||||
"Connection refused",
|
||||
"TTL expired",
|
||||
"Command not supported",
|
||||
"Address type not supported",
|
||||
"Unknown error")
|
||||
|
||||
_socks5autherrors = ("succeeded",
|
||||
"authentication is required",
|
||||
"all offered authentication methods were rejected",
|
||||
"unknown username or invalid password",
|
||||
"unknown error")
|
||||
|
||||
_socks4errors = ("request granted",
|
||||
"request rejected or failed",
|
||||
"request rejected because SOCKS server cannot connect to identd on the client",
|
||||
"request rejected because the client program and identd report different user-ids",
|
||||
"unknown error")
|
||||
|
||||
def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
|
||||
"""setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
|
||||
Sets a default proxy which all further socksocket objects will use,
|
||||
unless explicitly changed.
|
||||
"""
|
||||
global _defaultproxy
|
||||
_defaultproxy = (proxytype, addr, port, rdns, username, password)
|
||||
|
||||
def wrapmodule(module):
|
||||
"""wrapmodule(module)
|
||||
Attempts to replace a module's socket library with a SOCKS socket. Must set
|
||||
a default proxy using setdefaultproxy(...) first.
|
||||
This will only work on modules that import socket directly into the namespace;
|
||||
most of the Python Standard Library falls into this category.
|
||||
"""
|
||||
if _defaultproxy != None:
|
||||
module.socket.socket = socksocket
|
||||
else:
|
||||
raise GeneralProxyError((4, "no proxy specified"))
|
||||
|
||||
class socksocket(socket.socket):
|
||||
"""socksocket([family[, type[, proto]]]) -> socket object
|
||||
Open a SOCKS enabled socket. The parameters are the same as
|
||||
those of the standard socket init. In order for SOCKS to work,
|
||||
you must specify family=AF_INET, type=SOCK_STREAM and proto=0.
|
||||
"""
|
||||
|
||||
def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None):
|
||||
_orgsocket.__init__(self, family, type, proto, _sock)
|
||||
if _defaultproxy != None:
|
||||
self.__proxy = _defaultproxy
|
||||
else:
|
||||
self.__proxy = (None, None, None, None, None, None)
|
||||
self.__proxysockname = None
|
||||
self.__proxypeername = None
|
||||
self.__httptunnel = True
|
||||
|
||||
def __recvall(self, count):
|
||||
"""__recvall(count) -> data
|
||||
Receive EXACTLY the number of bytes requested from the socket.
|
||||
Blocks until the required number of bytes have been received.
|
||||
"""
|
||||
data = self.recv(count)
|
||||
while len(data) < count:
|
||||
d = self.recv(count-len(data))
|
||||
if not d: raise GeneralProxyError((0, "connection closed unexpectedly"))
|
||||
data = data + d
|
||||
return data
|
||||
|
||||
def sendall(self, content, *args):
|
||||
""" override socket.socket.sendall method to rewrite the header
|
||||
for non-tunneling proxies if needed
|
||||
"""
|
||||
if not self.__httptunnel:
|
||||
content = self.__rewriteproxy(content)
|
||||
return super(socksocket, self).sendall(content, *args)
|
||||
|
||||
def __rewriteproxy(self, header):
|
||||
""" rewrite HTTP request headers to support non-tunneling proxies
|
||||
(i.e. those which do not support the CONNECT method).
|
||||
This only works for HTTP (not HTTPS) since HTTPS requires tunneling.
|
||||
"""
|
||||
host, endpt = None, None
|
||||
hdrs = header.split("\r\n")
|
||||
for hdr in hdrs:
|
||||
if hdr.lower().startswith("host:"):
|
||||
host = hdr
|
||||
elif hdr.lower().startswith("get") or hdr.lower().startswith("post"):
|
||||
endpt = hdr
|
||||
if host and endpt:
|
||||
hdrs.remove(host)
|
||||
hdrs.remove(endpt)
|
||||
host = host.split(" ")[1]
|
||||
endpt = endpt.split(" ")
|
||||
if (self.__proxy[4] != None and self.__proxy[5] != None):
|
||||
hdrs.insert(0, self.__getauthheader())
|
||||
hdrs.insert(0, "Host: %s" % host)
|
||||
hdrs.insert(0, "%s http://%s%s %s" % (endpt[0], host, endpt[1], endpt[2]))
|
||||
return "\r\n".join(hdrs)
|
||||
|
||||
def __getauthheader(self):
|
||||
auth = self.__proxy[4] + ":" + self.__proxy[5]
|
||||
return "Proxy-Authorization: Basic " + base64.b64encode(auth)
|
||||
|
||||
def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
|
||||
"""setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
|
||||
Sets the proxy to be used.
|
||||
proxytype - The type of the proxy to be used. Three types
|
||||
are supported: PROXY_TYPE_SOCKS4 (including socks4a),
|
||||
PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
|
||||
addr - The address of the server (IP or DNS).
|
||||
port - The port of the server. Defaults to 1080 for SOCKS
|
||||
servers and 8080 for HTTP proxy servers.
|
||||
rdns - Should DNS queries be preformed on the remote side
|
||||
(rather than the local side). The default is True.
|
||||
Note: This has no effect with SOCKS4 servers.
|
||||
username - Username to authenticate with to the server.
|
||||
The default is no authentication.
|
||||
password - Password to authenticate with to the server.
|
||||
Only relevant when username is also provided.
|
||||
"""
|
||||
self.__proxy = (proxytype, addr, port, rdns, username, password)
|
||||
|
||||
def __negotiatesocks5(self, destaddr, destport):
|
||||
"""__negotiatesocks5(self,destaddr,destport)
|
||||
Negotiates a connection through a SOCKS5 server.
|
||||
"""
|
||||
# First we'll send the authentication packages we support.
|
||||
if (self.__proxy[4]!=None) and (self.__proxy[5]!=None):
|
||||
# The username/password details were supplied to the
|
||||
# setproxy method so we support the USERNAME/PASSWORD
|
||||
# authentication (in addition to the standard none).
|
||||
self.sendall(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02))
|
||||
else:
|
||||
# No username/password were entered, therefore we
|
||||
# only support connections with no authentication.
|
||||
self.sendall(struct.pack('BBB', 0x05, 0x01, 0x00))
|
||||
# We'll receive the server's response to determine which
|
||||
# method was selected
|
||||
chosenauth = self.__recvall(2)
|
||||
if chosenauth[0:1] != chr(0x05).encode():
|
||||
self.close()
|
||||
raise GeneralProxyError((1, _generalerrors[1]))
|
||||
# Check the chosen authentication method
|
||||
if chosenauth[1:2] == chr(0x00).encode():
|
||||
# No authentication is required
|
||||
pass
|
||||
elif chosenauth[1:2] == chr(0x02).encode():
|
||||
# Okay, we need to perform a basic username/password
|
||||
# authentication.
|
||||
self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5])
|
||||
authstat = self.__recvall(2)
|
||||
if authstat[0:1] != chr(0x01).encode():
|
||||
# Bad response
|
||||
self.close()
|
||||
raise GeneralProxyError((1, _generalerrors[1]))
|
||||
if authstat[1:2] != chr(0x00).encode():
|
||||
# Authentication failed
|
||||
self.close()
|
||||
raise Socks5AuthError((3, _socks5autherrors[3]))
|
||||
# Authentication succeeded
|
||||
else:
|
||||
# Reaching here is always bad
|
||||
self.close()
|
||||
if chosenauth[1] == chr(0xFF).encode():
|
||||
raise Socks5AuthError((2, _socks5autherrors[2]))
|
||||
else:
|
||||
raise GeneralProxyError((1, _generalerrors[1]))
|
||||
# Now we can request the actual connection
|
||||
req = struct.pack('BBB', 0x05, 0x01, 0x00)
|
||||
# If the given destination address is an IP address, we'll
|
||||
# use the IPv4 address request even if remote resolving was specified.
|
||||
try:
|
||||
ipaddr = socket.inet_aton(destaddr)
|
||||
req = req + chr(0x01).encode() + ipaddr
|
||||
except socket.error:
|
||||
# Well it's not an IP number, so it's probably a DNS name.
|
||||
if self.__proxy[3]:
|
||||
# Resolve remotely
|
||||
ipaddr = None
|
||||
req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr
|
||||
else:
|
||||
# Resolve locally
|
||||
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
|
||||
req = req + chr(0x01).encode() + ipaddr
|
||||
req = req + struct.pack(">H", destport)
|
||||
self.sendall(req)
|
||||
# Get the response
|
||||
resp = self.__recvall(4)
|
||||
if resp[0:1] != chr(0x05).encode():
|
||||
self.close()
|
||||
raise GeneralProxyError((1, _generalerrors[1]))
|
||||
elif resp[1:2] != chr(0x00).encode():
|
||||
# Connection failed
|
||||
self.close()
|
||||
if ord(resp[1:2])<=8:
|
||||
raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
|
||||
else:
|
||||
raise Socks5Error((9, _socks5errors[9]))
|
||||
# Get the bound address/port
|
||||
elif resp[3:4] == chr(0x01).encode():
|
||||
boundaddr = self.__recvall(4)
|
||||
elif resp[3:4] == chr(0x03).encode():
|
||||
resp = resp + self.recv(1)
|
||||
boundaddr = self.__recvall(ord(resp[4:5]))
|
||||
else:
|
||||
self.close()
|
||||
raise GeneralProxyError((1,_generalerrors[1]))
|
||||
boundport = struct.unpack(">H", self.__recvall(2))[0]
|
||||
self.__proxysockname = (boundaddr, boundport)
|
||||
if ipaddr != None:
|
||||
self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
|
||||
else:
|
||||
self.__proxypeername = (destaddr, destport)
|
||||
|
||||
def getproxysockname(self):
|
||||
"""getsockname() -> address info
|
||||
Returns the bound IP address and port number at the proxy.
|
||||
"""
|
||||
return self.__proxysockname
|
||||
|
||||
def getproxypeername(self):
|
||||
"""getproxypeername() -> address info
|
||||
Returns the IP and port number of the proxy.
|
||||
"""
|
||||
return _orgsocket.getpeername(self)
|
||||
|
||||
def getpeername(self):
|
||||
"""getpeername() -> address info
|
||||
Returns the IP address and port number of the destination
|
||||
machine (note: getproxypeername returns the proxy)
|
||||
"""
|
||||
return self.__proxypeername
|
||||
|
||||
def __negotiatesocks4(self,destaddr,destport):
|
||||
"""__negotiatesocks4(self,destaddr,destport)
|
||||
Negotiates a connection through a SOCKS4 server.
|
||||
"""
|
||||
# Check if the destination address provided is an IP address
|
||||
rmtrslv = False
|
||||
try:
|
||||
ipaddr = socket.inet_aton(destaddr)
|
||||
except socket.error:
|
||||
# It's a DNS name. Check where it should be resolved.
|
||||
if self.__proxy[3]:
|
||||
ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)
|
||||
rmtrslv = True
|
||||
else:
|
||||
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
|
||||
# Construct the request packet
|
||||
req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr
|
||||
# The username parameter is considered userid for SOCKS4
|
||||
if self.__proxy[4] != None:
|
||||
req = req + self.__proxy[4]
|
||||
req = req + chr(0x00).encode()
|
||||
# DNS name if remote resolving is required
|
||||
# NOTE: This is actually an extension to the SOCKS4 protocol
|
||||
# called SOCKS4A and may not be supported in all cases.
|
||||
if rmtrslv:
|
||||
req = req + destaddr + chr(0x00).encode()
|
||||
self.sendall(req)
|
||||
# Get the response from the server
|
||||
resp = self.__recvall(8)
|
||||
if resp[0:1] != chr(0x00).encode():
|
||||
# Bad data
|
||||
self.close()
|
||||
raise GeneralProxyError((1,_generalerrors[1]))
|
||||
if resp[1:2] != chr(0x5A).encode():
|
||||
# Server returned an error
|
||||
self.close()
|
||||
if ord(resp[1:2]) in (91, 92, 93):
|
||||
self.close()
|
||||
raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90]))
|
||||
else:
|
||||
raise Socks4Error((94, _socks4errors[4]))
|
||||
# Get the bound address/port
|
||||
self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0])
|
||||
if rmtrslv != None:
|
||||
self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
|
||||
else:
|
||||
self.__proxypeername = (destaddr, destport)
|
||||
|
||||
def __negotiatehttp(self, destaddr, destport):
|
||||
"""__negotiatehttp(self,destaddr,destport)
|
||||
Negotiates a connection through an HTTP server.
|
||||
"""
|
||||
# If we need to resolve locally, we do this now
|
||||
if not self.__proxy[3]:
|
||||
addr = socket.gethostbyname(destaddr)
|
||||
else:
|
||||
addr = destaddr
|
||||
headers = ["CONNECT ", addr, ":", str(destport), " HTTP/1.1\r\n"]
|
||||
headers += ["Host: ", destaddr, "\r\n"]
|
||||
if (self.__proxy[4] != None and self.__proxy[5] != None):
|
||||
headers += [self.__getauthheader(), "\r\n"]
|
||||
headers.append("\r\n")
|
||||
self.sendall("".join(headers).encode())
|
||||
# We read the response until we get the string "\r\n\r\n"
|
||||
resp = self.recv(1)
|
||||
while resp.find("\r\n\r\n".encode()) == -1:
|
||||
resp = resp + self.recv(1)
|
||||
# We just need the first line to check if the connection
|
||||
# was successful
|
||||
statusline = resp.splitlines()[0].split(" ".encode(), 2)
|
||||
if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()):
|
||||
self.close()
|
||||
raise GeneralProxyError((1, _generalerrors[1]))
|
||||
try:
|
||||
statuscode = int(statusline[1])
|
||||
except ValueError:
|
||||
self.close()
|
||||
raise GeneralProxyError((1, _generalerrors[1]))
|
||||
if statuscode != 200:
|
||||
self.close()
|
||||
raise HTTPError((statuscode, statusline[2]))
|
||||
self.__proxysockname = ("0.0.0.0", 0)
|
||||
self.__proxypeername = (addr, destport)
|
||||
|
||||
def connect(self, destpair):
|
||||
"""connect(self, despair)
|
||||
Connects to the specified destination through a proxy.
|
||||
destpar - A tuple of the IP/DNS address and the port number.
|
||||
(identical to socket's connect).
|
||||
To select the proxy server use setproxy().
|
||||
"""
|
||||
# Do a minimal input check first
|
||||
if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (not isinstance(destpair[0], basestring)) or (type(destpair[1]) != int):
|
||||
raise GeneralProxyError((5, _generalerrors[5]))
|
||||
if self.__proxy[0] == PROXY_TYPE_SOCKS5:
|
||||
if self.__proxy[2] != None:
|
||||
portnum = self.__proxy[2]
|
||||
else:
|
||||
portnum = 1080
|
||||
_orgsocket.connect(self, (self.__proxy[1], portnum))
|
||||
self.__negotiatesocks5(destpair[0], destpair[1])
|
||||
elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
|
||||
if self.__proxy[2] != None:
|
||||
portnum = self.__proxy[2]
|
||||
else:
|
||||
portnum = 1080
|
||||
_orgsocket.connect(self,(self.__proxy[1], portnum))
|
||||
self.__negotiatesocks4(destpair[0], destpair[1])
|
||||
elif self.__proxy[0] == PROXY_TYPE_HTTP:
|
||||
if self.__proxy[2] != None:
|
||||
portnum = self.__proxy[2]
|
||||
else:
|
||||
portnum = 8080
|
||||
_orgsocket.connect(self,(self.__proxy[1], portnum))
|
||||
self.__negotiatehttp(destpair[0], destpair[1])
|
||||
elif self.__proxy[0] == PROXY_TYPE_HTTP_NO_TUNNEL:
|
||||
if self.__proxy[2] != None:
|
||||
portnum = self.__proxy[2]
|
||||
else:
|
||||
portnum = 8080
|
||||
_orgsocket.connect(self,(self.__proxy[1],portnum))
|
||||
if destpair[1] == 443:
|
||||
self.__negotiatehttp(destpair[0],destpair[1])
|
||||
else:
|
||||
self.__httptunnel = False
|
||||
elif self.__proxy[0] == None:
|
||||
_orgsocket.connect(self, (destpair[0], destpair[1]))
|
||||
else:
|
||||
raise GeneralProxyError((4, _generalerrors[4]))
|
@ -1,104 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
ExampleAlgorithm.py
|
||||
---------------------
|
||||
Date : August 2012
|
||||
Copyright : (C) 2012 by Tim Sutton
|
||||
Email : tim at linfiniti 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__ = 'Tim Sutton'
|
||||
__date__ = 'August 2012'
|
||||
__copyright__ = '(C) 2012, Tim Sutton'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
from sextante.core.GeoAlgorithm import GeoAlgorithm
|
||||
from sextante.outputs.OutputVector import OutputVector
|
||||
from sextante.parameters.ParameterVector import ParameterVector
|
||||
from sextante.core.Sextante import Sextante
|
||||
from qgis.core import *
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
from sextante.parameters.ParameterExtent import ParameterExtent
|
||||
from sextante.parameters.ParameterCrs import ParameterCrs
|
||||
|
||||
|
||||
class ExampleAlgorithm(GeoAlgorithm):
|
||||
'''This is an example algorithm that takes a vector layer and creates
|
||||
a new one just with just those features of the input layer that are
|
||||
selected.
|
||||
It is meant to be used as an example of how to create your own SEXTANTE
|
||||
algorithms and explain methods and variables used to do it.
|
||||
An algorithm like this will be available in all SEXTANTE elements, and
|
||||
there is not need for additional work.
|
||||
|
||||
All SEXTANTE algorithms should extend the GeoAlgorithm class'''
|
||||
|
||||
#constants used to refer to parameters and outputs.
|
||||
#They will be used when calling the algorithm from another algorithm,
|
||||
#or when calling SEXTANTE from the QGIS console.
|
||||
OUTPUT_LAYER = "OUTPUT_LAYER"
|
||||
INPUT_LAYER = "INPUT_LAYER"
|
||||
|
||||
def defineCharacteristics(self):
|
||||
'''Here we define the inputs and output of the algorithm, along
|
||||
with some other properties'''
|
||||
|
||||
#the name that the user will see in the toolbox
|
||||
self.name = "Create new layer with selected features"
|
||||
|
||||
#the branch of the toolbox under which the algorithm will appear
|
||||
self.group = "Algorithms for vector layers"
|
||||
|
||||
#we add the input vector layer. It can have any kind of geometry
|
||||
#It is a mandatory (not optional) one, hence the False argument
|
||||
self.addParameter(ParameterVector(self.INPUT_LAYER, "Input layer", ParameterVector.VECTOR_TYPE_ANY, False))
|
||||
self.addParameter(ParameterExtent("EXTENT","EXTENT"))
|
||||
self.addParameter(ParameterCrs("CRS", "CRS"))
|
||||
# we add a vector layer as output
|
||||
self.addOutput(OutputVector(self.OUTPUT_LAYER, "Output layer with selected features"))
|
||||
|
||||
|
||||
def processAlgorithm(self, progress):
|
||||
'''Here is where the processing itself takes place'''
|
||||
|
||||
#the first thing to do is retrieve the values of the parameters
|
||||
#entered by the user
|
||||
inputFilename = self.getParameterValue(self.INPUT_LAYER)
|
||||
output = self.getOutputValue(self.OUTPUT_LAYER)
|
||||
|
||||
#input layers values are always a string with its location.
|
||||
#That string can be converted into a QGIS object (a QgsVectorLayer in this case))
|
||||
#using the Sextante.getObject() method
|
||||
vectorLayer = Sextante.getObject(inputFilename)
|
||||
|
||||
#And now we can process
|
||||
|
||||
#First we create the output layer.
|
||||
#The output value entered by the user is a string containing a filename,
|
||||
#so we can use it directly
|
||||
settings = QSettings()
|
||||
systemEncoding = settings.value( "/UI/encoding", "System" ).toString()
|
||||
provider = vectorLayer.dataProvider()
|
||||
writer = QgsVectorFileWriter( output, systemEncoding, provider.fields(), provider.geometryType(), provider.crs() )
|
||||
|
||||
#Now we take the selected features and add them to the output layer
|
||||
selection = vectorLayer.selectedFeatures()
|
||||
for feat in selection:
|
||||
writer.addFeature(feat)
|
||||
del writer
|
||||
|
||||
#There is nothing more to do here. We do not have to open the layer that we have created.
|
||||
#SEXTANTE will take care of that, or will handle it if this algorithm is executed within
|
||||
#a complex model
|
@ -1,78 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
ExampleAlgorithmProvider.py
|
||||
---------------------
|
||||
Date : August 2012
|
||||
Copyright : (C) 2012 by Tim Sutton
|
||||
Email : tim at linfiniti 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__ = 'Tim Sutton'
|
||||
__date__ = 'August 2012'
|
||||
__copyright__ = '(C) 2012, Tim Sutton'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
from sextante.core.AlgorithmProvider import AlgorithmProvider
|
||||
from sextanteexampleprovider.ExampleAlgorithm import ExampleAlgorithm
|
||||
from sextante.core.SextanteConfig import Setting, SextanteConfig
|
||||
|
||||
class ExampleAlgorithmProvider(AlgorithmProvider):
|
||||
|
||||
MY_DUMMY_SETTING = "MY_DUMMY_SETTING"
|
||||
|
||||
def __init__(self):
|
||||
AlgorithmProvider.__init__(self)
|
||||
self.alglist = [ExampleAlgorithm()]
|
||||
|
||||
def initializeSettings(self):
|
||||
'''In this method we add settings needed to configure our provider.
|
||||
Do not forget to call the parent method, since it takes care or
|
||||
automatically adding a setting for activating or deactivating the
|
||||
algorithms in the provider'''
|
||||
AlgorithmProvider.initializeSettings(self)
|
||||
SextanteConfig.addSetting(Setting("Example algorithms", ExampleAlgorithmProvider.MY_DUMMY_SETTING, "Example setting", "Default value"))
|
||||
'''To get the parameter of a setting parameter, use SextanteConfig.getSetting(name_of_parameter)'''
|
||||
|
||||
def unload(self):
|
||||
'''Setting should be removed here, so they do not appear anymore
|
||||
when the plugin is unloaded'''
|
||||
AlgorithmProvider.unload(self)
|
||||
SextanteConfig.removeSetting( ExampleAlgorithmProvider.MY_DUMMY_SETTING)
|
||||
|
||||
|
||||
def getName(self):
|
||||
'''This name is used to create the command line name of all the algorithms
|
||||
from this provider'''
|
||||
return "exampleprovider"
|
||||
|
||||
def getDescription(self):
|
||||
'''This is the name that will appear on the toolbox group.'''
|
||||
return "Example algorithms"
|
||||
|
||||
def getIcon(self):
|
||||
'''We return the default icon'''
|
||||
return AlgorithmProvider.getIcon(self)
|
||||
|
||||
|
||||
def _loadAlgorithms(self):
|
||||
'''Here we fill the list of algorithms in self.algs.
|
||||
This method is called whenever the list of algorithms should be updated.
|
||||
If the list of algorithms can change while executing SEXTANTE for QGIS
|
||||
(for instance, if it contains algorithms from user-defined scripts and
|
||||
a new script might have been added), you should create the list again
|
||||
here.
|
||||
In this case, since the list is always the same, we assign from the pre-made list.
|
||||
This assignment has to be done in this method even if the list does not change,
|
||||
since the self.algs list is cleared before calling this method'''
|
||||
self.algs = self.alglist
|
@ -1,46 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
SextanteExampleProviderPlugin.py
|
||||
---------------------
|
||||
Date : August 2012
|
||||
Copyright : (C) 2012 by Tim Sutton
|
||||
Email : tim at linfiniti 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__ = 'Tim Sutton'
|
||||
__date__ = 'August 2012'
|
||||
__copyright__ = '(C) 2012, Tim Sutton'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
from qgis.core import *
|
||||
import os, sys
|
||||
import inspect
|
||||
from sextante.core.Sextante import Sextante
|
||||
from sextanteexampleprovider.ExampleAlgorithmProvider import ExampleAlgorithmProvider
|
||||
|
||||
|
||||
cmd_folder = os.path.split(inspect.getfile( inspect.currentframe() ))[0]
|
||||
if cmd_folder not in sys.path:
|
||||
sys.path.insert(0, cmd_folder)
|
||||
|
||||
class SextanteExampleProviderPlugin:
|
||||
|
||||
def __init__(self):
|
||||
self.provider = ExampleAlgorithmProvider()
|
||||
def initGui(self):
|
||||
Sextante.addProvider(self.provider)
|
||||
|
||||
def unload(self):
|
||||
Sextante.removeProvider(self.provider)
|
||||
|
@ -1,11 +0,0 @@
|
||||
[general]
|
||||
name=SEXTANTE Example Provider
|
||||
description=An example plugin that adds algorithms to SEXTANTE. Mainly created to guide developers in the process of creating plugins that add new capabilities to SEXTANTE
|
||||
version=1.0
|
||||
qgisMinimumVersion=1.0
|
||||
|
||||
name=Victor Olaya
|
||||
email=volayaf@gmail.com
|
||||
website=www.sextantegis.com
|
||||
|
||||
class_name=SextanteExampleProviderPlugin
|
Loading…
x
Reference in New Issue
Block a user