diff options
Diffstat (limited to 'misc/openlayers/lib/OpenLayers/Strategy')
-rw-r--r-- | misc/openlayers/lib/OpenLayers/Strategy/BBOX.js | 290 | ||||
-rw-r--r-- | misc/openlayers/lib/OpenLayers/Strategy/Cluster.js | 283 | ||||
-rw-r--r-- | misc/openlayers/lib/OpenLayers/Strategy/Filter.js | 159 | ||||
-rw-r--r-- | misc/openlayers/lib/OpenLayers/Strategy/Fixed.js | 135 | ||||
-rw-r--r-- | misc/openlayers/lib/OpenLayers/Strategy/Paging.js | 233 | ||||
-rw-r--r-- | misc/openlayers/lib/OpenLayers/Strategy/Refresh.js | 141 | ||||
-rw-r--r-- | misc/openlayers/lib/OpenLayers/Strategy/Save.js | 231 |
7 files changed, 1472 insertions, 0 deletions
diff --git a/misc/openlayers/lib/OpenLayers/Strategy/BBOX.js b/misc/openlayers/lib/OpenLayers/Strategy/BBOX.js new file mode 100644 index 0000000..e066764 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Strategy/BBOX.js @@ -0,0 +1,290 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository 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 (in the same projection + * as the layer - not always the same projection as the map). + */ + bounds: null, + + /** + * Property: resolution + * {Float} The current data resolution. + */ + resolution: null, + + /** + * APIProperty: ratio + * {Float} The ratio of the data bounds to the viewport bounds (in each + * dimension). Default is 2. + */ + ratio: 2, + + /** + * Property: resFactor + * {Float} Optional factor used to determine when previously requested + * features are invalid. If set, the resFactor will be compared to the + * resolution of the previous request to the current map resolution. + * If resFactor > (old / new) and 1/resFactor < (old / new). If you + * set a resFactor of 1, data will be requested every time the + * resolution changes. If you set a resFactor of 3, data will be + * requested if the old resolution is 3 times the new, or if the new is + * 3 times the old. If the old bounds do not contain the new bounds + * new data will always be requested (with or without considering + * resFactor). + */ + resFactor: null, + + /** + * 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. + */ + + /** + * 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, + "refresh": this.update, + "visibilitychanged": this.update, + scope: this + }); + this.update(); + } + 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, + "refresh": this.update, + "visibilitychanged": this.update, + scope: this + }); + } + return deactivated; + }, + + /** + * Method: update + * Callback function called on "moveend" or "refresh" layer events. + * + * Parameters: + * options - {Object} Optional object whose properties will determine + * the behaviour of this Strategy + * + * Valid options include: + * force - {Boolean} if true, new data must be unconditionally read. + * noAbort - {Boolean} if true, do not abort previous requests. + */ + update: function(options) { + var mapBounds = this.getMapBounds(); + if (mapBounds !== null && ((options && options.force) || + (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) { + this.calculateBounds(mapBounds); + this.resolution = this.layer.map.getResolution(); + this.triggerRead(options); + } + }, + + /** + * Method: getMapBounds + * Get the map bounds expressed in the same projection as this layer. + * + * Returns: + * {<OpenLayers.Bounds>} Map bounds in the projection of the layer. + */ + getMapBounds: function() { + if (this.layer.map === null) { + return null; + } + var bounds = this.layer.map.getExtent(); + if(bounds && !this.layer.projection.equals( + this.layer.map.getProjectionObject())) { + bounds = bounds.clone().transform( + this.layer.map.getProjectionObject(), this.layer.projection + ); + } + return bounds; + }, + + /** + * Method: invalidBounds + * Determine whether the previously requested set of features is invalid. + * This occurs when the new map bounds do not contain the previously + * requested bounds. In addition, if <resFactor> is set, it will be + * considered. + * + * 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.getMapBounds(); + } + var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds); + if(!invalid && this.resFactor) { + var ratio = this.resolution / this.layer.map.getResolution(); + invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor)); + } + return invalid; + }, + + /** + * 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.getMapBounds(); + } + 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 + * + * Parameters: + * options - {Object} Additional options for the protocol's read method + * (optional) + * + * Returns: + * {<OpenLayers.Protocol.Response>} The protocol response object + * returned by the layer protocol. + */ + triggerRead: function(options) { + if (this.response && !(options && options.noAbort === true)) { + this.layer.protocol.abort(this.response); + this.layer.events.triggerEvent("loadend"); + } + var evt = {filter: this.createFilter()}; + this.layer.events.triggerEvent("loadstart", evt); + this.response = this.layer.protocol.read( + OpenLayers.Util.applyDefaults({ + filter: evt.filter, + callback: this.merge, + scope: this + }, options)); + }, + + /** + * Method: createFilter + * Creates a spatial BBOX filter. If the layer that this strategy belongs + * to has a filter property, this filter will be combined with the BBOX + * filter. + * + * 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. + * If the layer projection differs from the map projection, features + * will be transformed from the layer projection to the map projection. + * + * Parameters: + * resp - {<OpenLayers.Protocol.Response>} The response object passed + * by the protocol. + */ + merge: function(resp) { + this.layer.destroyFeatures(); + if (resp.success()) { + var features = resp.features; + if(features && features.length > 0) { + var remote = this.layer.projection; + var local = this.layer.map.getProjectionObject(); + if(!local.equals(remote)) { + var geom; + for(var i=0, len=features.length; i<len; ++i) { + geom = features[i].geometry; + if(geom) { + geom.transform(remote, local); + } + } + } + this.layer.addFeatures(features); + } + } else { + this.bounds = null; + } + this.response = null; + this.layer.events.triggerEvent("loadend", {response: resp}); + }, + + CLASS_NAME: "OpenLayers.Strategy.BBOX" +}); diff --git a/misc/openlayers/lib/OpenLayers/Strategy/Cluster.js b/misc/openlayers/lib/OpenLayers/Strategy/Cluster.js new file mode 100644 index 0000000..d478598 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Strategy/Cluster.js @@ -0,0 +1,283 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository 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, { + + /** + * APIProperty: distance + * {Integer} Pixel distance between features that should be considered a + * single cluster. Default is 20 pixels. + */ + distance: 20, + + /** + * APIProperty: threshold + * {Integer} Optional threshold below which original features will be + * added to the layer instead of clusters. For example, a threshold + * of 3 would mean that any time there are 2 or fewer features in + * a cluster, those features will be added directly to the layer instead + * of a cluster representing those features. Default is null (which is + * equivalent to 1 - meaning that clusters may contain just one feature). + */ + threshold: null, + + /** + * 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. + */ + + /** + * 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, + "featuresremoved": this.clearCache, + "moveend": 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, + "featuresremoved": this.clearCache, + "moveend": 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 features 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. + */ + clearCache: function() { + if(!this.clustering) { + this.features = null; + } + }, + + /** + * Method: cluster + * Cluster features based on some threshold distance. + * + * Parameters: + * event - {Object} The event received when cluster is called as a + * result of a moveend event. + */ + cluster: function(event) { + if((!event || event.zoomChanged) && this.features) { + var resolution = this.layer.map.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]; + if(feature.geometry) { + clustered = false; + for(var j=clusters.length-1; j>=0; --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.clustering = true; + this.layer.removeAllFeatures(); + this.clustering = false; + if(clusters.length > 0) { + if(this.threshold > 1) { + var clone = clusters.slice(); + clusters = []; + var candidate; + for(var i=0, len=clone.length; i<len; ++i) { + candidate = clone[i]; + if(candidate.attributes.count < this.threshold) { + Array.prototype.push.apply(clusters, candidate.cluster); + } else { + clusters.push(candidate); + } + } + } + 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" +}); diff --git a/misc/openlayers/lib/OpenLayers/Strategy/Filter.js b/misc/openlayers/lib/OpenLayers/Strategy/Filter.js new file mode 100644 index 0000000..721fe52 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Strategy/Filter.js @@ -0,0 +1,159 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Strategy.js + * @requires OpenLayers/Filter.js + */ + +/** + * Class: OpenLayers.Strategy.Filter + * Strategy for limiting features that get added to a layer by + * evaluating a filter. The strategy maintains a cache of + * all features until removeFeatures is called on the layer. + * + * Inherits from: + * - <OpenLayers.Strategy> + */ +OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, { + + /** + * APIProperty: filter + * {<OpenLayers.Filter>} Filter for limiting features sent to the layer. + * Use the <setFilter> method to update this filter after construction. + */ + filter: null, + + /** + * Property: cache + * {Array(<OpenLayers.Feature.Vector>)} List of currently cached + * features. + */ + cache: null, + + /** + * Property: caching + * {Boolean} The filter is currently caching features. + */ + caching: false, + + /** + * Constructor: OpenLayers.Strategy.Filter + * Create a new filter strategy. + * + * Parameters: + * options - {Object} Optional object whose properties will be set on the + * instance. + */ + + /** + * APIMethod: activate + * Activate the strategy. Register any listeners, do appropriate setup. + * By default, this strategy automatically activates itself when a layer + * is added to a map. + * + * Returns: + * {Boolean} True if the strategy was successfully activated or false if + * the strategy was already active. + */ + activate: function() { + var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); + if (activated) { + this.cache = []; + this.layer.events.on({ + "beforefeaturesadded": this.handleAdd, + "beforefeaturesremoved": this.handleRemove, + scope: this + }); + } + return activated; + }, + + /** + * APIMethod: deactivate + * Deactivate the strategy. Clear the feature cache. + * + * Returns: + * {Boolean} True if the strategy was successfully deactivated or false if + * the strategy was already inactive. + */ + deactivate: function() { + this.cache = null; + if (this.layer && this.layer.events) { + this.layer.events.un({ + "beforefeaturesadded": this.handleAdd, + "beforefeaturesremoved": this.handleRemove, + scope: this + }); + } + return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments); + }, + + /** + * Method: handleAdd + */ + handleAdd: function(event) { + if (!this.caching && this.filter) { + var features = event.features; + event.features = []; + var feature; + for (var i=0, ii=features.length; i<ii; ++i) { + feature = features[i]; + if (this.filter.evaluate(feature)) { + event.features.push(feature); + } else { + this.cache.push(feature); + } + } + } + }, + + /** + * Method: handleRemove + */ + handleRemove: function(event) { + if (!this.caching) { + this.cache = []; + } + }, + + /** + * APIMethod: setFilter + * Update the filter for this strategy. This will re-evaluate + * any features on the layer and in the cache. Only features + * for which filter.evalute(feature) returns true will be + * added to the layer. Others will be cached by the strategy. + * + * Parameters: + * filter - {<OpenLayers.Filter>} A filter for evaluating features. + */ + setFilter: function(filter) { + this.filter = filter; + var previousCache = this.cache; + this.cache = []; + // look through layer for features to remove from layer + this.handleAdd({features: this.layer.features}); + // cache now contains features to remove from layer + if (this.cache.length > 0) { + this.caching = true; + this.layer.removeFeatures(this.cache.slice()); + this.caching = false; + } + // now look through previous cache for features to add to layer + if (previousCache.length > 0) { + var event = {features: previousCache}; + this.handleAdd(event); + if (event.features.length > 0) { + // event has features to add to layer + this.caching = true; + this.layer.addFeatures(event.features); + this.caching = false; + } + } + }, + + CLASS_NAME: "OpenLayers.Strategy.Filter" + +}); diff --git a/misc/openlayers/lib/OpenLayers/Strategy/Fixed.js b/misc/openlayers/lib/OpenLayers/Strategy/Fixed.js new file mode 100644 index 0000000..b06f2cd --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Strategy/Fixed.js @@ -0,0 +1,135 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Strategy.js + */ + +/** + * Class: OpenLayers.Strategy.Fixed + * A simple strategy that requests features once and never requests new data. + * + * Inherits from: + * - <OpenLayers.Strategy> + */ +OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, { + + /** + * APIProperty: preload + * {Boolean} Load data before layer made visible. Enabling this may result + * in considerable overhead if your application loads many data layers + * that are not visible by default. Default is false. + */ + preload: false, + + /** + * Constructor: OpenLayers.Strategy.Fixed + * Create a new Fixed strategy. + * + * Parameters: + * options - {Object} Optional object whose properties will be set on the + * instance. + */ + + /** + * Method: activate + * Activate the strategy: load data or add listener to load when visible + * + * Returns: + * {Boolean} True if the strategy was successfully activated or false if + * the strategy was already active. + */ + activate: function() { + var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); + if(activated) { + this.layer.events.on({ + "refresh": this.load, + scope: this + }); + if(this.layer.visibility == true || this.preload) { + this.load(); + } else { + this.layer.events.on({ + "visibilitychanged": this.load, + scope: this + }); + } + } + return activated; + }, + + /** + * Method: deactivate + * Deactivate the strategy. Undo what is done in <activate>. + * + * Returns: + * {Boolean} The strategy was successfully deactivated. + */ + deactivate: function() { + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); + if(deactivated) { + this.layer.events.un({ + "refresh": this.load, + "visibilitychanged": this.load, + scope: this + }); + } + return deactivated; + }, + + /** + * Method: load + * Tells protocol to load data and unhooks the visibilitychanged event + * + * Parameters: + * options - {Object} options to pass to protocol read. + */ + load: function(options) { + var layer = this.layer; + layer.events.triggerEvent("loadstart", {filter: layer.filter}); + layer.protocol.read(OpenLayers.Util.applyDefaults({ + callback: this.merge, + filter: layer.filter, + scope: this + }, options)); + layer.events.un({ + "visibilitychanged": this.load, + scope: this + }); + }, + + /** + * Method: merge + * Add all features to the layer. + * If the layer projection differs from the map projection, features + * will be transformed from the layer projection to the map projection. + * + * Parameters: + * resp - {<OpenLayers.Protocol.Response>} The response object passed + * by the protocol. + */ + merge: function(resp) { + var layer = this.layer; + layer.destroyFeatures(); + var features = resp.features; + if (features && features.length > 0) { + var remote = layer.projection; + var local = layer.map.getProjectionObject(); + if(!local.equals(remote)) { + var geom; + for(var i=0, len=features.length; i<len; ++i) { + geom = features[i].geometry; + if(geom) { + geom.transform(remote, local); + } + } + } + layer.addFeatures(features); + } + layer.events.triggerEvent("loadend", {response: resp}); + }, + + CLASS_NAME: "OpenLayers.Strategy.Fixed" +}); diff --git a/misc/openlayers/lib/OpenLayers/Strategy/Paging.js b/misc/openlayers/lib/OpenLayers/Strategy/Paging.js new file mode 100644 index 0000000..22154fa --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Strategy/Paging.js @@ -0,0 +1,233 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository 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: 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. + */ + + /** + * 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" +}); diff --git a/misc/openlayers/lib/OpenLayers/Strategy/Refresh.js b/misc/openlayers/lib/OpenLayers/Strategy/Refresh.js new file mode 100644 index 0000000..cca187c --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Strategy/Refresh.js @@ -0,0 +1,141 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Strategy.js + */ + +/** + * Class: OpenLayers.Strategy.Refresh + * A strategy that refreshes the layer. By default the strategy waits for a + * call to <refresh> before refreshing. By configuring the strategy with + * the <interval> option, refreshing can take place automatically. + * + * Inherits from: + * - <OpenLayers.Strategy> + */ +OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, { + + /** + * Property: force + * {Boolean} Force a refresh on the layer. Default is false. + */ + force: false, + + /** + * Property: interval + * {Number} Auto-refresh. Default is 0. If > 0, layer will be refreshed + * every N milliseconds. + */ + interval: 0, + + /** + * Property: timer + * {Number} The id of the timer. + */ + timer: null, + + /** + * Constructor: OpenLayers.Strategy.Refresh + * Create a new Refresh strategy. + * + * Parameters: + * options - {Object} Optional object whose properties will be set on the + * instance. + */ + + /** + * APIMethod: activate + * Activate the strategy. Register any listeners, do appropriate setup. + * + * Returns: + * {Boolean} True if the strategy was successfully activated. + */ + activate: function() { + var activated = OpenLayers.Strategy.prototype.activate.call(this); + if(activated) { + if(this.layer.visibility === true) { + this.start(); + } + this.layer.events.on({ + "visibilitychanged": this.reset, + scope: this + }); + } + return activated; + }, + + /** + * APIMethod: deactivate + * Deactivate the strategy. Unregister any listeners, do appropriate + * tear-down. + * + * Returns: + * {Boolean} True if the strategy was successfully deactivated. + */ + deactivate: function() { + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); + if(deactivated) { + this.stop(); + this.layer.events.un({ + "visibilitychanged": this.reset, + scope: this + }); + } + return deactivated; + }, + + /** + * Method: reset + * Start or cancel the refresh interval depending on the visibility of + * the layer. + */ + reset: function() { + if(this.layer.visibility === true) { + this.start(); + } else { + this.stop(); + } + }, + + /** + * Method: start + * Start the refresh interval. + */ + start: function() { + if(this.interval && typeof this.interval === "number" && + this.interval > 0) { + + this.timer = window.setInterval( + OpenLayers.Function.bind(this.refresh, this), + this.interval); + } + }, + + /** + * APIMethod: refresh + * Tell the strategy to refresh which will refresh the layer. + */ + refresh: function() { + if (this.layer && this.layer.refresh && + typeof this.layer.refresh == "function") { + + this.layer.refresh({force: this.force}); + } + }, + + /** + * Method: stop + * Cancels the refresh interval. + */ + stop: function() { + if(this.timer !== null) { + window.clearInterval(this.timer); + this.timer = null; + } + }, + + CLASS_NAME: "OpenLayers.Strategy.Refresh" +}); diff --git a/misc/openlayers/lib/OpenLayers/Strategy/Save.js b/misc/openlayers/lib/OpenLayers/Strategy/Save.js new file mode 100644 index 0000000..2211e95 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Strategy/Save.js @@ -0,0 +1,231 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Strategy.js + */ + +/** + * Class: OpenLayers.Strategy.Save + * A strategy that commits newly created or modified features. By default + * the strategy waits for a call to <save> before persisting changes. By + * configuring the strategy with the <auto> option, changes can be saved + * automatically. + * + * Inherits from: + * - <OpenLayers.Strategy> + */ +OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, { + + /** + * APIProperty: events + * {<OpenLayers.Events>} An events object that handles all + * events on the strategy object. + * + * Register a listener for a particular event with the following syntax: + * (code) + * strategy.events.register(type, obj, listener); + * (end) + * + * Supported event types: + * start - Triggered before saving + * success - Triggered after a successful transaction + * fail - Triggered after a failed transaction + * + */ + + /** + * Property: events + * {<OpenLayers.Events>} Events instance for triggering this protocol + * events. + */ + events: null, + + /** + * APIProperty: auto + * {Boolean | Number} Auto-save. Default is false. If true, features will be + * saved immediately after being added to the layer and with each + * modification or deletion. If auto is a number, features will be + * saved on an interval provided by the value (in seconds). + */ + auto: false, + + /** + * Property: timer + * {Number} The id of the timer. + */ + timer: null, + + /** + * Constructor: OpenLayers.Strategy.Save + * Create a new Save strategy. + * + * Parameters: + * options - {Object} Optional object whose properties will be set on the + * instance. + */ + initialize: function(options) { + OpenLayers.Strategy.prototype.initialize.apply(this, [options]); + this.events = new OpenLayers.Events(this); + }, + + /** + * 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) { + if(this.auto) { + if(typeof this.auto === "number") { + this.timer = window.setInterval( + OpenLayers.Function.bind(this.save, this), + this.auto * 1000 + ); + } else { + this.layer.events.on({ + "featureadded": this.triggerSave, + "afterfeaturemodified": this.triggerSave, + 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) { + if(this.auto) { + if(typeof this.auto === "number") { + window.clearInterval(this.timer); + } else { + this.layer.events.un({ + "featureadded": this.triggerSave, + "afterfeaturemodified": this.triggerSave, + scope: this + }); + } + } + } + return deactivated; + }, + + /** + * Method: triggerSave + * Registered as a listener. Calls save if a feature has insert, update, + * or delete state. + * + * Parameters: + * event - {Object} The event this function is listening for. + */ + triggerSave: function(event) { + var feature = event.feature; + if(feature.state === OpenLayers.State.INSERT || + feature.state === OpenLayers.State.UPDATE || + feature.state === OpenLayers.State.DELETE) { + this.save([event.feature]); + } + }, + + /** + * APIMethod: save + * Tell the layer protocol to commit unsaved features. If the layer + * projection differs from the map projection, features will be + * transformed into the layer projection before being committed. + * + * Parameters: + * features - {Array} Features to be saved. If null, then default is all + * features in the layer. Features are assumed to be in the map + * projection. + */ + save: function(features) { + if(!features) { + features = this.layer.features; + } + this.events.triggerEvent("start", {features:features}); + var remote = this.layer.projection; + var local = this.layer.map.getProjectionObject(); + if(!local.equals(remote)) { + var len = features.length; + var clones = new Array(len); + var orig, clone; + for(var i=0; i<len; ++i) { + orig = features[i]; + clone = orig.clone(); + clone.fid = orig.fid; + clone.state = orig.state; + if(orig.url) { + clone.url = orig.url; + } + clone._original = orig; + clone.geometry.transform(local, remote); + clones[i] = clone; + } + features = clones; + } + this.layer.protocol.commit(features, { + callback: this.onCommit, + scope: this + }); + }, + + /** + * Method: onCommit + * Called after protocol commit. + * + * Parameters: + * response - {<OpenLayers.Protocol.Response>} A response object. + */ + onCommit: function(response) { + var evt = {"response": response}; + if(response.success()) { + var features = response.reqFeatures; + // deal with inserts, updates, and deletes + var state, feature; + var destroys = []; + var insertIds = response.insertIds || []; + var j = 0; + for(var i=0, len=features.length; i<len; ++i) { + feature = features[i]; + // if projection was different, we may be dealing with clones + feature = feature._original || feature; + state = feature.state; + if(state) { + if(state == OpenLayers.State.DELETE) { + destroys.push(feature); + } else if(state == OpenLayers.State.INSERT) { + feature.fid = insertIds[j]; + ++j; + } + feature.state = null; + } + } + + if(destroys.length > 0) { + this.layer.destroyFeatures(destroys); + } + + this.events.triggerEvent("success", evt); + + } else { + this.events.triggerEvent("fail", evt); + } + }, + + CLASS_NAME: "OpenLayers.Strategy.Save" +}); |