Adding support for chaining processes.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
OpenLayers.ProxyHost = 'proxy.cgi?url=';
|
OpenLayers.ProxyHost = 'proxy.cgi?url=';
|
||||||
|
|
||||||
var map, client, process;
|
var map, client, intersect, buffer;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
|
||||||
@@ -23,18 +23,27 @@ function init() {
|
|||||||
|
|
||||||
client = new OpenLayers.WPSClient({
|
client = new OpenLayers.WPSClient({
|
||||||
servers: {
|
servers: {
|
||||||
local: "http://demo.opengeo.org/geoserver/wps"
|
opengeo: 'http://demo.opengeo.org/geoserver/wps'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a process and execute it
|
// Create a process and configure it
|
||||||
process = client.getProcess("local", "JTS:intersection");
|
intersect = client.getProcess('opengeo', 'JTS:intersection');
|
||||||
process.execute({
|
intersect.configure({
|
||||||
// spatial input can be a feature or a geometry or an array of
|
// spatial input can be a feature or a geometry or an array of
|
||||||
// features or geometries
|
// features or geometries
|
||||||
inputs: {
|
inputs: {
|
||||||
a: features,
|
a: features,
|
||||||
b: geometry
|
b: geometry
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create another process which chains the previous one and execute it
|
||||||
|
buffer = client.getProcess('opengeo', 'JTS:buffer');
|
||||||
|
buffer.execute({
|
||||||
|
inputs: {
|
||||||
|
geom: intersect.output(),
|
||||||
|
distance: 1
|
||||||
},
|
},
|
||||||
success: function(outputs) {
|
success: function(outputs) {
|
||||||
// outputs.result is a feature or an array of features for spatial
|
// outputs.result is a feature or an array of features for spatial
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class: OpenLayers.WPSProces
|
* Class: OpenLayers.WPSProcess
|
||||||
*/
|
*/
|
||||||
OpenLayers.WPSProcess = OpenLayers.Class({
|
OpenLayers.WPSProcess = OpenLayers.Class({
|
||||||
|
|
||||||
@@ -23,7 +23,8 @@ OpenLayers.WPSProcess = OpenLayers.Class({
|
|||||||
* {<OpenLayers.Events>}
|
* {<OpenLayers.Events>}
|
||||||
*
|
*
|
||||||
* Supported event types:
|
* Supported event types:
|
||||||
* describeprocess - fires when the process description is available
|
* describeprocess - Fires when the process description is available for
|
||||||
|
* the first time.
|
||||||
*/
|
*/
|
||||||
events: null,
|
events: null,
|
||||||
|
|
||||||
@@ -57,6 +58,20 @@ OpenLayers.WPSProcess = OpenLayers.Class({
|
|||||||
*/
|
*/
|
||||||
formats: null,
|
formats: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property: chained
|
||||||
|
* {Integer} Number of chained processes for pending execute reqeusts that
|
||||||
|
* don't have a full configuration yet.
|
||||||
|
*/
|
||||||
|
chained: 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property: executeCallbacks
|
||||||
|
* {Array} Callbacks waiting to be executed until all chained processes
|
||||||
|
* are configured;
|
||||||
|
*/
|
||||||
|
executeCallbacks: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor: OpenLayers.WPSProcess
|
* Constructor: OpenLayers.WPSProcess
|
||||||
*
|
*
|
||||||
@@ -74,6 +89,7 @@ OpenLayers.WPSProcess = OpenLayers.Class({
|
|||||||
OpenLayers.Util.extend(this, options);
|
OpenLayers.Util.extend(this, options);
|
||||||
|
|
||||||
this.events = new OpenLayers.Events(this);
|
this.events = new OpenLayers.Events(this);
|
||||||
|
this.executeCallbacks = [];
|
||||||
|
|
||||||
this.formats = {
|
this.formats = {
|
||||||
'application/wkt': new OpenLayers.Format.WKT(),
|
'application/wkt': new OpenLayers.Format.WKT(),
|
||||||
@@ -86,17 +102,38 @@ OpenLayers.WPSProcess = OpenLayers.Class({
|
|||||||
* Issues a DescribeProcess request asynchronously and fires the
|
* Issues a DescribeProcess request asynchronously and fires the
|
||||||
* 'describeprocess' event as soon as the response is available in
|
* 'describeprocess' event as soon as the response is available in
|
||||||
* <description>.
|
* <description>.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* options - {Object} Coniguration for the method call
|
||||||
|
*
|
||||||
|
* Available options:
|
||||||
|
* callback - {Function} Callback to execute when the description is
|
||||||
|
* available. Will be called with the parsed description as argument.
|
||||||
|
* Optional.
|
||||||
|
* scope - {Object} The scope in which the callback will be executed.
|
||||||
|
* Default is the global object.
|
||||||
*/
|
*/
|
||||||
describe: function() {
|
describe: function(options) {
|
||||||
|
options = options || {};
|
||||||
|
function callback() {
|
||||||
|
if (options.callback) {
|
||||||
|
window.setTimeout(function() {
|
||||||
|
options.callback.call(options.scope, this.description);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
var server = this.client.servers[this.server];
|
var server = this.client.servers[this.server];
|
||||||
if (this.description !== null) {
|
if (this.description !== null) {
|
||||||
|
callback();
|
||||||
return;
|
return;
|
||||||
} else if (server.describeProcessResponse[this.identifier] === null) {
|
} else if (server.describeProcessResponse[this.identifier] === null) {
|
||||||
// pending request
|
// pending request
|
||||||
|
this.events.register('describeprocess', this, callback);
|
||||||
return;
|
return;
|
||||||
} else if (this.identifier in server.describeProcessResponse) {
|
} else if (this.identifier in server.describeProcessResponse) {
|
||||||
// process description already cached on client
|
// process description already cached on client
|
||||||
this.parseDescription();
|
this.parseDescription();
|
||||||
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// set to null so we know a describeFeature request is pending
|
// set to null so we know a describeFeature request is pending
|
||||||
@@ -109,14 +146,21 @@ OpenLayers.WPSProcess = OpenLayers.Class({
|
|||||||
REQUEST: 'DescribeProcess',
|
REQUEST: 'DescribeProcess',
|
||||||
IDENTIFIER: this.identifier
|
IDENTIFIER: this.identifier
|
||||||
},
|
},
|
||||||
success: this.parseDescription,
|
success: function(response) {
|
||||||
|
this.parseDescription(response);
|
||||||
|
if (options.callback) {
|
||||||
|
options.callback.call(options.scope, this.description);
|
||||||
|
}
|
||||||
|
},
|
||||||
scope: this
|
scope: this
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* APIMethod: execute
|
* APIMethod: configure
|
||||||
* Executes the process
|
* Configure the process, but do not execute it. Use this for processes
|
||||||
|
* that are chained as input of a different process by means of the
|
||||||
|
* <output> method.
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* options - {Object}
|
* options - {Object}
|
||||||
@@ -126,20 +170,18 @@ OpenLayers.WPSProcess = OpenLayers.Class({
|
|||||||
* For spatial data inputs, the value of an input is usually an
|
* For spatial data inputs, the value of an input is usually an
|
||||||
* <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of
|
* <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of
|
||||||
* geometries or features.
|
* geometries or features.
|
||||||
* success - {Function} Callback to call when the process is complete.
|
* callback - {Function} Callback to call when the configuration is
|
||||||
* This function is called with an outputs object as argument, which
|
* complete. Optional.
|
||||||
* will have a 'result' property. For processes that generate spatial
|
* scope - {Object} Optional scope for the callback.
|
||||||
* output, this will either be a single <OpenLayers.Feature.Vector> or
|
|
||||||
* an array of features.
|
|
||||||
* scope - {Object} Optional scope for the success callback.
|
|
||||||
*/
|
*/
|
||||||
execute: function(options) {
|
configure: function(options) {
|
||||||
if (!this.description) {
|
if (!this.description) {
|
||||||
this.events.register('describeprocess', this, function execute() {
|
this.describe({
|
||||||
this.events.unregister('describeprocess', this, execute);
|
callback: function() {
|
||||||
this.execute(options);
|
this.configure(options);
|
||||||
|
},
|
||||||
|
scope: this
|
||||||
});
|
});
|
||||||
this.describe();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var description = this.description,
|
var description = this.description,
|
||||||
@@ -149,26 +191,105 @@ OpenLayers.WPSProcess = OpenLayers.Class({
|
|||||||
input = description.dataInputs[i];
|
input = description.dataInputs[i];
|
||||||
this.setInputData(input, inputs[input.identifier]);
|
this.setInputData(input, inputs[input.identifier]);
|
||||||
}
|
}
|
||||||
//TODO For now we only handle responseForm with a single output
|
if (options.callback) {
|
||||||
this.setResponseForm();
|
options.callback.call(options.scope);
|
||||||
OpenLayers.Request.POST({
|
}
|
||||||
url: this.client.servers[this.server].url,
|
},
|
||||||
data: new OpenLayers.Format.WPSExecute().write(this.description),
|
|
||||||
success: function(response) {
|
/**
|
||||||
var mimeType = this.findMimeType(this.description.processOutputs[0].complexOutput);
|
* APIMethod: execute
|
||||||
var features = this.formats[mimeType].read(response.responseText);
|
* Configures and executes the process
|
||||||
if (options.success) {
|
*
|
||||||
options.success.call(options.scope, {
|
* Parameters:
|
||||||
result: features
|
* options - {Object}
|
||||||
|
*
|
||||||
|
* Available options:
|
||||||
|
* inputs - {Object} The inputs for the process, keyed by input identifier.
|
||||||
|
* For spatial data inputs, the value of an input is usually an
|
||||||
|
* <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of
|
||||||
|
* geometries or features.
|
||||||
|
* output - {String} The identifier of an output to parse. Optional. If not
|
||||||
|
* provided, the first output will be parsed.
|
||||||
|
* success - {Function} Callback to call when the process is complete.
|
||||||
|
* This function is called with an outputs object as argument, which
|
||||||
|
* will have a 'result' property. For processes that generate spatial
|
||||||
|
* output, this will either be a single <OpenLayers.Feature.Vector> or
|
||||||
|
* an array of features.
|
||||||
|
* scope - {Object} Optional scope for the success callback.
|
||||||
|
*/
|
||||||
|
execute: function(options) {
|
||||||
|
this.configure({
|
||||||
|
inputs: options.inputs,
|
||||||
|
callback: function() {
|
||||||
|
var me = this;
|
||||||
|
//TODO For now we only deal with a single output
|
||||||
|
var output = this.getOutputIndex(
|
||||||
|
me.description.processOutputs, options.output
|
||||||
|
);
|
||||||
|
me.setResponseForm({outputIndex: output});
|
||||||
|
(function callback() {
|
||||||
|
OpenLayers.Util.removeItem(me.executeCallbacks, callback);
|
||||||
|
if (me.chained !== 0) {
|
||||||
|
me.executeCallbacks.push(callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OpenLayers.Request.POST({
|
||||||
|
url: me.client.servers[me.server].url,
|
||||||
|
data: new OpenLayers.Format.WPSExecute().write(me.description),
|
||||||
|
success: function(response) {
|
||||||
|
var mimeType = me.findMimeType(
|
||||||
|
me.description.processOutputs[output].complexOutput.supported.formats
|
||||||
|
);
|
||||||
|
//TODO For now we assume a spatial output
|
||||||
|
var features = me.formats[mimeType].read(response.responseText);
|
||||||
|
if (options.success) {
|
||||||
|
options.success.call(options.scope, {
|
||||||
|
result: features
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scope: me
|
||||||
});
|
});
|
||||||
}
|
})();
|
||||||
},
|
},
|
||||||
scope: this
|
scope: this
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: output
|
||||||
|
* Chain an output of a configured process (see <configure>) as input to
|
||||||
|
* another process.
|
||||||
|
*
|
||||||
|
* (code)
|
||||||
|
* intersect = client.getProcess('opengeo', 'JTS:intersection');
|
||||||
|
* intersect.configure({
|
||||||
|
* // ...
|
||||||
|
* });
|
||||||
|
* buffer = client.getProcess('opengeo', 'JTS:buffer');
|
||||||
|
* buffer.execute({
|
||||||
|
* inputs: {
|
||||||
|
* geom: intersect.output(), // <-- here we're chaining
|
||||||
|
* distance: 1
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* });
|
||||||
|
* (end)
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* identifier - {String} Identifier of the output that we're chaining. If
|
||||||
|
* not provided, the first output will be used.
|
||||||
|
*/
|
||||||
|
output: function(identifier) {
|
||||||
|
return new OpenLayers.WPSProcess.ChainLink({
|
||||||
|
process: this,
|
||||||
|
output: identifier
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method: parseDescription
|
* Method: parseDescription
|
||||||
|
* Parses the DescribeProcess response
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* response - {Object}
|
* response - {Object}
|
||||||
@@ -197,15 +318,37 @@ OpenLayers.WPSProcess = OpenLayers.Class({
|
|||||||
*/
|
*/
|
||||||
setInputData: function(input, data) {
|
setInputData: function(input, data) {
|
||||||
// clear any previous data
|
// clear any previous data
|
||||||
input.data = {};
|
delete input.data;
|
||||||
if (data) {
|
delete input.reference;
|
||||||
|
if (data instanceof OpenLayers.WPSProcess.ChainLink) {
|
||||||
|
++this.chained;
|
||||||
|
input.reference = {
|
||||||
|
method: 'POST',
|
||||||
|
href: data.process.server === this.server ?
|
||||||
|
//TODO what about implementations other than GeoServer?
|
||||||
|
'http://geoserver/wps' :
|
||||||
|
this.client.servers[data.process.server].url
|
||||||
|
};
|
||||||
|
data.process.describe({
|
||||||
|
callback: function() {
|
||||||
|
--this.chained;
|
||||||
|
this.chainProcess(input, data);
|
||||||
|
},
|
||||||
|
scope: this
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
input.data = {};
|
||||||
var complexData = input.complexData;
|
var complexData = input.complexData;
|
||||||
if (complexData) {
|
if (complexData) {
|
||||||
var format = this.findMimeType(complexData);
|
var format = this.findMimeType(complexData.supported.formats);
|
||||||
input.data.complexData = {
|
input.data.complexData = {
|
||||||
mimeType: format,
|
mimeType: format,
|
||||||
value: this.formats[format].write(this.toFeatures(data))
|
value: this.formats[format].write(this.toFeatures(data))
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
input.data.literalData = {
|
||||||
|
value: data
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -213,17 +356,81 @@ OpenLayers.WPSProcess = OpenLayers.Class({
|
|||||||
/**
|
/**
|
||||||
* Method: setResponseForm
|
* Method: setResponseForm
|
||||||
* Sets the responseForm property of the <execute> payload.
|
* Sets the responseForm property of the <execute> payload.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* options - {Object} See below.
|
||||||
|
*
|
||||||
|
* Available options:
|
||||||
|
* outputIndex - {Integer} The index of the output to use. Optional.
|
||||||
|
* supportedFormats - {Object} Object with supported mime types as key,
|
||||||
|
* and true as value for supported types. Optional.
|
||||||
*/
|
*/
|
||||||
setResponseForm: function() {
|
setResponseForm: function(options) {
|
||||||
output = this.description.processOutputs[0];
|
options = options || {};
|
||||||
|
output = this.description.processOutputs[options.outputIndex || 0];
|
||||||
this.description.responseForm = {
|
this.description.responseForm = {
|
||||||
rawDataOutput: {
|
rawDataOutput: {
|
||||||
identifier: output.identifier,
|
identifier: output.identifier,
|
||||||
mimeType: this.findMimeType(output.complexOutput)
|
mimeType: this.findMimeType(output.complexOutput.supported.formats, options.supportedFormats)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: getOutputIndex
|
||||||
|
* Gets the index of a processOutput by its identifier
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* outputs - {Array} The processOutputs array to look at
|
||||||
|
* identifier - {String} The identifier of the output
|
||||||
|
*
|
||||||
|
* Returns
|
||||||
|
* {Integer} The index of the processOutput with the provided identifier
|
||||||
|
* in the outputs array.
|
||||||
|
*/
|
||||||
|
getOutputIndex: function(outputs, identifier) {
|
||||||
|
var output;
|
||||||
|
if (identifier) {
|
||||||
|
for (var i=outputs.length-1; i>=0; --i) {
|
||||||
|
if (outputs[i].identifier === identifier) {
|
||||||
|
output = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output = 0;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: chainProcess
|
||||||
|
* Sets a fully configured chained process as input for this process.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* input - {Object} The dataInput that the chained process provides.
|
||||||
|
* chainLink - {<OpenLayers.WPSProcess.ChainLink} The process to chain.
|
||||||
|
*/
|
||||||
|
chainProcess: function(input, chainLink) {
|
||||||
|
var output = this.getOutputIndex(
|
||||||
|
chainLink.process.description.processOutputs, chainLink.output
|
||||||
|
);
|
||||||
|
input.reference.mimeType = this.findMimeType(
|
||||||
|
input.complexData.supported.formats,
|
||||||
|
chainLink.process.description.processOutputs[output].complexOutput.supported.formats
|
||||||
|
);
|
||||||
|
var formats = {};
|
||||||
|
formats[input.reference.mimeType] = true;
|
||||||
|
chainLink.process.setResponseForm({
|
||||||
|
outputIndex: output,
|
||||||
|
supportedFormats: formats
|
||||||
|
});
|
||||||
|
input.reference.body = chainLink.process.description;
|
||||||
|
while (this.executeCallbacks.length > 0) {
|
||||||
|
this.executeCallbacks[0]();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method: toFeatures
|
* Method: toFeatures
|
||||||
* Converts spatial input into features so it can be processed by
|
* Converts spatial input into features so it can be processed by
|
||||||
@@ -256,16 +463,19 @@ OpenLayers.WPSProcess = OpenLayers.Class({
|
|||||||
* Finds a supported mime type.
|
* Finds a supported mime type.
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* complex - {Object} A complexData or complexOutput object from the
|
* sourceFormats - {Object} An object literal with mime types as key and
|
||||||
* process description.
|
* true as value for supported formats.
|
||||||
|
* targetFormats - {Object} Like <sourceFormats>, but optional to check for
|
||||||
|
* supported mime types on a different target than this process.
|
||||||
|
* Default is to check against this process's supported formats.
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* {String} A supported mime type.
|
* {String} A supported mime type.
|
||||||
*/
|
*/
|
||||||
findMimeType: function(complex) {
|
findMimeType: function(sourceFormats, targetFormats) {
|
||||||
var formats = complex.supported.formats;
|
targetFormats = targetFormats || this.formats;
|
||||||
for (var f in formats) {
|
for (var f in sourceFormats) {
|
||||||
if (f in this.formats) {
|
if (f in targetFormats) {
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,3 +484,18 @@ OpenLayers.WPSProcess = OpenLayers.Class({
|
|||||||
CLASS_NAME: "OpenLayers.WPSProcess"
|
CLASS_NAME: "OpenLayers.WPSProcess"
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class: OpenLayers.WPSProcess.ChainLink
|
||||||
|
*/
|
||||||
|
OpenLayers.WPSProcess.ChainLink = OpenLayers.Class({
|
||||||
|
|
||||||
|
process: null,
|
||||||
|
|
||||||
|
output: null,
|
||||||
|
|
||||||
|
initialize: function(options) {
|
||||||
|
OpenLayers.Util.extend(this, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user