Create control to perform SLD selections on WMS layers, r=ahocevar (closes #2570)
git-svn-id: http://svn.openlayers.org/trunk/openlayers@10290 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
196
examples/SLDSelect.html
Normal file
196
examples/SLDSelect.html
Normal file
@@ -0,0 +1,196 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>OpenLayers SLD based selection control</title>
|
||||
<link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
|
||||
<link rel="stylesheet" href="style.css" type="text/css" />
|
||||
<style type="text/css">
|
||||
.olControlSLDSelectBoxActive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.olControlSLDSelectPolygonActive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.olControlSLDSelectLineActive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.olControlSLDSelectPointActive {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<script src="../lib/OpenLayers.js"></script>
|
||||
<script type="text/javascript">
|
||||
var map, controls, layers;
|
||||
|
||||
function init(){
|
||||
OpenLayers.ProxyHost= "proxy.cgi?url=";
|
||||
map = new OpenLayers.Map('map', {allOverlays: true, controls: []});
|
||||
var url = "http://demo.opengeo.org/geoserver/wms";
|
||||
layers = {
|
||||
states: new OpenLayers.Layer.WMS("State boundary", url,
|
||||
{layers: 'topp:tasmania_state_boundaries', format: 'image/gif', transparent: 'TRUE'},
|
||||
{singleTile: true}),
|
||||
roads: new OpenLayers.Layer.WMS("Roads", url,
|
||||
{layers: 'topp:tasmania_roads', format: 'image/gif', transparent: 'TRUE'},
|
||||
{singleTile: true}),
|
||||
waterbodies: new OpenLayers.Layer.WMS("Water bodies", url,
|
||||
{layers: 'topp:tasmania_water_bodies', format: 'image/gif', transparent: 'TRUE'},
|
||||
{singleTile: true}),
|
||||
cities: new OpenLayers.Layer.WMS("Cities", url,
|
||||
{layers: 'topp:tasmania_cities', format: 'image/gif', transparent: 'TRUE'},
|
||||
{singleTile: true})
|
||||
};
|
||||
|
||||
for (var key in layers) {
|
||||
map.addLayer(layers[key]);
|
||||
}
|
||||
|
||||
map.setCenter(new OpenLayers.LonLat(146.65748632815,-42.230763671875), 7);
|
||||
|
||||
map.addControl(new OpenLayers.Control.LayerSwitcher());
|
||||
|
||||
controls = {
|
||||
navigation: new OpenLayers.Control.Navigation(),
|
||||
box: new OpenLayers.Control.SLDSelect(
|
||||
OpenLayers.Handler.RegularPolygon,
|
||||
{
|
||||
displayClass: 'olControlSLDSelectBox',
|
||||
layers: [layers['waterbodies']],
|
||||
handlerOptions: {irregular: true}
|
||||
}
|
||||
),
|
||||
polygon: new OpenLayers.Control.SLDSelect(
|
||||
OpenLayers.Handler.Polygon,
|
||||
{
|
||||
displayClass: 'olControlSLDSelectPolygon',
|
||||
layers: [layers['waterbodies']]
|
||||
}
|
||||
),
|
||||
line: new OpenLayers.Control.SLDSelect(
|
||||
OpenLayers.Handler.Path,
|
||||
{
|
||||
displayClass: 'olControlSLDSelectLine',
|
||||
layers: [layers['waterbodies']]
|
||||
}
|
||||
),
|
||||
point: new OpenLayers.Control.SLDSelect(
|
||||
OpenLayers.Handler.Click,
|
||||
{
|
||||
displayClass: 'olControlSLDSelectPoint',
|
||||
layers: [layers['waterbodies']]
|
||||
}
|
||||
),
|
||||
circle: new OpenLayers.Control.SLDSelect(
|
||||
OpenLayers.Handler.RegularPolygon,
|
||||
{
|
||||
displayClass: 'olControlSLDSelectBox',
|
||||
layers: [layers['waterbodies']],
|
||||
handlerOptions: {sides: 40}
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
for(var key in controls) {
|
||||
map.addControl(controls[key]);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleControl(element) {
|
||||
for(var key in controls) {
|
||||
var control = controls[key];
|
||||
if(element.value == key && element.checked) {
|
||||
control.activate();
|
||||
} else {
|
||||
control.deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSelectionLayer(element) {
|
||||
var selectLayers = [];
|
||||
var elements = element.value.split("_");
|
||||
for (var key in layers) {
|
||||
var layer = layers[key];
|
||||
for (var i=0, len=elements.length; i<len; i++) {
|
||||
var value = elements[i];
|
||||
if (value == key && element.checked) {
|
||||
selectLayers.push(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i=0, len=this.map.controls.length; i<len; i++) {
|
||||
var control = this.map.controls[i];
|
||||
if (control instanceof OpenLayers.Control.SLDSelect) {
|
||||
control.setLayers(selectLayers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="init()">
|
||||
<h1 id="title">SLD based selection on WMS layers</h1>
|
||||
|
||||
<div id="tags"></div>
|
||||
|
||||
<div id="shortdesc">Using Styled Layer Descriptors to make a selection on WMS layers</div>
|
||||
|
||||
<div id="map" style="width: 512; height: 256; border: 1px solid red;"></div>
|
||||
|
||||
<div id="docs">
|
||||
This example uses the OpenLayers.Control.SLDSelect to select features in a WMS
|
||||
layer. The features are highlighted using Styled Layer Descriptors (SLD). The
|
||||
control supports point, box, line and polygon selection modes by configuring the
|
||||
appriopriate handler.
|
||||
</div>
|
||||
|
||||
<div id="controls">
|
||||
<ul id="controlToggle"><b>Map Controls</b>
|
||||
<li>
|
||||
<input type="radio" name="control" value="navigation" id="noneToggle" onclick="toggleControl(this);" CHECKED>
|
||||
<label for="noneToggle">navigate</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="radio" name="control" value="box" id="boxToggle" onclick="toggleControl(this);">
|
||||
<label for="boxToggle">SLD select with box</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="radio" name="control" value="polygon" id="polygonToggle" onclick="toggleControl(this);">
|
||||
<label for="polygonToggle">SLD select with polygon</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="radio" name="control" value="line" id="lineToggle" onclick="toggleControl(this);">
|
||||
<label for="lineToggle">SLD select with line</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="radio" name="control" value="point" id="pointToggle" onclick="toggleControl(this);">
|
||||
<label for="pointToggle">SLD select with point</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="radio" name="control" value="circle" id="circleToggle" onclick="toggleControl(this);">
|
||||
<label for="circleToggle">SLD select with circle</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="layers">
|
||||
<ul id="layerToggle"><b>Selection layer</b>
|
||||
<li>
|
||||
<input type="radio" name="layer" value="waterbodies" id="waterbodiesToggle" onclick="toggleSelectionLayer(this);" CHECKED>
|
||||
<label for="noneToggle">Water bodies</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="radio" name="layer" value="cities" id="citiesToggle" onclick="toggleSelectionLayer(this);">
|
||||
<label for="citiesToggle">Cities</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="radio" name="layer" value="roads" id="roadsToggle" onclick="toggleSelectionLayer(this);">
|
||||
<label for="roadsToggle">Roads</label>
|
||||
</li>
|
||||
<li>
|
||||
<input type="radio" name="layer" value="roads_cities" id="roadsCitiesToggle" onclick="toggleSelectionLayer(this);">
|
||||
<label for="roadsCitiesToggle">Roads and cities</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -180,6 +180,7 @@
|
||||
"OpenLayers/Control/WMSGetFeatureInfo.js",
|
||||
"OpenLayers/Control/Graticule.js",
|
||||
"OpenLayers/Control/TransformFeature.js",
|
||||
"OpenLayers/Control/SLDSelect.js",
|
||||
"OpenLayers/Geometry.js",
|
||||
"OpenLayers/Geometry/Rectangle.js",
|
||||
"OpenLayers/Geometry/Collection.js",
|
||||
|
||||
569
lib/OpenLayers/Control/SLDSelect.js
Normal file
569
lib/OpenLayers/Control/SLDSelect.js
Normal file
@@ -0,0 +1,569 @@
|
||||
/* Copyright (c) 2006-2010 MetaCarta, Inc., published under the Clear BSD
|
||||
* license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
|
||||
* full text of the license. */
|
||||
|
||||
/**
|
||||
* @requires OpenLayers/Control.js
|
||||
* @requires OpenLayers/Layer/WMS/Post.js
|
||||
* @requires OpenLayers/Handler/RegularPolygon.js
|
||||
* @requires OpenLayers/Handler/Polygon.js
|
||||
* @requires OpenLayers/Handler/Path.js
|
||||
* @requires OpenLayers/Handler/Click.js
|
||||
* @requires OpenLayers/Filter/Spatial.js
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class: OpenLayers.Control.SLDSelect
|
||||
* Perform selections on WMS layers using Styled Layer Descriptor (SLD)
|
||||
*
|
||||
* Inherits from:
|
||||
* - <OpenLayers.Control>
|
||||
*/
|
||||
OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, {
|
||||
|
||||
/**
|
||||
* Constant: EVENT_TYPES
|
||||
* {Array(String)} Supported application event types. Register a listener
|
||||
* for a particular event with the following syntax:
|
||||
* (code)
|
||||
* control.events.register(type, obj, listener);
|
||||
* (end)
|
||||
*
|
||||
* Listeners will be called with a reference to an event object. The
|
||||
* properties of this event depends on exactly what happened.
|
||||
*
|
||||
* Supported control event types (in addition to those from
|
||||
* <OpenLayers.Control>):
|
||||
* selected - Triggered when a selection occurs. Listeners receive an
|
||||
* event with *filters* and *layer* properties. Filters will be an
|
||||
* array of OpenLayers.Filter objects created in order to perform
|
||||
* the particular selection.
|
||||
*/
|
||||
EVENT_TYPES: ["selected"],
|
||||
|
||||
/**
|
||||
* APIProperty: clearOnDeactivate
|
||||
* {Boolean} Should the selection be cleared when the control is
|
||||
* deactivated. Default value is false.
|
||||
*/
|
||||
clearOnDeactivate: false,
|
||||
|
||||
/**
|
||||
* APIProperty: layers
|
||||
* {Array(<OpenLayers.Layer.WMS>)} The WMS layers this control will work
|
||||
* on.
|
||||
*/
|
||||
layers: null,
|
||||
|
||||
/**
|
||||
* Property: callbacks
|
||||
* {Object} The functions that are sent to the handler for callback
|
||||
*/
|
||||
callbacks: null,
|
||||
|
||||
/**
|
||||
* APIProperty: selectionSymbolizer
|
||||
* {Object} Determines the styling of the selected objects. Default is
|
||||
* a selection in red.
|
||||
*/
|
||||
selectionSymbolizer: {
|
||||
'Polygon': {fillColor: '#FF0000'},
|
||||
'Line': {strokeColor: '#FF0000', strokeWidth: 2},
|
||||
'Point': {graphicName: 'square', fillColor: '#FF0000', pointRadius: 5}
|
||||
},
|
||||
|
||||
/**
|
||||
* APIProperty: layerOptions
|
||||
* {Object} The options to apply to the selection layer, by default the
|
||||
* selection layer will be kept out of the layer switcher.
|
||||
*/
|
||||
layerOptions: null,
|
||||
|
||||
/**
|
||||
* APIProperty: handlerOptions
|
||||
* {Object} Used to set non-default properties on the control's handler
|
||||
*/
|
||||
handlerOptions: null,
|
||||
|
||||
/**
|
||||
* APIProperty: sketchStyle
|
||||
* {<OpenLayers.Style>|Object} Style or symbolizer to use for the sketch
|
||||
* handler. The recommended way of styling the sketch layer, however, is
|
||||
* to configure an <OpenLayers.StyleMap> in the layerOptions of the
|
||||
* <handlerOptions>:
|
||||
*
|
||||
* (code)
|
||||
* new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path, {
|
||||
* handlerOptions: {
|
||||
* layerOptions: {
|
||||
* styleMap: new OpenLayers.StyleMap({
|
||||
* "default": {strokeColor: "yellow"}
|
||||
* });
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
* (end)
|
||||
*/
|
||||
sketchStyle: null,
|
||||
|
||||
/**
|
||||
* APIProperty: wfsCache
|
||||
* {Object} Cache to use for storing parsed results from
|
||||
* <OpenLayers.Format.WFSDescribeFeatureType.read>. If not provided,
|
||||
* these will be cached on the prototype.
|
||||
*/
|
||||
wfsCache: {},
|
||||
|
||||
/**
|
||||
* APIProperty: layerCache
|
||||
* {Object} Cache to use for storing references to the selection layers.
|
||||
* Normally each source layer will have exactly 1 selection layer of
|
||||
* type OpenLayers.Layer.WMS.Post. If not provided, layers will
|
||||
* be cached on the prototype. Note that if <clearOnDeactivate> is
|
||||
* true, the layer will no longer be cached after deactivating the
|
||||
* control.
|
||||
*/
|
||||
layerCache: {},
|
||||
|
||||
/**
|
||||
* Constructor: OpenLayers.Control.SLDSelect
|
||||
* Create a new control for selecting features in WMS layers using
|
||||
* Styled Layer Descriptor (SLD).
|
||||
*
|
||||
* Parameters:
|
||||
* handler - {<OpenLayers.Class>} A sketch handler class. This determines
|
||||
* the type of selection, e.g. box (<OpenLayers.Handler.Box>), point
|
||||
* (<OpenLayers.Handler.Point>), path (<OpenLayers.Handler.Path>) or
|
||||
* polygon (<OpenLayers.Handler.Polygon>) selection. To use circle
|
||||
* type selection, use <OpenLayers.Handler.RegularPolygon> and pass
|
||||
* the number of desired sides (e.g. 40) as "sides" property to the
|
||||
* <handlerOptions>.
|
||||
* options - {Object} An object containing all configuration properties for
|
||||
* the control.
|
||||
*
|
||||
* Valid options:
|
||||
* layers - Array({<OpenLayers.Layer.WMS>}) The layers to perform the
|
||||
* selection on.
|
||||
*/
|
||||
initialize: function(handler, options) {
|
||||
// concatenate events specific to this control with those from the base
|
||||
this.EVENT_TYPES =
|
||||
OpenLayers.Control.SLDSelect.prototype.EVENT_TYPES.concat(
|
||||
OpenLayers.Control.prototype.EVENT_TYPES
|
||||
);
|
||||
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
||||
|
||||
this.callbacks = OpenLayers.Util.extend({done: this.select,
|
||||
click: this.select}, this.callbacks);
|
||||
this.handlerOptions = this.handlerOptions || {};
|
||||
this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, {
|
||||
displayInLayerSwitcher: false
|
||||
});
|
||||
if (this.sketchStyle) {
|
||||
this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(
|
||||
this.handlerOptions.layerOptions,
|
||||
{styleMap: new OpenLayers.StyleMap({"default": this.sketchStyle})}
|
||||
);
|
||||
}
|
||||
this.handler = new handler(this, this.callbacks, this.handlerOptions);
|
||||
},
|
||||
|
||||
/**
|
||||
* APIMethod: destroy
|
||||
* Take care of things that are not handled in superclass.
|
||||
*/
|
||||
destroy: function() {
|
||||
for (var key in this.layerCache) {
|
||||
delete this.layerCache[key];
|
||||
}
|
||||
for (var key in this.wfsCache) {
|
||||
delete this.wfsCache[key];
|
||||
}
|
||||
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: coupleLayerVisiblity
|
||||
* Couple the selection layer and the source layer with respect to
|
||||
* layer visibility. So if the source layer is turned off, the
|
||||
* selection layer is also turned off.
|
||||
*
|
||||
* Parameters:
|
||||
* evt - {Object}
|
||||
*/
|
||||
coupleLayerVisiblity: function(evt) {
|
||||
this.setVisibility(evt.object.getVisibility());
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: createSelectionLayer
|
||||
* Creates a "clone" from the source layer in which the selection can
|
||||
* be drawn. This ensures both the source layer and the selection are
|
||||
* visible and not only the selection.
|
||||
*
|
||||
* Parameters:
|
||||
* source - {<OpenLayers.Layer.WMS>} The source layer on which the selection
|
||||
* is performed.
|
||||
*
|
||||
* Returns:
|
||||
* {<OpenLayers.Layer.WMS.Post>} A WMS Post layer since SLD selections can
|
||||
* easily get quite long.
|
||||
*/
|
||||
createSelectionLayer: function(source) {
|
||||
// check if we already have a selection layer for the source layer
|
||||
var selectionLayer;
|
||||
if (!this.layerCache[source.id]) {
|
||||
selectionLayer = new OpenLayers.Layer.WMS.Post(source.name,
|
||||
source.url, source.params,
|
||||
OpenLayers.Util.applyDefaults(
|
||||
this.layerOptions,
|
||||
source.getOptions())
|
||||
);
|
||||
this.layerCache[source.id] = selectionLayer;
|
||||
// make sure the layers are coupled wrt visibility, but only
|
||||
// if they are not displayed in the layer switcher, because in
|
||||
// that case the user cannot control visibility.
|
||||
if (this.layerOptions.displayInLayerSwitcher === false) {
|
||||
source.events.on({
|
||||
"visibilitychanged": this.coupleLayerVisiblity,
|
||||
scope: selectionLayer});
|
||||
}
|
||||
this.map.addLayer(selectionLayer);
|
||||
} else {
|
||||
selectionLayer = this.layerCache[source.id];
|
||||
}
|
||||
return selectionLayer;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: createSLD
|
||||
* Create the SLD document for the layer using the supplied filters.
|
||||
*
|
||||
* Parameters:
|
||||
* layer - {<OpenLayers.Layer.WMS>}
|
||||
* filters - Array({<OpenLayers.Filter>}) The filters to be applied.
|
||||
* geometryAttributes - Array({Object}) The geometry attributes of the
|
||||
* layer.
|
||||
*
|
||||
* Returns:
|
||||
* {String} The SLD document generated as a string.
|
||||
*/
|
||||
createSLD: function(layer, filters, geometryAttributes) {
|
||||
var sld = {version: "1.0.0", namedLayers: {}};
|
||||
var layerNames = [layer.params.LAYERS].join(",").split(",");
|
||||
for (var i=0, len=layerNames.length; i<len; i++) {
|
||||
var name = layerNames[i];
|
||||
sld.namedLayers[name] = {name: name, userStyles: []};
|
||||
var symbolizer = this.selectionSymbolizer;
|
||||
var geometryAttribute = geometryAttributes[i];
|
||||
if (geometryAttribute.type.indexOf('Polygon') >= 0) {
|
||||
symbolizer = {Polygon: this.selectionSymbolizer['Polygon']};
|
||||
} else if (geometryAttribute.type.indexOf('LineString') >= 0) {
|
||||
symbolizer = {Line: this.selectionSymbolizer['Line']};
|
||||
} else if (geometryAttribute.type.indexOf('Point') >= 0) {
|
||||
symbolizer = {Point: this.selectionSymbolizer['Point']};
|
||||
}
|
||||
var filter = filters[i];
|
||||
sld.namedLayers[name].userStyles.push({name: 'default', rules: [
|
||||
new OpenLayers.Rule({symbolizer: symbolizer,
|
||||
filter: filters[i],
|
||||
maxScaleDenominator: layer.options.minScale})
|
||||
]});
|
||||
}
|
||||
return new OpenLayers.Format.SLD().write(sld);
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: parseDescribeLayer
|
||||
* Parse the SLD WMS DescribeLayer response and issue the corresponding
|
||||
* WFS DescribeFeatureType request
|
||||
*
|
||||
* request - {XMLHttpRequest} The request object.
|
||||
*/
|
||||
parseDescribeLayer: function(request) {
|
||||
var format = new OpenLayers.Format.WMSDescribeLayer();
|
||||
var doc = request.responseXML;
|
||||
if(!doc || !doc.documentElement) {
|
||||
doc = request.responseText;
|
||||
}
|
||||
var describeLayer = format.read(doc);
|
||||
var typeNames = [];
|
||||
var url = null;
|
||||
for (var i=0, len=describeLayer.length; i<len; i++) {
|
||||
// perform a WFS DescribeFeatureType request
|
||||
if (describeLayer[i].owsType == "WFS") {
|
||||
typeNames.push(describeLayer[i].typeName);
|
||||
url = describeLayer[i].owsURL
|
||||
}
|
||||
}
|
||||
var options = {
|
||||
url: url,
|
||||
params: {
|
||||
SERVICE: "WFS",
|
||||
TYPENAME: typeNames.toString(),
|
||||
REQUEST: "DescribeFeatureType",
|
||||
VERSION: "1.0.0"
|
||||
},
|
||||
callback: function(request) {
|
||||
var format = new OpenLayers.Format.WFSDescribeFeatureType();
|
||||
var doc = request.responseXML;
|
||||
if(!doc || !doc.documentElement) {
|
||||
doc = request.responseText;
|
||||
}
|
||||
var describeFeatureType = format.read(doc);
|
||||
this.control.wfsCache[this.layer.id] = describeFeatureType;
|
||||
this._queue && this.applySelection();
|
||||
},
|
||||
scope: this
|
||||
};
|
||||
OpenLayers.Request.GET(options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: getGeometryAttributes
|
||||
* Look up the geometry attributes from the WFS DescribeFeatureType response
|
||||
*
|
||||
* Parameters:
|
||||
* layer - {<OpenLayers.Layer.WMS>} The layer for which to look up the
|
||||
* geometry attributes.
|
||||
*
|
||||
* Returns:
|
||||
* Array({Object}) Array of geometry attributes
|
||||
*/
|
||||
getGeometryAttributes: function(layer) {
|
||||
var result = [];
|
||||
var cache = this.wfsCache[layer.id];
|
||||
for (var i=0, len=cache.featureTypes.length; i<len; i++) {
|
||||
var typeName = cache.featureTypes[i];
|
||||
var properties = typeName.properties;
|
||||
for (var j=0, lenj=properties.length; j < lenj; j++) {
|
||||
var property = properties[j];
|
||||
var type = property.type;
|
||||
if ((type.indexOf('LineString') >= 0) ||
|
||||
(type.indexOf('GeometryAssociationType') >=0) ||
|
||||
(type.indexOf('GeometryPropertyType') >= 0) ||
|
||||
(type.indexOf('Point') >= 0) ||
|
||||
(type.indexOf('Polygon') >= 0) ) {
|
||||
result.push(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* APIMethod: activate
|
||||
* Activate the control. Activating the control will perform a SLD WMS
|
||||
* DescribeLayer request followed by a WFS DescribeFeatureType request
|
||||
* so that the proper symbolizers can be chosen based on the geometry
|
||||
* type.
|
||||
*/
|
||||
activate: function() {
|
||||
var activated = OpenLayers.Control.prototype.activate.call(this);
|
||||
if(activated) {
|
||||
for (var i=0, len=this.layers.length; i<len; i++) {
|
||||
var layer = this.layers[i];
|
||||
if (layer && !this.wfsCache[layer.id]) {
|
||||
var options = {
|
||||
url: layer.url,
|
||||
params: {
|
||||
SERVICE: "WMS",
|
||||
VERSION: layer.params.VERSION,
|
||||
LAYERS: layer.params.LAYERS,
|
||||
REQUEST: "DescribeLayer"
|
||||
},
|
||||
callback: this.parseDescribeLayer,
|
||||
scope: {layer: layer, control: this}
|
||||
};
|
||||
OpenLayers.Request.GET(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
return activated;
|
||||
},
|
||||
|
||||
/**
|
||||
* APIMethod: deactivate
|
||||
* Deactivate the control. If clearOnDeactivate is true, remove the
|
||||
* selection layer(s).
|
||||
*/
|
||||
deactivate: function() {
|
||||
var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
|
||||
if(deactivated) {
|
||||
for (var i=0, len=this.layers.length; i<len; i++) {
|
||||
var layer = this.layers[i];
|
||||
if (layer && this.clearOnDeactivate === true) {
|
||||
var layerCache = this.layerCache;
|
||||
var selectionLayer = layerCache[layer.id];
|
||||
if (selectionLayer) {
|
||||
layer.events.un({
|
||||
"visibilitychanged": this.coupleLayerVisiblity,
|
||||
scope: selectionLayer});
|
||||
selectionLayer.destroy();
|
||||
delete layerCache[layer.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return deactivated;
|
||||
},
|
||||
|
||||
/**
|
||||
* APIMethod: setLayers
|
||||
* Set the layers on which the selection should be performed. Call the
|
||||
* setLayers method if the layer(s) to be used change and the same
|
||||
* control should be used on a new set of layers.
|
||||
* If the control is already active, it will be active after the new
|
||||
* set of layers is set.
|
||||
*
|
||||
* Parameters:
|
||||
* layers - {Array(<OpenLayers.Layer.WMS>)} The new set of layers on which
|
||||
* the selection should be performed.
|
||||
*/
|
||||
setLayers: function(layers) {
|
||||
if(this.active) {
|
||||
this.deactivate();
|
||||
this.layers = layers;
|
||||
this.activate();
|
||||
} else {
|
||||
this.layers = layers;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Function: createFilter
|
||||
* Create the filter to be used in the SLD.
|
||||
*
|
||||
* Parameters:
|
||||
* geometryAttribute - {Object} Used to get the name of the geometry
|
||||
* attribute which is needed for constructing the spatial filter.
|
||||
* geometry - {<OpenLayers.Geometry>} The geometry to use.
|
||||
*
|
||||
* Returns:
|
||||
* {<OpenLayers.Filter.Spatial>} The spatial filter created.
|
||||
*/
|
||||
createFilter: function(geometryAttribute, geometry) {
|
||||
var filter = null;
|
||||
if (this.handler instanceof OpenLayers.Handler.RegularPolygon) {
|
||||
// box
|
||||
if (this.handler.irregular === true) {
|
||||
filter = new OpenLayers.Filter.Spatial({
|
||||
type: OpenLayers.Filter.Spatial.BBOX,
|
||||
property: geometryAttribute.name,
|
||||
value: geometry.getBounds()}
|
||||
);
|
||||
} else {
|
||||
filter = new OpenLayers.Filter.Spatial({
|
||||
type: OpenLayers.Filter.Spatial.INTERSECTS,
|
||||
property: geometryAttribute.name,
|
||||
value: geometry}
|
||||
);
|
||||
}
|
||||
} else if (this.handler instanceof OpenLayers.Handler.Polygon) {
|
||||
filter = new OpenLayers.Filter.Spatial({
|
||||
type: OpenLayers.Filter.Spatial.INTERSECTS,
|
||||
property: geometryAttribute.name,
|
||||
value: geometry}
|
||||
);
|
||||
} else if (this.handler instanceof OpenLayers.Handler.Path) {
|
||||
// if source layer is point based, use DWITHIN instead
|
||||
if (geometryAttribute.type.indexOf('Point') >= 0) {
|
||||
filter = new OpenLayers.Filter.Spatial({
|
||||
type: OpenLayers.Filter.Spatial.DWITHIN,
|
||||
property: geometryAttribute.name,
|
||||
distance: this.map.getExtent().getWidth()*0.01 ,
|
||||
distanceUnits: this.map.getUnits(),
|
||||
value: geometry}
|
||||
);
|
||||
} else {
|
||||
filter = new OpenLayers.Filter.Spatial({
|
||||
type: OpenLayers.Filter.Spatial.INTERSECTS,
|
||||
property: geometryAttribute.name,
|
||||
value: geometry}
|
||||
);
|
||||
}
|
||||
} else if (this.handler instanceof OpenLayers.Handler.Click) {
|
||||
if (geometryAttribute.type.indexOf('Polygon') >= 0) {
|
||||
filter = new OpenLayers.Filter.Spatial({
|
||||
type: OpenLayers.Filter.Spatial.INTERSECTS,
|
||||
property: geometryAttribute.name,
|
||||
value: geometry}
|
||||
);
|
||||
} else {
|
||||
filter = new OpenLayers.Filter.Spatial({
|
||||
type: OpenLayers.Filter.Spatial.DWITHIN,
|
||||
property: geometryAttribute.name,
|
||||
distance: this.map.getExtent().getWidth()*0.01 ,
|
||||
distanceUnits: this.map.getUnits(),
|
||||
value: geometry}
|
||||
);
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: select
|
||||
* When the handler is done, use SLD_BODY on the selection layer to
|
||||
* display the selection in the map.
|
||||
*
|
||||
* Parameters:
|
||||
* geometry - {Object} or {<OpenLayers.Geometry>}
|
||||
*/
|
||||
select: function(geometry) {
|
||||
this._queue = function() {
|
||||
for (var i=0, len=this.layers.length; i<len; i++) {
|
||||
var layer = this.layers[i];
|
||||
var geometryAttributes = this.getGeometryAttributes(layer);
|
||||
var filters = [];
|
||||
for (var j=0, lenj=geometryAttributes.length; j<lenj; j++) {
|
||||
var geometryAttribute = geometryAttributes[j];
|
||||
if (geometryAttribute !== null) {
|
||||
// from the click handler we will not get an actual
|
||||
// geometry so transform
|
||||
if (!(geometry instanceof OpenLayers.Geometry)) {
|
||||
var point = this.map.getLonLatFromPixel(
|
||||
geometry.xy);
|
||||
geometry = new OpenLayers.Geometry.Point(
|
||||
point.lon, point.lat);
|
||||
}
|
||||
var filter = this.createFilter(geometryAttribute,
|
||||
geometry);
|
||||
if (filter !== null) {
|
||||
filters.push(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var selectionLayer = this.createSelectionLayer(layer);
|
||||
var sld = this.createSLD(layer, filters, geometryAttributes);
|
||||
|
||||
this.events.triggerEvent("selected", {
|
||||
layer: layer,
|
||||
filters: filters
|
||||
});
|
||||
|
||||
selectionLayer.mergeNewParams({SLD_BODY: sld});
|
||||
delete this._queue;
|
||||
}
|
||||
}
|
||||
this.applySelection();
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: applySelection
|
||||
* Checks if all required wfs data is cached, and applies the selection
|
||||
*/
|
||||
applySelection: function() {
|
||||
var canApply = true;
|
||||
for (var i=0, len=this.layers.length; i<len; i++) {
|
||||
if(!this.wfsCache[this.layers[i].id]) {
|
||||
canApply = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
canApply && this._queue.call(this);
|
||||
},
|
||||
|
||||
CLASS_NAME: "OpenLayers.Control.SLDSelect"
|
||||
});
|
||||
182
tests/Control/SLDSelect.html
Normal file
182
tests/Control/SLDSelect.html
Normal file
@@ -0,0 +1,182 @@
|
||||
<html>
|
||||
<head>
|
||||
<script src="../../lib/OpenLayers.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
function test_initialize(t) {
|
||||
t.plan(11);
|
||||
var control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Click);
|
||||
t.eq(control.handler instanceof OpenLayers.Handler.Click, true, "Click handler created");
|
||||
t.ok(control.handler.callbacks["click"] === control.select, "Click callback correctly set");
|
||||
control.destroy();
|
||||
control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.RegularPolygon, {handlerOptions: {irregular: true}});
|
||||
t.eq(control.handler instanceof OpenLayers.Handler.RegularPolygon, true, "RegularPolygon handler created");
|
||||
t.eq(control.handler.irregular, true, "RegularPolygon handler is irregular");
|
||||
t.eq(control.handler.persist, false, "RegularPolygon handler is not persistant");
|
||||
t.ok(control.handler.callbacks["done"] === control.select, "Done callback correctly set");
|
||||
control.destroy();
|
||||
control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Polygon);
|
||||
t.eq(control.handler instanceof OpenLayers.Handler.Polygon, true, "Polygon handler created");
|
||||
t.ok(control.handler.callbacks["done"] === control.select, "Done callback correctly set");
|
||||
control.destroy();
|
||||
control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path);
|
||||
t.eq(control.handler instanceof OpenLayers.Handler.Path, true, "Path handler created");
|
||||
t.ok(control.handler.callbacks["done"] === control.select, "Done callback correctly set");
|
||||
control.destroy();
|
||||
var layer = new OpenLayers.Layer.WMS('Foo', 'http://foo', {LAYERS: 'A'});
|
||||
control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.RegularPolygon, {layers: [layer]});
|
||||
t.eq(control.layers.length, 1, "Layers property correctly set");
|
||||
control.destroy();
|
||||
layer.destroy();
|
||||
}
|
||||
|
||||
function test_select(t) {
|
||||
t.plan(8);
|
||||
var parser = new OpenLayers.Format.WFSDescribeFeatureType();
|
||||
var map = new OpenLayers.Map('map');
|
||||
var layer = new OpenLayers.Layer.WMS('Foo', 'http://foo', {LAYERS: 'AAA64'});
|
||||
map.addLayer(layer);
|
||||
|
||||
var text =
|
||||
'<?xml version="1.0" encoding="ISO-8859-1" ?>' +
|
||||
'<schema' +
|
||||
' targetNamespace="http://mapserver.gis.umn.edu/mapserver" ' +
|
||||
' xmlns:rws="http://mapserver.gis.umn.edu/mapserver" ' +
|
||||
' xmlns:ogc="http://www.opengis.net/ogc"' +
|
||||
' xmlns:xsd="http://www.w3.org/2001/XMLSchema"' +
|
||||
' xmlns="http://www.w3.org/2001/XMLSchema"' +
|
||||
' xmlns:gml="http://www.opengis.net/gml"' +
|
||||
' elementFormDefault="qualified" version="0.1" >' +
|
||||
' <import namespace="http://www.opengis.net/gml"' +
|
||||
' schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd" />' +
|
||||
' <element name="AAA64" ' +
|
||||
' type="rws:AAA64Type" ' +
|
||||
' substitutionGroup="gml:_Feature" />' +
|
||||
' <complexType name="AAA64Type">' +
|
||||
' <complexContent>' +
|
||||
' <extension base="gml:AbstractFeatureType">' +
|
||||
' <sequence>' +
|
||||
' <element name="geometry" type="gml:MultiLineStringPropertyType" minOccurs="0" maxOccurs="1"/>' +
|
||||
' <element name="OBJECTID" type="string"/>' +
|
||||
' </sequence>' +
|
||||
' </extension>' +
|
||||
' </complexContent>' +
|
||||
' </complexType>' +
|
||||
'</schema>';
|
||||
|
||||
OpenLayers.Control.SLDSelect.prototype.wfsCache[layer.id] = parser.read(text);
|
||||
var control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.RegularPolygon,
|
||||
{layers: [layer], clearOnDeactivate: true, handlerOptions: {irregular: true} });
|
||||
|
||||
var testEvent = function(evt) {
|
||||
t.eq(evt.filters.length, 1, "Event has a filters array set");
|
||||
t.eq(evt.filters[0] instanceof OpenLayers.Filter.Spatial, true, "Spatial filter has been created");
|
||||
};
|
||||
|
||||
control.events.register("selected", this, testEvent);
|
||||
map.addControl(control);
|
||||
var geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
|
||||
new OpenLayers.Geometry.Point(0, 0), 5, 4);
|
||||
control.select(geometry);
|
||||
control.events.unregister("selected", this, testEvent);
|
||||
t.eq(map.layers.length, 2, "Selection layer has been created and added to the map");
|
||||
t.eq(map.layers[1] instanceof OpenLayers.Layer.WMS.Post, true, "A WMS Post layer has been created as the selection layer");
|
||||
var expected_sld = '<sld:StyledLayerDescriptor xmlns:sld="http://www.opengis.net/sld" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"><sld:NamedLayer><sld:Name>AAA64</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><sld:Name>default</sld:Name><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:LineSymbolizer><sld:Stroke><sld:CssParameter name="stroke">#FF0000</sld:CssParameter><sld:CssParameter name="stroke-width">2</sld:CssParameter></sld:Stroke></sld:LineSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor>';
|
||||
|
||||
t.xml_eq(map.layers[1].params.SLD_BODY, expected_sld, "SLD generated correctly");
|
||||
|
||||
var geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
|
||||
new OpenLayers.Geometry.Point(0, 0), 7, 4);
|
||||
control.select(geometry);
|
||||
t.eq(map.layers.length, 2, "Selection layer is reused when new selection is performed");
|
||||
|
||||
map.layers[0].setVisibility(false);
|
||||
t.eq(map.layers[1].getVisibility(), false, "Visibility of selection layer is synchronized with source layer");
|
||||
// activate would issue a SLD WMS DescribeLayer request and we are bypassing this here
|
||||
control.active = true;
|
||||
control.deactivate();
|
||||
t.eq(map.layers.length, 1, "Selection layer is removed on deactive if clearOnDeactivate is set to true");
|
||||
map.destroy();
|
||||
}
|
||||
|
||||
function test_multiselect(t) {
|
||||
t.plan(2);
|
||||
|
||||
var parser = new OpenLayers.Format.WFSDescribeFeatureType();
|
||||
var map = new OpenLayers.Map('map');
|
||||
var layer = new OpenLayers.Layer.WMS('Multi', 'http://foo', {LAYERS: 'KGNAT.VKUNSTWERK,KGNAT.LKUNSTWERK,KGNAT.PKUNSTWERK'});
|
||||
map.addLayer(layer);
|
||||
|
||||
var text =
|
||||
'<?xml version="1.0" encoding="ISO-8859-1" ?>' +
|
||||
'<schema' +
|
||||
' targetNamespace="http://mapserver.gis.umn.edu/mapserver" ' +
|
||||
' xmlns:rws="http://mapserver.gis.umn.edu/mapserver" ' +
|
||||
' xmlns:ogc="http://www.opengis.net/ogc"' +
|
||||
' xmlns:xsd="http://www.w3.org/2001/XMLSchema"' +
|
||||
' xmlns="http://www.w3.org/2001/XMLSchema"' +
|
||||
' xmlns:gml="http://www.opengis.net/gml"' +
|
||||
' elementFormDefault="qualified" version="0.1" >' +
|
||||
' <import namespace="http://www.opengis.net/gml"' +
|
||||
' schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd" />' +
|
||||
' <element name="KGNAT.VKUNSTWERK" ' +
|
||||
' type="rws:KGNAT.VKUNSTWERKType" ' +
|
||||
' substitutionGroup="gml:_Feature" />' +
|
||||
' <complexType name="KGNAT.VKUNSTWERKType">' +
|
||||
' <complexContent>' +
|
||||
' <extension base="gml:AbstractFeatureType">' +
|
||||
' <sequence>' +
|
||||
' <element name="geometry" type="gml:MultiPolygonPropertyType" minOccurs="0" maxOccurs="1"/>' +
|
||||
' </sequence>' +
|
||||
' </extension>' +
|
||||
' </complexContent>' +
|
||||
' </complexType>' +
|
||||
' <element name="KGNAT.LKUNSTWERK" ' +
|
||||
' type="rws:KGNAT.LKUNSTWERKType" ' +
|
||||
' substitutionGroup="gml:_Feature" />' +
|
||||
' <complexType name="KGNAT.LKUNSTWERKType">' +
|
||||
' <complexContent>' +
|
||||
' <extension base="gml:AbstractFeatureType">' +
|
||||
' <sequence>' +
|
||||
' <element name="geometry" type="gml:MultiLineStringPropertyType" minOccurs="0" maxOccurs="1"/>' +
|
||||
' </sequence>' +
|
||||
' </extension>' +
|
||||
' </complexContent>' +
|
||||
' </complexType>' +
|
||||
' <element name="KGNAT.PKUNSTWERK" ' +
|
||||
' type="rws:KGNAT.PKUNSTWERKType" ' +
|
||||
' substitutionGroup="gml:_Feature" />' +
|
||||
' <complexType name="KGNAT.PKUNSTWERKType">' +
|
||||
' <complexContent>' +
|
||||
' <extension base="gml:AbstractFeatureType">' +
|
||||
' <sequence>' +
|
||||
' <element name="geometry" type="gml:MultiPointPropertyType" minOccurs="0" maxOccurs="1"/>' +
|
||||
' </sequence>' +
|
||||
' </extension>' +
|
||||
' </complexContent>' +
|
||||
' </complexType>' +
|
||||
'</schema>';
|
||||
|
||||
OpenLayers.Control.SLDSelect.prototype.wfsCache[layer.id] = parser.read(text);
|
||||
var control = new OpenLayers.Control.SLDSelect(OpenLayers.Handler.RegularPolygon, {handlerOptions: {irregular: true}, layers: [layer]});
|
||||
var testEvent = function(evt) {
|
||||
t.eq(evt.filters.length, 3, "Event has a filters array set with length tree");
|
||||
};
|
||||
control.events.register("selected", this, testEvent);
|
||||
|
||||
map.addControl(control);
|
||||
var geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
|
||||
new OpenLayers.Geometry.Point(0, 0), 5, 4);
|
||||
control.select(geometry);
|
||||
var expected_sld = '<sld:StyledLayerDescriptor xmlns:sld="http://www.opengis.net/sld" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"><sld:NamedLayer><sld:Name>KGNAT.VKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><sld:Name>default</sld:Name><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:PolygonSymbolizer><sld:Fill><sld:CssParameter name="fill">#FF0000</sld:CssParameter></sld:Fill></sld:PolygonSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer><sld:NamedLayer><sld:Name>KGNAT.LKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><sld:Name>default</sld:Name><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:LineSymbolizer><sld:Stroke><sld:CssParameter name="stroke">#FF0000</sld:CssParameter><sld:CssParameter name="stroke-width">2</sld:CssParameter></sld:Stroke></sld:LineSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer><sld:NamedLayer><sld:Name>KGNAT.PKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><sld:Name>default</sld:Name><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:PointSymbolizer><sld:Graphic><sld:Mark><sld:WellKnownName>square</sld:WellKnownName><sld:Fill><sld:CssParameter name="fill">#FF0000</sld:CssParameter></sld:Fill><sld:Stroke/></sld:Mark><sld:Size>10</sld:Size></sld:Graphic></sld:PointSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor>';
|
||||
|
||||
t.xml_eq(map.layers[1].params.SLD_BODY, expected_sld, "SLD generated correctly");
|
||||
map.destroy();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="map" style="width: 100px; height: 100px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -38,6 +38,7 @@
|
||||
<li>Control/TransformFeature.html</li>
|
||||
<li>Control/WMSGetFeatureInfo.html</li>
|
||||
<li>Control/PanPanel.html</li>
|
||||
<li>Control/SLDSelect.html</li>
|
||||
<li>Events.html</li>
|
||||
<li>Extras.html</li>
|
||||
<li>Feature.html</li>
|
||||
|
||||
Reference in New Issue
Block a user