Batch merge for rc2 of 2.7. 'svn merge -r7967:HEAD from trunk (Closes #1733) (Closes #1489) (Closes #1639) (Closes #1718) (Closes #1723) (Closes #1732) (Closes #1616) (Closes #1722)

git-svn-id: http://svn.openlayers.org/branches/openlayers/2.7@8012 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
euzuro
2008-09-12 17:24:20 +00:00
parent a43e98762c
commit f7f338e265
51 changed files with 7219 additions and 90 deletions

View File

@@ -168,8 +168,6 @@ OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {
*/
createParams: function(center, zoom, layers) {
center = center || this.map.getCenter();
zoom = zoom || this.map.getZoom();
layers = layers || this.map.layers;
var params = OpenLayers.Util.getParameters(this.base);
@@ -177,8 +175,11 @@ OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {
// Break out of this function, and simply return the params from the
// base link.
if (center) {
params.zoom = this.map.getZoom();
//zoom
params.zoom = zoom || this.map.getZoom();
//lon,lat
var lat = center.lat;
var lon = center.lon;
@@ -192,10 +193,12 @@ OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {
}
params.lat = Math.round(lat*100000)/100000;
params.lon = Math.round(lon*100000)/100000;
//layers
layers = layers || this.map.layers;
params.layers = '';
for (var i=0, len=this.map.layers.length; i<len; i++) {
var layer = this.map.layers[i];
for (var i=0, len=layers.length; i<len; i++) {
var layer = layers[i];
if (layer.isBaseLayer) {
params.layers += (layer == this.map.baseLayer) ? "B" : "0";

View File

@@ -13,6 +13,12 @@
*/
OpenLayers.Format = OpenLayers.Class({
/**
* Property: options
* {Object} A reference to options passed to the constructor.
*/
options: null,
/**
* APIProperty: externalProjection
* {<OpenLayers.Projection>} When passed a externalProjection and
@@ -54,6 +60,14 @@ OpenLayers.Format = OpenLayers.Class({
*/
initialize: function(options) {
OpenLayers.Util.extend(this, options);
this.options = options;
},
/**
* APIMethod: destroy
* Clean up.
*/
destroy: function() {
},
/**

View File

@@ -0,0 +1,525 @@
/* Copyright (c) 2006-2008 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/Format/XML.js
*/
/**
* Eventually, this will require the OpenLayers.Format.GML. For now, since
* this parser can be included in a lib without the old GML parser, we
* declare the namespace if it doesn't exist.
*/
if(!OpenLayers.Format.GML) {
OpenLayers.Format.GML = {};
}
/**
* Class: OpenLayers.Format.GML.Base
* Superclass for GML parsers.
*
* Inherits from:
* - <OpenLayers.Format.XML>
*/
OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
/**
* Property: namespaces
* {Object} Mapping of namespace aliases to namespace URIs.
*/
namespaces: {
gml: "http://www.opengis.net/gml",
xlink: "http://www.w3.org/1999/xlink",
xsi: "http://www.w3.org/2001/XMLSchema-instance",
wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
},
/**
* Property: defaultPrefix
*/
defaultPrefix: "gml",
/**
* Property: schemaLocation
* {String} Schema location for a particular minor version.
*/
schemaLocation: null,
/**
* APIProperty: featureType
* {String} The local (without prefix) feature typeName.
*/
featureType: null,
/**
* APIProperty: featureNS
* {String} The feature namespace. Must be set in the options at
* construction.
*/
featureNS: null,
/**
* APIProperty: geometry
* {String} Name of geometry element. Defaults to "geometry".
*/
geometryName: "geometry",
/**
* APIProperty: extractAttributes
* {Boolean} Extract attributes from GML. Default is true.
*/
extractAttributes: true,
/**
* APIProperty: srsName
* {String} URI for spatial reference system. This is optional for
* single part geometries and mandatory for collections and multis.
* If set, the srsName attribute will be written for all geometries.
* Default is null.
*/
srsName: null,
/**
* APIProperty: xy
* {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
* Changing is not recommended, a new Format should be instantiated.
*/
xy: true,
/**
* Property: regExes
* Compiled regular expressions for manipulating strings.
*/
regExes: {
trimSpace: (/^\s*|\s*$/g),
removeSpace: (/\s*/g),
splitSpace: (/\s+/),
trimComma: (/\s*,\s*/g)
},
/**
* Constructor: OpenLayers.Format.GML.Base
* Instances of this class are not created directly. Use the
* <OpenLayers.Format.GML.v2> or <OpenLayers.Format.GML.v3> constructor
* instead.
*
* Parameters:
* options - {Object} An optional object whose properties will be set on
* this instance.
*
* Valid options properties:
* featureType - {String} Local (without prefix) feature typeName (required).
* featureNS - {String} Feature namespace (required).
* geometryName - {String} Geometry element name.
*/
initialize: function(options) {
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
this.setNamespace("feature", options.featureNS);
},
/**
* Method: read
*
* Parameters:
* data - {DOMElement} A gml:featureMember element, a gml:featureMembers
* element, or an element containing either of the above at any level.
*
* Returns:
* {Array(<OpenLayers.Feature.Vector>)} An array of features.
*/
read: function(data) {
if(typeof data == "string") {
data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
}
if(data && data.nodeType == 9) {
data = data.documentElement;
}
var features = [];
this.readNode(data, {features: features});
if(features.length == 0) {
// look for gml:featureMember elements
var elements = this.getElementsByTagNameNS(
data, this.namespaces.gml, "featureMember"
);
if(elements.length) {
for(var i=0, len=elements.length; i<len; ++i) {
this.readNode(elements[i], {features: features});
}
} else {
// look for gml:featureMembers elements (this is v3, but does no harm here)
var elements = this.getElementsByTagNameNS(
data, this.namespaces.gml, "featureMembers"
);
if(elements.length) {
// there can be only one
this.readNode(elements[0], {features: features});
}
}
}
return features;
},
/**
* Property: readers
* Contains public functions, grouped by namespace prefix, that will
* be applied when a namespaced node is found matching the function
* name. The function will be applied in the scope of this parser
* with two arguments: the node being read and a context object passed
* from the parent.
*/
readers: {
"gml": {
"featureMember": function(node, obj) {
this.readChildNodes(node, obj);
},
"featureMembers": function(node, obj) {
this.readChildNodes(node, obj);
},
"Point": function(node, container) {
var obj = {points: []};
this.readChildNodes(node, obj);
if(!container.components) {
container.components = [];
}
container.components.push(obj.points[0]);
},
"coordinates": function(node, obj) {
var str = this.concatChildValues(node).replace(
this.regExes.trimSpace, ""
);
str = str.replace(this.regExes.trimComma, ",");
var pointList = str.split(this.regExes.splitSpace);
var coords;
var numPoints = pointList.length;
var points = new Array(numPoints);
for(var i=0; i<numPoints; ++i) {
coords = pointList[i].split(",");
if (this.xy) {
points[i] = new OpenLayers.Geometry.Point(
coords[0], coords[1], coords[2]
);
} else {
points[i] = new OpenLayers.Geometry.Point(
coords[1], coords[0], coords[2]
);
}
}
obj.points = points;
},
"coord": function(node, obj) {
var coord = {};
this.readChildNodes(node, coord);
if(!obj.points) {
obj.points = [];
}
obj.points.push(new OpenLayers.Geometry.Point(
coord.x, coord.y, coord.z
));
},
"X": function(node, coord) {
coord.x = this.getChildValue(node);
},
"Y": function(node, coord) {
coord.y = this.getChildValue(node);
},
"Z": function(node, coord) {
coord.z = this.getChildValue(node);
},
"MultiPoint": function(node, container) {
var obj = {components: []};
this.readChildNodes(node, obj);
container.components = [
new OpenLayers.Geometry.MultiPoint(obj.components)
];
},
"pointMember": function(node, obj) {
this.readChildNodes(node, obj);
},
"LineString": function(node, container) {
var obj = {};
this.readChildNodes(node, obj);
if(!container.components) {
container.components = [];
}
container.components.push(
new OpenLayers.Geometry.LineString(obj.points)
);
},
"MultiLineString": function(node, container) {
var obj = {components: []};
this.readChildNodes(node, obj);
container.components = [
new OpenLayers.Geometry.MultiLineString(obj.components)
];
},
"lineStringMember": function(node, obj) {
this.readChildNodes(node, obj);
},
"Polygon": function(node, container) {
var obj = {outer: null, inner: []};
this.readChildNodes(node, obj);
obj.inner.unshift(obj.outer);
if(!container.components) {
container.components = [];
}
container.components.push(
new OpenLayers.Geometry.Polygon(obj.inner)
);
},
"LinearRing": function(node, obj) {
var container = {};
this.readChildNodes(node, container);
obj.components = [new OpenLayers.Geometry.LinearRing(
container.points
)];
},
"MultiPolygon": function(node, container) {
var obj = {components: []};
this.readChildNodes(node, obj);
container.components = [
new OpenLayers.Geometry.MultiPolygon(obj.components)
];
},
"polygonMember": function(node, obj) {
this.readChildNodes(node, obj);
},
"GeometryCollection": function(node, container) {
var obj = {components: []};
this.readChildNodes(node, obj);
container.components = [
new OpenLayers.Geometry.Collection(obj.components)
];
},
"geometryMember": function(node, obj) {
this.readChildNodes(node, obj);
}
},
"feature": {
"*": function(node, obj) {
// The node can either be named like the featureType, or it
// can be a child of the feature:featureType. Children can be
// geometry or attributes.
var name;
var local = node.localName || node.nodeName.split(":").pop();
if(local == this.featureType) {
name = "_typeName";
} else {
// Assume attribute elements have one child node and that the child
// is a text node. Otherwise assume it is a geometry node.
if(node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
if(this.extractAttributes) {
name = "_attribute";
}
} else {
name = "_geometry";
}
}
if(name) {
this.readers.feature[name].apply(this, [node, obj]);
}
},
"_typeName": function(node, obj) {
var container = {components: [], attributes: {}};
this.readChildNodes(node, container);
var feature = new OpenLayers.Feature.Vector(
container.components[0], container.attributes
);
var fid = node.getAttribute("fid") ||
this.getAttributeNS(node, this.namespaces["gml"], "id");
if(fid) {
feature.fid = fid;
}
if(this.internalProjection && this.externalProjection &&
feature.geometry) {
feature.geometry.transform(
this.externalProjection, this.internalProjection
);
}
obj.features.push(feature);
},
"_geometry": function(node, obj) {
this.readChildNodes(node, obj);
},
"_attribute": function(node, obj) {
var local = node.localName || node.nodeName.split(":").pop();
var value = this.getChildValue(node);
obj.attributes[local] = value;
}
},
"wfs": {
"FeatureCollection": function(node, obj) {
this.readChildNodes(node, obj);
}
}
},
/**
* Method: write
*
* Parameters:
* features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
* An array of features or a single feature.
*
* Returns:
* {String} Given an array of features, a doc with a gml:featureMembers
* element will be returned. Given a single feature, a doc with a
* gml:featureMember element will be returned.
*/
write: function(features) {
var name;
if(features instanceof Array) {
name = "featureMembers";
} else {
name = "featureMember";
}
var root = this.writeNode("gml:" + name, features);
this.setAttributeNS(
root, this.namespaces["xsi"],
"xsi:schemaLocation", this.schemaLocation
);
return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
},
/**
* Property: writers
* As a compliment to the readers property, this structure contains public
* writing functions grouped by namespace alias and named like the
* node names they produce.
*/
writers: {
"gml": {
"featureMember": function(feature) {
var node = this.createElementNSPlus("gml:featureMember");
this.writeNode("feature:_typeName", feature, node);
return node;
},
"MultiPoint": function(geometry) {
var node = this.createElementNSPlus("gml:MultiPoint");
for(var i=0; i<geometry.components.length; ++i) {
this.writeNode("pointMember", geometry.components[i], node);
}
return node;
},
"pointMember": function(geometry) {
var node = this.createElementNSPlus("gml:pointMember");
this.writeNode("Point", geometry, node);
return node;
},
"MultiLineString": function(geometry) {
var node = this.createElementNSPlus("gml:MultiLineString");
for(var i=0; i<geometry.components.length; ++i) {
this.writeNode("lineStringMember", geometry.components[i], node);
}
return node;
},
"lineStringMember": function(geometry) {
var node = this.createElementNSPlus("gml:lineStringMember");
this.writeNode("LineString", geometry, node);
return node;
},
"MultiPolygon": function(geometry) {
var node = this.createElementNSPlus("gml:MultiPolygon");
for(var i=0; i<geometry.components.length; ++i) {
this.writeNode(
"polygonMember", geometry.components[i], node
);
}
return node;
},
"polygonMember": function(geometry) {
var node = this.createElementNSPlus("gml:polygonMember");
this.writeNode("Polygon", geometry, node);
return node;
},
"GeometryCollection": function(geometry) {
var node = this.createElementNSPlus("gml:GeometryCollection");
for(var i=0, len=geometry.components.length; i<len; ++i) {
this.writeNode("geometryMember", geometry.components[i], node);
}
return node;
},
"geometryMember": function(geometry) {
var node = this.createElementNSPlus("gml:geometryMember");
var child = this.writeNode("feature:_geometry", geometry);
node.appendChild(child.firstChild);
return node;
}
},
"feature": {
"_typeName": function(feature) {
var node = this.createElementNSPlus("feature:" + this.featureType, {
attributes: {fid: feature.fid}
});
if(feature.geometry) {
this.writeNode("feature:_geometry", feature.geometry, node);
}
for(var name in feature.attributes) {
var value = feature.attributes[name];
if(value != null) {
this.writeNode(
"feature:_attribute",
{name: name, value: value}, node
)
}
}
return node;
},
"_geometry": function(geometry) {
if(this.externalProjection && this.internalProjection) {
geometry = geometry.clone().transform(
this.internalProjection, this.externalProjection
);
}
var node = this.createElementNSPlus(
"feature:" + this.geometryName
);
var type = this.geometryTypes[geometry.CLASS_NAME];
var child = this.writeNode("gml:" + type, geometry, node);
if(this.srsName) {
child.setAttribute("srsName", this.srsName);
}
return node;
},
"_attribute": function(obj) {
return this.createElementNSPlus("feature:" + obj.name, {
value: obj.value
});
}
},
"wfs": {
"FeatureCollection": function(features) {
/**
* This is only here because GML2 only describes abstract
* feature collections. Typically, you would not be using
* the GML format to write wfs elements. This just provides
* some way to write out lists of features. GML3 defines the
* featureMembers element, so that is used by default instead.
*/
var node = this.createElementNSPlus("wfs:FeatureCollection");
for(var i=0, len=features.length; i<len; ++i) {
this.writeNode("gml:featureMember", features[i], node);
}
return node;
}
}
},
/**
* Property: geometryTypes
* {Object} Maps OpenLayers geometry class names to GML element names.
*/
geometryTypes: {
"OpenLayers.Geometry.Point": "Point",
"OpenLayers.Geometry.MultiPoint": "MultiPoint",
"OpenLayers.Geometry.LineString": "LineString",
"OpenLayers.Geometry.MultiLineString": "MultiLineString",
"OpenLayers.Geometry.Polygon": "Polygon",
"OpenLayers.Geometry.MultiPolygon": "MultiPolygon",
"OpenLayers.Geometry.Collection": "GeometryCollection"
},
CLASS_NAME: "OpenLayers.Format.GML.Base"
});

View File

@@ -0,0 +1,192 @@
/* Copyright (c) 2006-2008 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/Format/GML/Base.js
*/
/**
* Class: OpenLayers.Format.GML.v2
* Parses GML version 2.
*
* Inherits from:
* - <OpenLayers.Format.GML.Base>
*/
OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
/**
* Property: schemaLocation
* {String} Schema location for a particular minor version.
*/
schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
/**
* Constructor: OpenLayers.Format.GML.v2
* Create a parser for GML v2.
*
* Parameters:
* options - {Object} An optional object whose properties will be set on
* this instance.
*
* Valid options properties:
* featureType - {String} Local (without prefix) feature typeName (required).
* featureNS - {String} Feature namespace (required).
* geometryName - {String} Geometry element name.
*/
initialize: function(options) {
OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
},
/**
* Property: readers
* Contains public functions, grouped by namespace prefix, that will
* be applied when a namespaced node is found matching the function
* name. The function will be applied in the scope of this parser
* with two arguments: the node being read and a context object passed
* from the parent.
*/
readers: {
"gml": OpenLayers.Util.applyDefaults({
"outerBoundaryIs": function(node, container) {
var obj = {};
this.readChildNodes(node, obj);
container.outer = obj.components[0];
},
"innerBoundaryIs": function(node, container) {
var obj = {};
this.readChildNodes(node, obj);
container.inner.push(obj.components[0]);
},
"Box": function(node, container) {
var obj = {};
this.readChildNodes(node, obj);
if(!container.components) {
container.components = [];
}
var min = obj.points[0];
var max = obj.points[1];
container.components.push(
new OpenLayers.Bounds(min.x, min.y, max.x, max.y)
);
}
}, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
"feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
"wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
},
/**
* Method: write
*
* Parameters:
* features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
* An array of features or a single feature.
*
* Returns:
* {String} Given an array of features, a doc with a gml:featureMembers
* element will be returned. Given a single feature, a doc with a
* gml:featureMember element will be returned.
*/
write: function(features) {
var name;
if(features instanceof Array) {
// GML2 only has abstract feature collections
// wfs provides a feature collection from a well-known schema
name = "wfs:FeatureCollection";
} else {
name = "gml:featureMember";
}
var root = this.writeNode(name, features);
this.setAttributeNS(
root, this.namespaces["xsi"],
"xsi:schemaLocation", this.schemaLocation
);
return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
},
/**
* Property: writers
* As a compliment to the readers property, this structure contains public
* writing functions grouped by namespace alias and named like the
* node names they produce.
*/
writers: {
"gml": OpenLayers.Util.applyDefaults({
"Point": function(geometry) {
var node = this.createElementNSPlus("gml:Point");
this.writeNode("coordinates", [geometry], node);
return node;
},
"coordinates": function(points) {
var numPoints = points.length;
var parts = new Array(numPoints);
var point;
for(var i=0; i<numPoints; ++i) {
point = points[i];
if(this.xy) {
parts[i] = point.x + "," + point.y;
} else {
parts[i] = point.y + "," + point.x;
}
if(point.z != undefined) { // allow null or undefined
parts[i] += "," + point.z;
}
}
return this.createElementNSPlus("gml:coordinates", {
attributes: {
decimal: ".", cs: ",", ts: " "
},
value: (numPoints == 1) ? parts[0] : parts.join(" ")
});
},
"LineString": function(geometry) {
var node = this.createElementNSPlus("gml:LineString");
this.writeNode("coordinates", geometry.components, node);
return node;
},
"Polygon": function(geometry) {
var node = this.createElementNSPlus("gml:Polygon");
this.writeNode("outerBoundaryIs", geometry.components[0], node);
for(var i=1; i<geometry.components.length; ++i) {
this.writeNode(
"innerBoundaryIs", geometry.components[i], node
);
}
return node;
},
"outerBoundaryIs": function(ring) {
var node = this.createElementNSPlus("gml:outerBoundaryIs");
this.writeNode("LinearRing", ring, node);
return node;
},
"innerBoundaryIs": function(ring) {
var node = this.createElementNSPlus("gml:innerBoundaryIs");
this.writeNode("LinearRing", ring, node);
return node;
},
"LinearRing": function(ring) {
var node = this.createElementNSPlus("gml:LinearRing");
this.writeNode("coordinates", ring.components, node);
return node;
},
"Box": function(bounds) {
var node = this.createElementNSPlus("gml:Box");
this.writeNode("coordinates", [
{x: bounds.left, y: bounds.bottom},
{x: bounds.right, y: bounds.top}
], node);
// srsName attribute is optional for gml:Box
if(this.srsName) {
node.setAttribute("srsName", this.srsName);
}
return node;
}
}, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
"feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
"wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
},
CLASS_NAME: "OpenLayers.Format.GML.v2"
});

View File

@@ -0,0 +1,287 @@
/* Copyright (c) 2006-2008 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/Format/GML/Base.js
*/
/**
* Class: OpenLayers.Format.GML.v3
* Parses GML version 3.
*
* Inherits from:
* - <OpenLayers.Format.GML.Base>
*/
OpenLayers.Format.GML.v3 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
/**
* Property: schemaLocation
* {String} Schema location for a particular minor version. The writers
* conform with the Simple Features Profile for GML.
*/
schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd",
/**
* Constructor: OpenLayers.Format.GML.v3
* Create a parser for GML v3.
*
* Parameters:
* options - {Object} An optional object whose properties will be set on
* this instance.
*
* Valid options properties:
* featureType - {String} Local (without prefix) feature typeName (required).
* featureNS - {String} Feature namespace (required).
* geometryName - {String} Geometry element name.
*/
initialize: function(options) {
OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
},
/**
* Property: readers
* Contains public functions, grouped by namespace prefix, that will
* be applied when a namespaced node is found matching the function
* name. The function will be applied in the scope of this parser
* with two arguments: the node being read and a context object passed
* from the parent.
*/
readers: {
"gml": OpenLayers.Util.applyDefaults({
"featureMembers": function(node, obj) {
this.readChildNodes(node, obj);
},
"pos": function(node, obj) {
var str = this.getChildValue(node).replace(
this.regExes.trimSpace, ""
);
var coords = str.split(this.regExes.splitSpace);
var point;
if(this.xy) {
point = new OpenLayers.Geometry.Point(
coords[0], coords[1], coords[2]
);
} else {
point = new OpenLayers.Geometry.Point(
coords[1], coords[0], coords[2]
);
}
obj.points = [point];
},
"posList": function(node, obj) {
var str = this.concatChildValues(node).replace(
this.regExes.trimSpace, ""
);
var coords = str.split(this.regExes.splitSpace);
var dim = parseInt(node.getAttribute("dimension")) || 2;
var j, x, y, z;
var numPoints = coords.length / dim;
var points = new Array(numPoints);
for(var i=0, len=coords.length; i<len; i += dim) {
x = coords[i];
y = coords[i+1];
z = (dim == 2) ? undefined : coords[i+2];
if (this.xy) {
points[i/dim] = new OpenLayers.Geometry.Point(x, y, z);
} else {
points[i/dim] = new OpenLayers.Geometry.Point(y, x, z);
}
}
obj.points = points;
},
"exterior": function(node, container) {
var obj = {};
this.readChildNodes(node, obj);
container.outer = obj.components[0];
},
"interior": function(node, container) {
var obj = {};
this.readChildNodes(node, obj);
container.inner.push(obj.components[0]);
},
"MultiSurface": function(node, container) {
var obj = {components: []};
this.readChildNodes(node, obj);
if(obj.components.length > 0) {
container.components = [
new OpenLayers.Geometry.MultiPolygon(obj.components)
];
}
},
"surfaceMember": function(node, obj) {
this.readChildNodes(node, obj);
},
"surfaceMembers": function(node, obj) {
this.readChildNodes(node, obj);
},
"pointMembers": function(node, obj) {
this.readChildNodes(node, obj);
},
"lineStringMembers": function(node, obj) {
this.readChildNodes(node, obj);
},
"polygonMembers": function(node, obj) {
this.readChildNodes(node, obj);
},
"geometryMembers": function(node, obj) {
this.readChildNodes(node, obj);
},
"Envelope": function(node, container) {
var obj = {points: new Array(2)};
this.readChildNodes(node, obj);
if(!container.components) {
container.components = [];
}
var min = obj.points[0];
var max = obj.points[1];
container.components.push(
new OpenLayers.Bounds(min.x, min.y, max.x, max.y)
);
},
"lowerCorner": function(node, container) {
var obj = {};
this.readChildNodes(node, obj)
container.points[0] = obj.points[0];
},
"upperCorner": function(node, container) {
var obj = {};
this.readChildNodes(node, obj)
container.points[1] = obj.points[0];
}
}, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
"feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
"wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
},
/**
* Method: write
*
* Parameters:
* features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
* An array of features or a single feature.
*
* Returns:
* {String} Given an array of features, a doc with a gml:featureMembers
* element will be returned. Given a single feature, a doc with a
* gml:featureMember element will be returned.
*/
write: function(features) {
var name;
if(features instanceof Array) {
name = "featureMembers";
} else {
name = "featureMember";
}
var root = this.writeNode("gml:" + name, features);
this.setAttributeNS(
root, this.namespaces["xsi"],
"xsi:schemaLocation", this.schemaLocation
);
return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
},
/**
* Property: writers
* As a compliment to the readers property, this structure contains public
* writing functions grouped by namespace alias and named like the
* node names they produce.
*/
writers: {
"gml": OpenLayers.Util.applyDefaults({
"featureMembers": function(features) {
var node = this.createElementNSPlus("gml:featureMembers");
for(var i=0, len=features.length; i<len; ++i) {
this.writeNode("feature:_typeName", features[i], node);
}
return node;
},
"Point": function(geometry) {
var node = this.createElementNSPlus("gml:Point");
this.writeNode("pos", geometry, node);
return node;
},
"pos": function(point) {
// only 2d for simple features profile
var pos = (this.xy) ?
(point.x + " " + point.y) : (point.y + " " + point.x);
return this.createElementNSPlus("gml:pos", {
value: pos
});
},
"LineString": function(geometry) {
var node = this.createElementNSPlus("gml:LineString");
this.writeNode("posList", geometry.components, node);
return node;
},
"posList": function(points) {
// only 2d for simple features profile
var len = points.length;
var parts = new Array(len);
var point;
for(var i=0; i<len; ++i) {
point = points[i];
if(this.xy) {
parts[i] = point.x + " " + point.y;
} else {
parts[i] = point.y + " " + point.x;
}
}
return this.createElementNSPlus("gml:posList", {
value: parts.join(" ")
});
},
"Polygon": function(geometry) {
var node = this.createElementNSPlus("gml:Polygon");
this.writeNode("exterior", geometry.components[0], node);
for(var i=1, len=geometry.components.length; i<len; ++i) {
this.writeNode(
"interior", geometry.components[i], node
);
}
return node;
},
"exterior": function(ring) {
var node = this.createElementNSPlus("gml:exterior");
this.writeNode("LinearRing", ring, node);
return node;
},
"interior": function(ring) {
var node = this.createElementNSPlus("gml:interior");
this.writeNode("LinearRing", ring, node);
return node;
},
"LinearRing": function(ring) {
var node = this.createElementNSPlus("gml:LinearRing");
this.writeNode("posList", ring.components, node);
return node;
},
"Envelope": function(bounds) {
var node = this.createElementNSPlus("gml:Envelope");
this.writeNode("lowerCorner", bounds, node);
this.writeNode("upperCorner", bounds, node);
// srsName attribute is required for gml:Envelope
if(this.srsName) {
node.setAttribute("srsName", this.srsName);
}
return node;
},
"lowerCorner": function(bounds) {
var node = this.createElementNSPlus("gml:lowerCorner");
this.writeNode("pos", {x: bounds.left, y: bounds.bottom}, node);
return node;
},
"upperCorner": function(bounds) {
var node = this.createElementNSPlus("gml:upperCorner");
this.writeNode("pos", {x: bounds.right, y: bounds.top}, node);
return node;
}
}, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
"feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
"wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
},
CLASS_NAME: "OpenLayers.Format.GML.v3"
});

View File

@@ -19,6 +19,46 @@
*/
OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
/**
* Property: namespaces
* {Object} Mapping of namespace aliases to namespace URIs. Properties
* of this object should not be set individually. Read-only. All
* XML subclasses should have their own namespaces object. Use
* <setNamespace> to add or set a namespace alias after construction.
*/
namespaces: null,
/**
* Property: namespaceAlias
* {Object} Mapping of namespace URI to namespace alias. This object
* is read-only. Use <setNamespace> to add or set a namespace alias.
*/
namespaceAlias: null,
/**
* Property: defaultPrefix
* {String} The default namespace alias for creating element nodes.
*/
defaultPrefix: null,
/**
* Property: readers
* Contains public functions, grouped by namespace prefix, that will
* be applied when a namespaced node is found matching the function
* name. The function will be applied in the scope of this parser
* with two arguments: the node being read and a context object passed
* from the parent.
*/
readers: {},
/**
* Property: writers
* As a compliment to the <readers> property, this structure contains public
* writing functions grouped by namespace alias and named like the
* node names they produce.
*/
writers: {},
/**
* Property: xmldom
* {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
@@ -44,6 +84,34 @@ OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
}
OpenLayers.Format.prototype.initialize.apply(this, [options]);
// clone the namespace object and set all namespace aliases
this.namespaces = OpenLayers.Util.extend({}, this.namespaces);
this.namespaceAlias = {};
for(var alias in this.namespaces) {
this.namespaceAlias[this.namespaces[alias]] = alias;
}
},
/**
* APIMethod: destroy
* Clean up.
*/
destroy: function() {
this.xmldom = null;
OpenLayers.Format.prototype.destroy.apply(this, arguments);
},
/**
* Method: setNamespace
* Set a namespace alias and URI for the format.
*
* Parameters:
* alias - {String} The namespace alias (prefix).
* uri - {String} The namespace URI.
*/
setNamespace: function(alias, uri) {
this.namespaces[alias] = uri;
this.namespaceAlias[uri] = alias;
},
/**
@@ -289,11 +357,12 @@ OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
* {String} The value of the first child of the given node.
*/
getChildValue: function(node, def) {
var value;
if (node && node.firstChild && node.firstChild.nodeValue) {
value = node.firstChild.nodeValue;
} else {
value = (def != undefined) ? def : "";
var value = def || "";
if(node) {
var child = node.firstChild;
if(child) {
value = child.nodeValue || value;
}
}
return value;
},
@@ -384,6 +453,172 @@ OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
}
},
/**
* Method: createElementNSPlus
* Shorthand for creating namespaced elements with optional attributes and
* child text nodes.
*
* Parameters:
* name - {String} The qualified node name.
* options - {Object} Optional object for node configuration.
*
* Valid options:
* uri - {String} Optional namespace uri for the element - supply a prefix
* instead if the namespace uri is a property of the format's namespace
* object.
* attributes - {Object} Optional attributes to be set using the
* <setAttributes> method.
* value - {String} Optional text to be appended as a text node.
*
* Returns:
* {Element} An element node.
*/
createElementNSPlus: function(name, options) {
options = options || {};
var loc = name.indexOf(":");
// order of prefix preference
// 1. in the uri option
// 2. in the prefix option
// 3. in the qualified name
// 4. from the defaultPrefix
var uri = options.uri || this.namespaces[options.prefix];
if(!uri) {
loc = name.indexOf(":");
uri = this.namespaces[name.substring(0, loc)];
}
if(!uri) {
uri = this.namespaces[this.defaultPrefix];
}
var node = this.createElementNS(uri, name);
if(options.attributes) {
this.setAttributes(node, options.attributes);
}
if(options.value) {
node.appendChild(this.createTextNode(options.value));
}
return node;
},
/**
* Method: setAttributes
* Set multiple attributes given key value pairs from an object.
*
* Parameters:
* node - {Element} An element node.
* obj - {Object || Array} An object whose properties represent attribute
* names and values represent attribute values. If an attribute name
* is a qualified name ("prefix:local"), the prefix will be looked up
* in the parsers {namespaces} object. If the prefix is found,
* setAttributeNS will be used instead of setAttribute.
*/
setAttributes: function(node, obj) {
var value, loc, alias, uri;
for(var name in obj) {
if(obj[name] != null && obj[name].toString) {
value = obj[name].toString();
// check for qualified attribute name ("prefix:local")
uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
this.setAttributeNS(node, uri, name, value);
}
}
},
/**
* Method: readNode
* Shorthand for applying one of the named readers given the node
* namespace and local name. Readers take two args (node, obj) and
* generally extend or modify the second.
*
* Parameters:
* node - {DOMElement} The node to be read (required).
* obj - {Object} The object to be modified (optional).
*
* Returns:
* {Object} The input object, modified (or a new one if none was provided).
*/
readNode: function(node, obj) {
if(!obj) {
obj = {};
}
var group = this.readers[this.namespaceAlias[node.namespaceURI]];
if(group) {
var local = node.localName || node.nodeName.split(":").pop();
var reader = group[local] || group["*"];
if(reader) {
reader.apply(this, [node, obj]);
}
}
return obj;
},
/**
* Method: readChildNodes
* Shorthand for applying the named readers to all children of a node.
* For each child of type 1 (element), <readSelf> is called.
*
* Parameters:
* node - {DOMElement} The node to be read (required).
* obj - {Object} The object to be modified (optional).
*
* Returns:
* {Object} The input object, modified.
*/
readChildNodes: function(node, obj) {
if(!obj) {
obj = {};
}
var children = node.childNodes;
var child;
for(var i=0, len=children.length; i<len; ++i) {
child = children[i];
if(child.nodeType == 1) {
this.readNode(child, obj);
}
}
return obj;
},
/**
* Method: writeNode
* Shorthand for applying one of the named writers and appending the
* results to a node. If a qualified name is not provided for the
* second argument (and a local name is used instead), the namespace
* of the parent node will be assumed.
*
* Parameters:
* name - {String} The name of a node to generate. If a qualified name
* (e.g. "pre:Name") is used, the namespace prefix is assumed to be
* in the <writers> group. If a local name is used (e.g. "Name") then
* the namespace of the parent is assumed. If a local name is used
* and no parent is supplied, then the default namespace is assumed.
* obj - {Object} Structure containing data for the writer.
* parent - {DOMElement} Result will be appended to this node. If no parent
* is supplied, the node will not be appended to anything.
*
* Returns:
* {DOMElement} The child node.
*/
writeNode: function(name, obj, parent) {
var prefix, local;
var split = name.indexOf(":");
if(split > 0) {
prefix = name.substring(0, split);
local = name.substring(split + 1);
} else {
if(parent) {
prefix = this.namespaceAlias[parent.namespaceURI];
} else {
prefix = this.defaultPrefix;
}
local = name;
}
var child = this.writers[prefix][local].apply(this, [obj]);
if(parent) {
parent.appendChild(child);
}
return child;
},
CLASS_NAME: "OpenLayers.Format.XML"
});

View File

@@ -5,6 +5,7 @@
/**
* @requires OpenLayers/Layer/Grid.js
* @requires OpenLayers/Tile/Image.js
*/
/**

View File

@@ -38,7 +38,12 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
* Supported map event types (in addition to those from <OpenLayers.Layer>):
* - *beforefeatureadded* Triggered before a feature is added. Listeners
* will receive an object with a *feature* property referencing the
* feature to be added.
* feature to be added. To stop the feature from being added, a
* listener should return false.
* - *beforefeaturesadded* Triggered before an array of features is added.
* Listeners will receive an object with a *features* property
* referencing the feature to be added. To stop the features from
* being added, a listener should return false.
* - *featureadded* Triggered after a feature is added. The event
* object passed to listeners will have a *feature* property with a
* reference to the added feature.
@@ -72,7 +77,8 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
* - *refresh* Triggered when something wants a strategy to ask the protocol
* for a new set of features.
*/
EVENT_TYPES: ["beforefeatureadded", "featureadded", "featuresadded",
EVENT_TYPES: ["beforefeatureadded", "beforefeaturesadded",
"featureadded", "featuresadded",
"beforefeatureremoved", "featureremoved", "featuresremoved",
"beforefeatureselected", "featureselected", "featureunselected",
"beforefeaturemodified", "featuremodified", "afterfeaturemodified",
@@ -407,19 +413,14 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
}
if(!zoomChanged && coordSysUnchanged) {
var unrenderedFeatures = {};
for(var i in this.unrenderedFeatures) {
var feature = this.unrenderedFeatures[i];
if(!this.drawFeature(feature)) {
unrenderedFeatures[i] = feature;
}
this.drawFeature(feature);
}
this.unrenderedFeatures = unrenderedFeatures;
}
}
if (!this.drawn || zoomChanged || !coordSysUnchanged) {
this.unrenderedFeatures = {};
this.drawn = true;
var feature;
for(var i=0, len=this.features.length; i<len; i++) {
@@ -429,9 +430,7 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
this.renderer.locked = false;
}
feature = this.features[i];
if (!this.drawFeature(feature)) {
this.unrenderedFeatures[feature.id] = feature;
};
this.drawFeature(feature);
}
}
},
@@ -450,6 +449,15 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
}
var notify = !options || !options.silent;
if(notify) {
var event = {features: features};
var ret = this.events.triggerEvent("beforefeaturesadded", event);
if(ret === false) {
return;
}
features = event.features;
}
for (var i=0, len=features.length; i<len; i++) {
if (i != (features.length - 1)) {
@@ -476,16 +484,15 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
}
if (notify) {
this.events.triggerEvent("beforefeatureadded", {
feature: feature
});
if(this.events.triggerEvent("beforefeatureadded",
{feature: feature}) === false) {
continue;
};
this.preFeatureInsert(feature);
}
if (this.drawn) {
if(!this.drawFeature(feature)) {
this.unrenderedFeatures[feature.id] = feature;
}
this.drawFeature(feature);
}
if (notify) {
@@ -603,10 +610,6 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
* style - {Object} Symbolizer hash or {String} renderIntent
*
* Returns:
* {Boolean} true if the renderer was able to draw the feature, false
* otherwise
*/
drawFeature: function(feature, style) {
if (typeof style != "object") {
@@ -618,7 +621,11 @@ OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
}
}
return this.renderer.drawFeature(feature, style);
if (!this.renderer.drawFeature(feature, style)) {
this.unrenderedFeatures[feature.id] = feature;
} else {
delete this.unrenderedFeatures[feature.id];
};
},
/**

View File

@@ -167,7 +167,14 @@ OpenLayers.Map = OpenLayers.Class({
/**
* Property: controls
* {Array(<OpenLayers.Control>)} List of controls associated with the map
* {Array(<OpenLayers.Control>)} List of controls associated with the map.
*
* If not provided in the map options at construction, the map will
* be given the following controls by default:
* - <OpenLayers.Control.Navigation>
* - <OpenLayers.Control.PanZoom>
* - <OpenLayers.Control.ArgParser>
* - <OpenLayers.Control.Attribution>
*/
controls: null,

View File

@@ -112,6 +112,15 @@ OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
* options - {Object} Optional object for configuring the request.
* This object is modified and should not be reused.
*
* Valid options:
* url - {String} Url for the request.
* params - {Object} Parameters to get serialized as a query string.
* headers - {Object} Headers to be set on the request.
* filter - {<OpenLayers.Filter.BBOX>} If a bbox filter is sent, it will be
* serialized according to the OpenSearch Geo extension
* (bbox=minx,miny,maxx,maxy). Note that a BBOX filter as the child
* of a logical filter will not be serialized.
*
* Returns:
* {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
* references the HTTP request, this object is also passed to the
@@ -121,6 +130,14 @@ OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
read: function(options) {
options = OpenLayers.Util.applyDefaults(options, this.options);
var resp = new OpenLayers.Protocol.Response({requestType: "read"});
if(options.filter && options.filter instanceof OpenLayers.Filter.Spatial) {
if(options.filter.type == OpenLayers.Filter.Spatial.BBOX) {
options.params = OpenLayers.Util.extend(options.params, {
bbox: options.filter.value.toArray()
});
}
}
resp.priv = OpenLayers.Request.GET({
url: options.url,

View File

@@ -0,0 +1,87 @@
/* Copyright (c) 2006-2008 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/Protocol.js
*/
/**
* Class: OpenLayers.Protocol.SQL
* Abstract SQL protocol class. Not to be instantiated directly. Use
* one of the SQL protocol subclasses instead.
*
* Inherits from:
* - <OpenLayers.Protocol>
*/
OpenLayers.Protocol.SQL = OpenLayers.Class(OpenLayers.Protocol, {
/**
* APIProperty: databaseName
* {String}
*/
databaseName: 'ol',
/**
* APIProperty: tableName
* Name of the database table into which Features should be saved.
*/
tableName: "ol_vector_features",
/**
* Property: postReadFiltering
* {Boolean} Whether the filter (if there's one) must be applied after
* the features have been read from the database; for example the
* BBOX strategy passes the read method a BBOX spatial filter, if
* postReadFiltering is true every feature read from the database
* will go through the BBOX spatial filter, which can be costly;
* defaults to true.
*/
postReadFiltering: true,
/**
* Constructor: OpenLayers.Protocol.SQL
*/
initialize: function(options) {
OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
},
/**
* APIMethod: destroy
* Clean up the protocol.
*/
destroy: function() {
OpenLayers.Protocol.prototype.destroy.apply(this);
},
/**
* APIMethod: supported
* This should be overridden by specific subclasses
*
* Returns:
* {Boolean} Whether or not the browser supports the SQL backend
*/
supported: function() {
return false;
},
/**
* Method: evaluateFilter
* If postReadFiltering is true evaluate the filter against the feature
* and return the result of the evaluation, otherwise return true.
*
* Parameters:
* {<OpenLayers.Feature.Vector>} The feature.
* {<OpenLayers.Filter>} The filter.
*
* Returns:
* {Boolean} true if postReadFiltering if false, the result of the
* filter evaluation otherwise.
*/
evaluateFilter: function(feature, filter) {
return filter && this.postReadFiltering ?
filter.evaluate(feature) : true;
},
CLASS_NAME: "OpenLayers.Protocol.SQL"
});

View File

@@ -0,0 +1,559 @@
/* Copyright (c) 2006-2008 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 Gears/gears_init.js
* @requires OpenLayers/Protocol/SQL.js
* @requires OpenLayers/Format/JSON.js
* @requires OpenLayers/Format/WKT.js
*/
/**
* Class: OpenLayers.Protocol.SQL.Gears
* This Protocol stores feature in the browser via the Gears Database module
* <http://code.google.com/apis/gears/api_database.html>.
*
* The main advantage is that all the read, create, update and delete operations
* can be done offline.
*
* Inherits from:
* - <OpenLayers.Protocol.SQL>
*/
OpenLayers.Protocol.SQL.Gears = OpenLayers.Class(OpenLayers.Protocol.SQL, {
/**
* Property: FID_PREFIX
* {String}
*/
FID_PREFIX: '__gears_fid__',
/**
* Property: NULL_GEOMETRY
* {String}
*/
NULL_GEOMETRY: '__gears_null_geometry__',
/**
* Property: NULL_FEATURE_STATE
* {String}
*/
NULL_FEATURE_STATE: '__gears_null_feature_state__',
/**
* Property: jsonParser
* {<OpenLayers.Format.JSON>}
*/
jsonParser: null,
/**
* Property: wktParser
* {<OpenLayers.Format.WKT>}
*/
wktParser: null,
/**
* Property: fidRegExp
* {RegExp} Regular expression to know whether a feature was
* created in offline mode.
*/
fidRegExp: null,
/**
* Property: saveFeatureState
* {Boolean} Whether to save the feature state (<OpenLayers.State>)
* into the database, defaults to true.
*/
saveFeatureState: true,
/**
* Property: typeOfFid
* {String} The type of the feature identifier, either "number" or
* "string", defaults to "string".
*/
typeOfFid: "string",
/**
* Property: db
* {GearsDatabase}
*/
db: null,
/**
* Constructor: OpenLayers.Protocol.SQL.Gears
*/
initialize: function(options) {
if (!this.supported()) {
return;
}
OpenLayers.Protocol.SQL.prototype.initialize.apply(this, [options]);
this.jsonParser = new OpenLayers.Format.JSON();
this.wktParser = new OpenLayers.Format.WKT();
this.fidRegExp = new RegExp('^' + this.FID_PREFIX);
this.initializeDatabase();
},
/**
* Method: initializeDatabase
*/
initializeDatabase: function() {
this.db = google.gears.factory.create('beta.database');
this.db.open(this.databaseName);
this.db.execute(
"CREATE TABLE IF NOT EXISTS " + this.tableName +
" (fid TEXT UNIQUE, geometry TEXT, properties TEXT," +
" state TEXT)");
},
/**
* APIMethod: destroy
* Clean up the protocol.
*/
destroy: function() {
this.db.close();
this.db = null;
this.jsonParser = null;
this.wktParser = null;
OpenLayers.Protocol.SQL.prototype.destroy.apply(this);
},
/**
* APIMethod: supported
* Determine whether a browser supports Gears
*
* Returns:
* {Boolean} The browser supports Gears
*/
supported: function() {
return !!(window.google && google.gears);
},
/**
* Method: read
* Read all features from the database and return a
* <OpenLayers.Protocol.Response> instance. If the options parameter
* contains a callback attribute, the function is called with the response
* as a parameter.
*
* Parameters:
* options - {Object} Optional object for configuring the request; it
* can have the {Boolean} property "noFeatureStateReset" which
* specifies if the state of features read from the Gears
* database must be reset to null, if "noFeatureStateReset"
* is undefined or false then each feature's state is reset
* to null, if "noFeatureStateReset" is true the feature state
* is preserved.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object.
*/
read: function(options) {
options = OpenLayers.Util.applyDefaults(options, this.options);
var feature, features = [];
var rs = this.db.execute("SELECT * FROM " + this.tableName);
while (rs.isValidRow()) {
feature = this.unfreezeFeature(rs);
if (this.evaluateFilter(feature, options.filter)) {
if (!options.noFeatureStateReset) {
feature.state = null;
}
features.push(feature);
}
rs.next();
}
rs.close();
var resp = new OpenLayers.Protocol.Response({
code: OpenLayers.Protocol.Response.SUCCESS,
requestType: "read",
features: features
});
if (options && options.callback) {
options.callback.call(options.scope, resp);
}
return resp;
},
/**
* Method: unfreezeFeature
*
* Parameters:
* row - {ResultSet}
*
* Returns:
* {<OpenLayers.Feature.Vector>}
*/
unfreezeFeature: function(row) {
var feature;
var wkt = row.fieldByName('geometry');
if (wkt == this.NULL_GEOMETRY) {
feature = new OpenLayers.Feature.Vector();
} else {
feature = this.wktParser.read(wkt);
}
feature.attributes = this.jsonParser.read(
row.fieldByName('properties'));
feature.fid = this.extractFidFromField(row.fieldByName('fid'));
var state = row.fieldByName('state');
if (state == this.NULL_FEATURE_STATE) {
state = null;
}
feature.state = state;
return feature;
},
/**
* Method: extractFidFromField
*
* Parameters:
* field - {String}
*
* Returns
* {String} or {Number} The fid.
*/
extractFidFromField: function(field) {
if (!field.match(this.fidRegExp) && this.typeOfFid == "number") {
field = parseFloat(field);
}
return field;
},
/**
* Method: create
* Create new features into the database.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})} or
* {<OpenLayers.Feature.Vector>} The features to create in
* the database.
* options - {Object} Optional object for configuring the request.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object.
*/
create: function(features, options) {
options = OpenLayers.Util.applyDefaults(options, this.options);
var resp = this.createOrUpdate(features);
resp.requestType = "create";
if (options && options.callback) {
options.callback.call(options.scope, resp);
}
return resp;
},
/**
* Method: update
* Construct a request updating modified feature.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})} or
* {<OpenLayers.Feature.Vector>} The features to update in
* the database.
* options - {Object} Optional object for configuring the request.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object.
*/
update: function(features, options) {
options = OpenLayers.Util.applyDefaults(options, this.options);
var resp = this.createOrUpdate(features);
resp.requestType = "update";
if (options && options.callback) {
options.callback.call(options.scope, resp);
}
return resp;
},
/**
* Method: createOrUpdate
* Construct a request for updating or creating features in the
* database.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})} or
* {<OpenLayers.Feature.Vector>} The feature to create or update
* in the database.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object.
*/
createOrUpdate: function(features) {
if (!(features instanceof Array)) {
features = [features];
}
var i, len = features.length, feature;
var insertedFeatures = new Array(len);
for (i = 0; i < len; i++) {
feature = features[i];
var params = this.freezeFeature(feature);
this.db.execute(
"REPLACE INTO " + this.tableName +
" (fid, geometry, properties, state)" +
" VALUES (?, ?, ?, ?)",
params);
var clone = feature.clone();
clone.fid = this.extractFidFromField(params[0]);
insertedFeatures[i] = clone;
}
return new OpenLayers.Protocol.Response({
code: OpenLayers.Protocol.Response.SUCCESS,
features: insertedFeatures,
reqFeatures: features
});
},
/**
* Method: freezeFeature
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
* state - {String} The feature state to store in the database.
*
* Returns:
* {Array}
*/
freezeFeature: function(feature) {
// 2 notes:
// - fid might not be a string
// - getFeatureStateForFreeze needs the feature fid to it's stored
// in the feature here
feature.fid = feature.fid != null ?
"" + feature.fid : OpenLayers.Util.createUniqueID(this.FID_PREFIX);
var geometry = feature.geometry != null ?
feature.geometry.toString() : this.NULL_GEOMETRY;
var properties = this.jsonParser.write(feature.attributes);
var state = this.getFeatureStateForFreeze(feature);
return [feature.fid, geometry, properties, state];
},
/**
* Method: getFeatureStateForFreeze
* Get the state of the feature to store into the database.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The feature.
*
* Returns
* {String} The state
*/
getFeatureStateForFreeze: function(feature) {
var state;
if (!this.saveFeatureState) {
state = this.NULL_FEATURE_STATE;
} else if (this.createdOffline(feature)) {
// if the feature was created in offline mode, its
// state must remain INSERT
state = OpenLayers.State.INSERT;
} else {
state = feature.state;
}
return state;
},
/**
* Method: delete
* Delete features from the database.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})} or
* {<OpenLayers.Feature.Vector>}
* options - {Object} Optional object for configuring the request.
* This object is modified and should not be reused.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object.
*/
"delete": function(features, options) {
if (!(features instanceof Array)) {
features = [features];
}
options = OpenLayers.Util.applyDefaults(options, this.options);
var i, len, feature;
for (i = 0, len = features.length; i < len; i++) {
feature = features[i];
// if saveFeatureState is set to true and if the feature wasn't created
// in offline mode we don't delete it in the database but just update
// it state column
if (this.saveFeatureState && !this.createdOffline(feature)) {
var toDelete = feature.clone();
toDelete.fid = feature.fid;
if (toDelete.geometry) {
toDelete.geometry.destroy();
toDelete.geometry = null;
}
toDelete.state = feature.state;
this.createOrUpdate(toDelete);
} else {
this.db.execute(
"DELETE FROM " + this.tableName +
" WHERE fid = ?", [feature.fid]);
}
}
var resp = new OpenLayers.Protocol.Response({
code: OpenLayers.Protocol.Response.SUCCESS,
requestType: "delete",
reqFeatures: features
});
if (options && options.callback) {
options.callback.call(options.scope, resp);
}
return resp;
},
/**
* Method: createdOffline
* Returns true if the feature had a feature id when it was created in
* the Gears database, false otherwise; this is determined by
* checking the form of the feature's fid value.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*
* Returns:
* {Boolean}
*/
createdOffline: function(feature) {
return (typeof feature.fid == "string" &&
!!(feature.fid.match(this.fidRegExp)));
},
/**
* Method: commit
* Go over the features and for each take action
* based on the feature state. Possible actions are create,
* update and delete.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})}
* options - {Object} Object whose possible keys are "create", "update",
* "delete", "callback" and "scope", the values referenced by the
* first three are objects as passed to the "create", "update", and
* "delete" methods, the value referenced by the "callback" key is
* a function which is called when the commit operation is complete
* using the scope referenced by the "scope" key.
*
* Returns:
* {Array({<OpenLayers.Protocol.Response>})} An array of
* <OpenLayers.Protocol.Response> objects, one per request made
* to the database.
*/
commit: function(features, options) {
var opt, resp = [], nRequests = 0, nResponses = 0;
function callback(resp) {
if (++nResponses < nRequests) {
resp.last = false;
}
this.callUserCallback(options, resp);
}
var feature, toCreate = [], toUpdate = [], toDelete = [];
for (var i = features.length - 1; i >= 0; i--) {
feature = features[i];
switch (feature.state) {
case OpenLayers.State.INSERT:
toCreate.push(feature);
break;
case OpenLayers.State.UPDATE:
toUpdate.push(feature);
break;
case OpenLayers.State.DELETE:
toDelete.push(feature);
break;
}
}
if (toCreate.length > 0) {
nRequests++;
opt = OpenLayers.Util.applyDefaults(
{"callback": callback, "scope": this},
options.create
);
resp.push(this.create(toCreate, opt));
}
if (toUpdate.length > 0) {
nRequests++;
opt = OpenLayers.Util.applyDefaults(
{"callback": callback, "scope": this},
options.update
);
resp.push(this.update(toUpdate, opt));
}
if (toDelete.length > 0) {
nRequests++;
opt = OpenLayers.Util.applyDefaults(
{"callback": callback, "scope": this},
options["delete"]
);
resp.push(this["delete"](toDelete, opt));
}
return resp;
},
/**
* Method: clear
* Removes all rows of the table.
*/
clear: function() {
this.db.execute("DELETE FROM " + this.tableName);
},
/**
* Method: callUserCallback
* This method is called from within commit each time a request is made
* to the database, it is responsible for calling the user-supplied
* callbacks.
*
* Parameters:
* options - {Object} The map of options passed to the commit call.
* resp - {<OpenLayers.Protocol.Response>}
*/
callUserCallback: function(options, resp) {
var opt = options[resp.requestType];
if (opt && opt.callback) {
opt.callback.call(opt.scope, resp);
}
if (resp.last && options.callback) {
options.callback.call(options.scope);
}
},
CLASS_NAME: "OpenLayers.Protocol.SQL.Gears"
});

View File

@@ -810,7 +810,9 @@ OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
if (this.indexer) {
this.indexer.remove(element);
}
if (element._style.backgroundGraphic) {
var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
var bElem = OpenLayers.Util.getElement(backgroundId);
if (bElem && bElem.parentNode) {

View File

@@ -153,7 +153,7 @@ OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
* y - {Float}
*
* Returns:
* {Boolean} true if the translation parameters ar in the valid coordinates
* {Boolean} true if the translation parameters are in the valid coordinates
* range, false otherwise.
*/
translate: function(x, y) {

View File

@@ -33,6 +33,12 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
*/
symbolCache: {},
/**
* Property: offset
* {Object} Hash with "x" and "y" properties
*/
offset: null,
/**
* Constructor: OpenLayers.Renderer.VML
* Create a new VML renderer.
@@ -50,8 +56,10 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
style.addRule('olv\\:*', "behavior: url(#default#VML); " +
"position: absolute; display: inline-block;");
}
OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
arguments);
this.offset = {x: 0, y: 0};
},
/**
@@ -90,8 +98,18 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
arguments);
var resolution = this.getResolution();
var org = extent.left/resolution + " " +
(extent.top/resolution - this.size.h);
var left = extent.left/resolution;
var top = extent.top/resolution - this.size.h;
if (resolutionChanged) {
this.offset = {x: left, y: top};
left = 0;
top = 0;
} else {
left = left - this.offset.x;
top = top - this.offset.y;
}
var org = left + " " + top;
this.root.setAttribute("coordorigin", org);
var size = this.size.w + " " + this.size.h;
@@ -191,8 +209,8 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
var yOffset = (style.graphicYOffset != undefined) ?
style.graphicYOffset : -(0.5 * height);
node.style.left = ((geometry.x/resolution)+xOffset).toFixed();
node.style.top = ((geometry.y/resolution)-(yOffset+height)).toFixed();
node.style.left = ((geometry.x/resolution - this.offset.x)+xOffset).toFixed();
node.style.top = ((geometry.y/resolution - this.offset.y)-(yOffset+height)).toFixed();
node.style.width = width + "px";
node.style.height = height + "px";
node.style.flip = "y";
@@ -448,10 +466,10 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
var resolution = this.getResolution();
var scaledBox =
new OpenLayers.Bounds((bbox.left/resolution).toFixed(),
(bbox.bottom/resolution).toFixed(),
(bbox.right/resolution).toFixed(),
(bbox.top/resolution).toFixed());
new OpenLayers.Bounds((bbox.left/resolution - this.offset.x).toFixed(),
(bbox.bottom/resolution - this.offset.y).toFixed(),
(bbox.right/resolution - this.offset.x).toFixed(),
(bbox.top/resolution - this.offset.y).toFixed());
// Set the internal coordinate system to draw the path
node.style.left = scaledBox.left + "px";
@@ -613,9 +631,9 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
drawCircle: function(node, geometry, radius) {
if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
var resolution = this.getResolution();
node.style.left = ((geometry.x /resolution).toFixed() - radius) + "px";
node.style.top = ((geometry.y /resolution).toFixed() - radius) + "px";
node.style.left = ((geometry.x /resolution - this.offset.x).toFixed() - radius) + "px";
node.style.top = ((geometry.y /resolution - this.offset.y).toFixed() - radius) + "px";
var diameter = radius * 2;
@@ -680,8 +698,8 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
var comp, x, y;
for (var i = 0; i < numComponents; i++) {
comp = geometry.components[i];
x = (comp.x/resolution);
y = (comp.y/resolution);
x = (comp.x/resolution - this.offset.x);
y = (comp.y/resolution - this.offset.y);
parts[i] = " " + x.toFixed() + "," + y.toFixed() + " l ";
}
var end = (closeLine) ? " x e" : " e";
@@ -713,8 +731,8 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
path.push("m");
for (i=0, ilen=linearRing.components.length; i<ilen; i++) {
comp = linearRing.components[i];
x = comp.x / resolution;
y = comp.y / resolution;
x = comp.x / resolution - this.offset.x;
y = comp.y / resolution - this.offset.y;
path.push(" " + x.toFixed() + "," + y.toFixed());
if (i==0) {
path.push(" l");
@@ -741,8 +759,8 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
drawRectangle: function(node, geometry) {
var resolution = this.getResolution();
node.style.left = geometry.x/resolution + "px";
node.style.top = geometry.y/resolution + "px";
node.style.left = (geometry.x/resolution - this.offset.x) + "px";
node.style.top = (geometry.y/resolution - this.offset.y) + "px";
node.style.width = geometry.width/resolution + "px";
node.style.height = geometry.height/resolution + "px";
@@ -769,8 +787,8 @@ OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
var comp, x, y;
for (var i=0, len=geometry.components.length; i<len; i++) {
comp = geometry.components[i];
x = comp.x / resolution;
y = comp.y / resolution;
x = comp.x / resolution - this.offset.x;
y = comp.y / resolution - this.offset.y;
if ((i%3)==0 && (i/3)==0) {
path.push("m");
} else if ((i%3)==1) {

View File

@@ -87,7 +87,11 @@ OpenLayers.Request = {
var request = new OpenLayers.Request.XMLHttpRequest();
var url = config.url;
if(config.params) {
url += "?" + OpenLayers.Util.getParameterString(config.params);
var paramString = OpenLayers.Util.getParameterString(config.params);
if(paramString.length > 0) {
var separator = (url.indexOf('?') > -1) ? '&' : '?';
url += separator + paramString;
}
}
if(config.proxy && (url.indexOf("http") == 0)) {
url = config.proxy + encodeURIComponent(url);
@@ -265,4 +269,4 @@ OpenLayers.Request = {
return OpenLayers.Request.issue(config);
}
};
};

View File

@@ -0,0 +1,210 @@
/* Copyright (c) 2006-2008 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/Strategy.js
* @requires OpenLayers/Filter/Spatial.js
*/
/**
* Class: OpenLayers.Strategy.BBOX
* A simple strategy that reads new features when the viewport invalidates
* some bounds.
*
* Inherits from:
* - <OpenLayers.Strategy>
*/
OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {
/**
* Property: bounds
* {<OpenLayers.Bounds>} The current data bounds.
*/
bounds: null,
/**
* Property: ratio
* {Float} The ratio of the data bounds to the viewport bounds (in each
* dimension).
*/
ratio: 2,
/**
* Property: response
* {<OpenLayers.Protocol.Response>} The protocol response object returned
* by the layer protocol.
*/
response: null,
/**
* Constructor: OpenLayers.Strategy.BBOX
* Create a new BBOX strategy.
*
* Parameters:
* options - {Object} Optional object whose properties will be set on the
* instance.
*/
initialize: function(options) {
OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
},
/**
* Method: activate
* Set up strategy with regard to reading new batches of remote data.
*
* Returns:
* {Boolean} The strategy was successfully activated.
*/
activate: function() {
var activated = OpenLayers.Strategy.prototype.activate.call(this);
if(activated) {
this.layer.events.on({
"moveend": this.update,
scope: this
});
this.layer.events.on({
"refresh": this.update,
scope: this
});
}
return activated;
},
/**
* Method: deactivate
* Tear down strategy with regard to reading new batches of remote data.
*
* Returns:
* {Boolean} The strategy was successfully deactivated.
*/
deactivate: function() {
var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
if(deactivated) {
this.layer.events.un({
"moveend": this.update,
scope: this
});
this.layer.events.un({
"refresh": this.update,
scope: this
});
}
return deactivated;
},
/**
* Method: update
* Callback function called on "moveend" or "refresh" layer events.
*
* Parameters:
* options - {Object} An object with a property named "force", this
* property references a boolean value indicating if new data
* must be incondtionally read.
*/
update: function(options) {
var mapBounds = this.layer.map.getExtent();
if ((options && options.force) || this.invalidBounds(mapBounds)) {
this.calculateBounds(mapBounds);
this.triggerRead();
}
},
/**
* Method: invalidBounds
*
* Parameters:
* mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
* retrieved from the map object if not provided
*
* Returns:
* {Boolean}
*/
invalidBounds: function(mapBounds) {
if(!mapBounds) {
mapBounds = this.layer.map.getExtent();
}
return !this.bounds || !this.bounds.containsBounds(mapBounds);
},
/**
* Method: calculateBounds
*
* Parameters:
* mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
* retrieved from the map object if not provided
*/
calculateBounds: function(mapBounds) {
if(!mapBounds) {
mapBounds = this.layer.map.getExtent();
}
var center = mapBounds.getCenterLonLat();
var dataWidth = mapBounds.getWidth() * this.ratio;
var dataHeight = mapBounds.getHeight() * this.ratio;
this.bounds = new OpenLayers.Bounds(
center.lon - (dataWidth / 2),
center.lat - (dataHeight / 2),
center.lon + (dataWidth / 2),
center.lat + (dataHeight / 2)
);
},
/**
* Method: triggerRead
*
* Returns:
* {<OpenLayers.Protocol.Response>} The protocol response object
* returned by the layer protocol.
*/
triggerRead: function() {
var filter = this.createFilter();
if (this.response && this.response.priv &&
typeof this.response.priv.abort == "function") {
this.response.priv.abort();
}
this.response = this.layer.protocol.read({
filter: filter,
callback: this.merge,
scope: this
});
},
/**
* Method: createFilter
*
* Returns
* {<OpenLayers.Filter>} The filter object.
*/
createFilter: function() {
var filter = new OpenLayers.Filter.Spatial({
type: OpenLayers.Filter.Spatial.BBOX,
value: this.bounds,
projection: this.layer.projection
});
if (this.layer.filter) {
filter = new OpenLayers.Filter.Logical({
type: OpenLayers.Filter.Logical.AND,
filters: [this.layer.filter, filter]
});
}
return filter;
},
/**
* Method: merge
* Given a list of features, determine which ones to add to the layer.
*
* Parameters:
* resp - {<OpenLayers.Protocol.Response>} The response object passed
* by the protocol.
*/
merge: function(resp) {
this.layer.destroyFeatures();
var features = resp.features;
if(features && features.length > 0) {
this.layer.addFeatures(features);
}
},
CLASS_NAME: "OpenLayers.Strategy.BBOX"
});

View File

@@ -0,0 +1,261 @@
/* Copyright (c) 2006-2008 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/Strategy.js
*/
/**
* Class: OpenLayers.Strategy.Cluster
* Strategy for vector feature clustering.
*
* Inherits from:
* - <OpenLayers.Strategy>
*/
OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, {
/**
* Property: layer
* {<OpenLayers.Layer.Vector>} The layer that this strategy is assigned to.
*/
layer: null,
/**
* APIProperty: distance
* {Integer} Pixel distance between features that should be considered a
* single cluster. Default is 20 pixels.
*/
distance: 20,
/**
* Property: features
* {Array(<OpenLayers.Feature.Vector>)} Cached features.
*/
features: null,
/**
* Property: clusters
* {Array(<OpenLayers.Feature.Vector>)} Calculated clusters.
*/
clusters: null,
/**
* Property: clustering
* {Boolean} The strategy is currently clustering features.
*/
clustering: false,
/**
* Property: resolution
* {Float} The resolution (map units per pixel) of the current cluster set.
*/
resolution: null,
/**
* Constructor: OpenLayers.Strategy.Cluster
* Create a new clustering strategy.
*
* Parameters:
* options - {Object} Optional object whose properties will be set on the
* instance.
*/
initialize: function(options) {
OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
},
/**
* APIMethod: activate
* Activate the strategy. Register any listeners, do appropriate setup.
*
* Returns:
* {Boolean} The strategy was successfully activated.
*/
activate: function() {
var activated = OpenLayers.Strategy.prototype.activate.call(this);
if(activated) {
this.layer.events.on({
"beforefeaturesadded": this.cacheFeatures,
scope: this
});
this.layer.map.events.on({"zoomend": this.cluster, scope: this});
}
return activated;
},
/**
* APIMethod: deactivate
* Deactivate the strategy. Unregister any listeners, do appropriate
* tear-down.
*
* Returns:
* {Boolean} The strategy was successfully deactivated.
*/
deactivate: function() {
var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
if(deactivated) {
this.clearCache();
this.layer.events.un({
"beforefeaturesadded": this.cacheFeatures,
scope: this
});
this.layer.map.events.un({"zoomend": this.cluster, scope: this});
}
return deactivated;
},
/**
* Method: cacheFeatures
* Cache features before they are added to the layer.
*
* Parameters:
* event - {Object} The event that this was listening for. This will come
* with a batch of features to be clustered.
*
* Returns:
* {Boolean} False to stop layer from being added to the layer.
*/
cacheFeatures: function(event) {
var propagate = true;
if(!this.clustering) {
this.clearCache();
this.features = event.features;
this.cluster();
propagate = false;
}
return propagate;
},
/**
* Method: clearCache
* Clear out the cached features. This destroys features, assuming
* nothing else has a reference.
*/
clearCache: function() {
if(this.features) {
for(var i=0; i<this.features.length; ++i) {
this.features[i].destroy();
}
}
this.features = null;
},
/**
* Method: cluster
* Cluster features based on some threshold distance.
*/
cluster: function() {
if(this.features) {
var resolution = this.layer.getResolution();
if(resolution != this.resolution || !this.clustersExist()) {
this.resolution = resolution;
var clusters = [];
var feature, clustered, cluster;
for(var i=0; i<this.features.length; ++i) {
feature = this.features[i];
clustered = false;
for(var j=0; j<clusters.length; ++j) {
cluster = clusters[j];
if(this.shouldCluster(cluster, feature)) {
this.addToCluster(cluster, feature);
clustered = true;
break;
}
}
if(!clustered) {
clusters.push(this.createCluster(this.features[i]));
}
}
this.layer.destroyFeatures();
if(clusters.length > 0) {
this.clustering = true;
// A legitimate feature addition could occur during this
// addFeatures call. For clustering to behave well, features
// should be removed from a layer before requesting a new batch.
this.layer.addFeatures(clusters);
this.clustering = false;
}
this.clusters = clusters;
}
}
},
/**
* Method: clustersExist
* Determine whether calculated clusters are already on the layer.
*
* Returns:
* {Boolean} The calculated clusters are already on the layer.
*/
clustersExist: function() {
var exist = false;
if(this.clusters && this.clusters.length > 0 &&
this.clusters.length == this.layer.features.length) {
exist = true;
for(var i=0; i<this.clusters.length; ++i) {
if(this.clusters[i] != this.layer.features[i]) {
exist = false;
break;
}
}
}
return exist;
},
/**
* Method: shouldCluster
* Determine whether to include a feature in a given cluster.
*
* Parameters:
* cluster - {<OpenLayers.Feature.Vector>} A cluster.
* feature - {<OpenLayers.Feature.Vector>} A feature.
*
* Returns:
* {Boolean} The feature should be included in the cluster.
*/
shouldCluster: function(cluster, feature) {
var cc = cluster.geometry.getBounds().getCenterLonLat();
var fc = feature.geometry.getBounds().getCenterLonLat();
var distance = (
Math.sqrt(
Math.pow((cc.lon - fc.lon), 2) + Math.pow((cc.lat - fc.lat), 2)
) / this.resolution
);
return (distance <= this.distance);
},
/**
* Method: addToCluster
* Add a feature to a cluster.
*
* Parameters:
* cluster - {<OpenLayers.Feature.Vector>} A cluster.
* feature - {<OpenLayers.Feature.Vector>} A feature.
*/
addToCluster: function(cluster, feature) {
cluster.cluster.push(feature);
cluster.attributes.count += 1;
},
/**
* Method: createCluster
* Given a feature, create a cluster.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*
* Returns:
* {<OpenLayers.Feature.Vector>} A cluster.
*/
createCluster: function(feature) {
var center = feature.geometry.getBounds().getCenterLonLat();
var cluster = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Point(center.lon, center.lat),
{count: 1}
);
cluster.cluster = [feature];
return cluster;
},
CLASS_NAME: "OpenLayers.Strategy.Cluster"
});

View File

@@ -0,0 +1,241 @@
/* Copyright (c) 2006-2008 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/Strategy.js
*/
/**
* Class: OpenLayers.Strategy.Paging
* Strategy for vector feature paging
*
* Inherits from:
* - <OpenLayers.Strategy>
*/
OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, {
/**
* Property: layer
* {<OpenLayers.Layer.Vector>} The layer that this strategy is assigned to.
*/
layer: null,
/**
* Property: features
* {Array(<OpenLayers.Feature.Vector>)} Cached features.
*/
features: null,
/**
* Property: length
* {Integer} Number of features per page. Default is 10.
*/
length: 10,
/**
* Property: num
* {Integer} The currently displayed page number.
*/
num: null,
/**
* Property: paging
* {Boolean} The strategy is currently changing pages.
*/
paging: false,
/**
* Constructor: OpenLayers.Strategy.Paging
* Create a new paging strategy.
*
* Parameters:
* options - {Object} Optional object whose properties will be set on the
* instance.
*/
initialize: function(options) {
OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
},
/**
* APIMethod: activate
* Activate the strategy. Register any listeners, do appropriate setup.
*
* Returns:
* {Boolean} The strategy was successfully activated.
*/
activate: function() {
var activated = OpenLayers.Strategy.prototype.activate.call(this);
if(activated) {
this.layer.events.on({
"beforefeaturesadded": this.cacheFeatures,
scope: this
});
}
return activated;
},
/**
* APIMethod: deactivate
* Deactivate the strategy. Unregister any listeners, do appropriate
* tear-down.
*
* Returns:
* {Boolean} The strategy was successfully deactivated.
*/
deactivate: function() {
var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
if(deactivated) {
this.clearCache();
this.layer.events.un({
"beforefeaturesadded": this.cacheFeatures,
scope: this
});
}
return deactivated;
},
/**
* Method: cacheFeatures
* Cache features before they are added to the layer.
*
* Parameters:
* event - {Object} The event that this was listening for. This will come
* with a batch of features to be paged.
*/
cacheFeatures: function(event) {
if(!this.paging) {
this.clearCache();
this.features = event.features;
this.pageNext(event);
}
},
/**
* Method: clearCache
* Clear out the cached features. This destroys features, assuming
* nothing else has a reference.
*/
clearCache: function() {
if(this.features) {
for(var i=0; i<this.features.length; ++i) {
this.features[i].destroy();
}
}
this.features = null;
this.num = null;
},
/**
* APIMethod: pageCount
* Get the total count of pages given the current cache of features.
*
* Returns:
* {Integer} The page count.
*/
pageCount: function() {
var numFeatures = this.features ? this.features.length : 0;
return Math.ceil(numFeatures / this.length);
},
/**
* APIMethod: pageNum
* Get the zero based page number.
*
* Returns:
* {Integer} The current page number being displayed.
*/
pageNum: function() {
return this.num;
},
/**
* APIMethod: pageLength
* Gets or sets page length.
*
* Parameters:
* newLength: {Integer} Optional length to be set.
*
* Returns:
* {Integer} The length of a page (number of features per page).
*/
pageLength: function(newLength) {
if(newLength && newLength > 0) {
this.length = newLength;
}
return this.length;
},
/**
* APIMethod: pageNext
* Display the next page of features.
*
* Returns:
* {Boolean} A new page was displayed.
*/
pageNext: function(event) {
var changed = false;
if(this.features) {
if(this.num === null) {
this.num = -1;
}
var start = (this.num + 1) * this.length;
changed = this.page(start, event);
}
return changed;
},
/**
* APIMethod: pagePrevious
* Display the previous page of features.
*
* Returns:
* {Boolean} A new page was displayed.
*/
pagePrevious: function() {
var changed = false;
if(this.features) {
if(this.num === null) {
this.num = this.pageCount();
}
var start = (this.num - 1) * this.length;
changed = this.page(start);
}
return changed;
},
/**
* Method: page
* Display the page starting at the given index from the cache.
*
* Returns:
* {Boolean} A new page was displayed.
*/
page: function(start, event) {
var changed = false;
if(this.features) {
if(start >= 0 && start < this.features.length) {
var num = Math.floor(start / this.length);
if(num != this.num) {
this.paging = true;
var features = this.features.slice(start, start + this.length);
this.layer.removeFeatures(this.layer.features);
this.num = num;
// modify the event if any
if(event && event.features) {
// this.was called by an event listener
event.features = features;
} else {
// this was called directly on the strategy
this.layer.addFeatures(features);
}
this.paging = false;
changed = true;
}
}
}
return changed;
},
CLASS_NAME: "OpenLayers.Strategy.Paging"
});

View File

@@ -796,13 +796,18 @@ OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
/**
* Function: distVincenty
* Given two objects representing points with geographic coordinates, this
* calculates the distance between those points on the surface of an
* ellipsoid.
*
* Parameters:
* p1 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
* p2 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
*
* Returns:
* {Float}
* {Float} The distance (in km) between the two input points as measured on an
* ellipsoid. Note that the input point objects must be in geographic
* coordinates (decimal degrees) and the return distance is in kilometers.
*/
OpenLayers.Util.distVincenty=function(p1, p2) {
var a = 6378137, b = 6356752.3142, f = 1/298.257223563;