[OpenLayers-Commits] r12339 - in
sandbox/mpriour/temporal_map/openlayers/lib/OpenLayers: . Control
commits-20090109 at openlayers.org
commits-20090109 at openlayers.org
Wed Sep 7 13:45:47 EDT 2011
Author: mpriour
Date: 2011-09-07 10:45:46 -0700 (Wed, 07 Sep 2011)
New Revision: 12339
Added:
sandbox/mpriour/temporal_map/openlayers/lib/OpenLayers/TimeHandler.js
Removed:
sandbox/mpriour/temporal_map/openlayers/lib/OpenLayers/Control/TimeManager/
Modified:
sandbox/mpriour/temporal_map/openlayers/lib/OpenLayers/Control/TimeManager.js
Log:
- Modify TimeManager control to use simple class based TimeHandlers rather than OpenLayers.Control based TimeManager subclasses to control the time-enabled layer display.
- Modify TimeManager to automatically configure itself from the map and properly handle the addition or removal of layers to the map.
- Provide a method by which the control will use only the properties it was configured with instead of listening to layer related map events.
Modified: sandbox/mpriour/temporal_map/openlayers/lib/OpenLayers/Control/TimeManager.js
===================================================================
--- sandbox/mpriour/temporal_map/openlayers/lib/OpenLayers/Control/TimeManager.js 2011-09-07 17:34:09 UTC (rev 12338)
+++ sandbox/mpriour/temporal_map/openlayers/lib/OpenLayers/Control/TimeManager.js 2011-09-07 17:45:46 UTC (rev 12339)
@@ -21,6 +21,8 @@
* Constant: EVENT_TYPES
*
* Supported event types:
+ * - *beforetick* Triggered before the control advances one step in time.
+ * Return false to prevent the tick from occuring.
* - *tick* Triggered when the control advances one step in time.
* Listeners receive an event object with a *currentTime* parameter.
* Event is fired after the time has been incremented but before the
@@ -37,12 +39,12 @@
* property indicating the control reset due to running in looped mode
* (true) or the reset function call (false)
*/
- EVENT_TYPES: ["tick","play","stop","reset"],
+ EVENT_TYPES: ["beforetick","tick","play","stop","reset"],
/**
* Property: layers
- * {Array(<OpenLayers.Layer.Vector>)}
+ * {Array(<OpenLayers.Layer>)}
*/
layers: null,
@@ -62,17 +64,21 @@
/**
* Property: range
- * {Array(Date)} 2 member array containing the minimum and maximum times
+ * {Array(Date|String)} 2 member array containing the minimum and maximum times
* in UTC that the time-series animation will use. (Optional if using
* the intervals property). The 1st value should ALWAYS be less than
* the second value. Use negative step values to do reverse time.
+ * Note: You can use an ISO 8601 formated string (see
+ * http://tools.ietf.org/html/rfc3339) or Date objects.
*/
range:null,
/**
* Property: intervals
- * {Array(Date)} Array of valid distinct UTC dates/times that the time-
+ * {Array(Date|String)} Array of valid distinct UTC dates/times that the time-
* series animation can use. (Optional)
+ * Note: You can use an ISO 8601 formated string (see
+ * http://tools.ietf.org/html/rfc3339) or Date objects.
*/
intervals:null,
@@ -108,19 +114,13 @@
currentTime:null,
/**
- * Private Property: childControls
- * {Array(<OpenLayers.Control.TimeManager>)} An array of the controls that
+ * Private Property: timeHandlers
+ * {Array(<OpenLayers.TimeHandler>)} An array of the handlers that
* this control "manages". Read-Only
*/
- childControls:null,
+ timeHandlers:null,
/**
- * Private Property: utcOffset
- * {Number} millisecond difference between local & UTC time. Read-Only
- */
- utcOffset:new Date().getTimezoneOffset() * -6e4,
-
- /**
* Constructor: OpenLayers.Control.TimeManager
* Create a new time manager control.
*
@@ -134,21 +134,139 @@
if(this.intervals){
for(var i=0,len=this.intervals.length;i<len;i++){
var interval = this.intervals[i];
- if(!(interval[i] instanceof Date))this.intervals[i]=new Date(Date.parse(interval)+utcOffset);
+ if(!(interval[i] instanceof Date))this.intervals[i]=OpenLayers.Date.parse(interval);
}
+ this.intervals.sort(function(a,b){return b-a});
this.range=[this.intervals[0],this.intervals[this.intervals.length-1]];
+ this.fixedIntervals=true;
}else if(this.range){
- if(!(this.range[0] instanceof Date))this.range[0]=new Date(Date.parse(this.range[0])+utcOffset);
- if(!(this.range[1] instanceof Date))this.range[1]=new Date(Date.parse(this.range[1])+utcOffset);
+ if(!(this.range[0] instanceof Date))this.range[0]=OpenLayers.Date.parse(this.range[0]);
+ if(!(this.range[1] instanceof Date))this.range[1]=OpenLayers.Date.parse(this.range[1]);
+ this.fixedRange = true;
}
- this.currentTime = new Date(this.range[0].getTime());
- this.childControls = this.buildChildControls(options.layers,OpenLayers.Util.applyDefaults({
- utcOffset: this.utcOffset,
- intervals:this.intervals,
- range:this.range
- },options));
+ if (this.range[0]) {
+ this.currentTime = new Date(this.range[0].getTime());
+ }
+ if (options.layers) {
+ this.timeHandlers = this.buildTimeHandlers(options.layers);
+ if(this.timeHandlers.length){this.fixedLayers=true;}
+ }
},
/**
+ * APIMethod: destroy
+ * Destroys the control
+ */
+ destroy:function(){
+ for(var i=this.timeHandlers.length-1;i>-1;i--){
+ this.timeHandlers[i].destroy();
+ }
+ this.layers=null;
+ OpenLayers.Control.prototype.destroy.call(this);
+ },
+ /**
+ * APIMethod: setMap
+ * Sets the map parameter of the control. Also called automattically when
+ * the control is added to the map.
+ * Parameter:
+ * map {<OpenLayers.Map>}
+ */
+ setMap:function(map){
+ OpenLayers.Control.prototype.setMap.call(this,map);
+ //if the control was not directly intialized with specific layers, then
+ //get layers from map and build appropiate time handlers
+ if(!this.timeHandlers){
+ for(var i=0,len=map.layers.length;i<len;i++){
+ var lyr=map.layers[i];
+ if(lyr.metadata.timeInterval && lyr.metadata.timeInterval.length){
+ if(!this.layers)layers=[];
+ this.layers.push(lyr);
+ }
+ }
+ this.timeHandlers = this.buildTimeHandlers(this.layers)
+ }
+ //if no interval was specified & interval !== false, get from timeHandlers
+ if(!this.intevals && this.intervals !== false){
+ this.intervals = this.buildIntervals(this.timeHandlers);
+ }
+ //if no range was specified then get from timeHandlers
+ if(!this.range){
+ this.range = this.buildRange(this.timeHandlers);
+ }
+
+ //set map handlers for layer additions and removal
+ this.map.events.on({
+ 'addlayer':this.onAddLayer,
+ 'removelayer':this.onRemoveLayer,
+ scope:this
+ })
+ },
+ onAddLayer: function(evt){
+ var lyr = evt.layer;
+ var added=false;
+ if (lyr.metadata.timeInterval && !this.fixedLayers) {
+ this.timeHandlers || (this.timeHandlers = [])
+ var handlerClass = lyr.CLASS_NAME.match(/\.Layer\.(\w+)/)[1]
+ for (var i = 0, len = this.timeHandlers.length; i < len; i++) {
+ if (!lyr.timeHandler && this.timeHandlers[i] instanceof OpenLayers.TimeHandler[handlerClass]) {
+ this.timeHandlers[i].addLayer(lyr);
+ added = true;
+ break;
+ }
+ }
+ if (!added) {
+ var handlers = this.buildTimeHandlers([lyr]);
+ this.timeHandlers.push(handlers[0]);
+ added = true;
+ }
+ //check if layer could be used in a time handler & if so modify the
+ //control range & interval as needed. time handler will convert timeInterval
+ //values to real dates
+ if(added){
+ var lyrIntervals = lyr.metadata.timeInterval;
+ if (lyrIntervals.length > 2 && !this.fixedIntervals) {
+ this.intervals || (this.intervals = []);
+ this.intervals = this.getUniqueDates(this.intervals.concat(lyrIntervals));
+ //adjust range as needed
+ if(this.intervals[0]<this.range[0]){this.range[0]=new Date(this.intervals[0].getTime())}
+ if(this.intervals[1]>this.range[1]){this.range[1]=new Date(this.intervals[1].getTime())}
+ }
+ else if(!this.fixedRange){
+ if(lyrIntervals[0]<this.range[0]){this.range[0]=new Date(lyrIntervals[0].getTime())}
+ if(lyrIntervals[1]>this.range[1]){this.range[1]=new Date(lyrIntervals[1].getTime())}
+ }
+ }
+ }
+ },
+ onRemoveLayer:function(evt){
+ var lyr = evt.layer;
+ if(lyr.metadata.timeInterval){
+ var lyrIntervals = lyr.metadata.timeInterval;
+ //find the handler with the layer
+ for(var i=0, len = this.timeHandlers.length;i<len;i++){
+ var handler = this.timeHandlers[i];
+ if(OpenLayers.Util.indexOf(handler.layers,lyr)>-1){
+ handler.removeLayer(lyr);
+ //if the handler doesn't handle any layers, get rid of it
+ if(!handler.layers.length){
+ this.timeHandlers.splice(i,1);
+ handler.destroy();
+ }
+ break;
+ }
+ }
+ if(lyrIntervals.length>2 && !this.fixedIntervals){
+ this.intervals = this.buildIntervals(this.timeHandlers);
+ if(this.intervals[0]>this.range[0]){this.range[0]=new Date(this.intervals[0].getTime())}
+ if(this.intervals[1]<this.range[1]){this.range[1]=new Date(this.intervals[1].getTime())}
+ }
+ else if(!this.fixedRange){
+ if (lyrIntervals[0].getTime() == this.range[0].getTime() || lyrIntervals[1].getTime() == this.range[1].getTime()) {
+ this.range = this.buildRange(this.timeHandlers);
+ }
+ }
+ }
+ },
+ /**
* Method: tick
* Advance/reverse time one step forward/backward. Fires the 'tick' event
* if time can be incremented without exceeding the time range.
@@ -183,7 +301,19 @@
}
}
else {
- this.events.triggerEvent('tick', {currentTime: this.currentTime});
+ function canTickCheck(){
+ var canTick = false;
+ for (var i = 0, len = this.timeHandlers.length; i < len; i++) {
+ canTick = this.timeHandlers[i].canTick;
+ if (!canTick) break;
+ }
+ return canTick;
+ }
+ if (canTickCheck()) {
+ this.events.triggerEvent('tick', {currentTime: this.currentTime});
+ }else{
+ //TODO: Handle not yet ready timeHandlers & don't tick until we're ready
+ }
}
},
/**
@@ -213,11 +343,12 @@
* current time only if the animation is not currently running
*
* Parameters:
- * range - {Arrray} UTC time range
+ * range - {Arrray(Date|String)} UTC time range using either Date objects
+ * or ISO 8601 formatted strings
*/
setRange:function(range){
- if(!(range[0] instanceof Date))range[0]=new Date(Date.parse(range[0])+utcOffset);
- if(!(range[1] instanceof Date))range[1]=new Date(Date.parse(range[1])+utcOffset);
+ if(!(range[0] instanceof Date))range[0]=OpenLayers.Date.parse(range[0]);
+ if(!(range[1] instanceof Date))range[1]=OpenLayers.Date.parse(range[1]);
this.range=range;
//set current time to correct location if the timer isn't running yet.
if(!this.timer){this.currentTime = this.range[(this.step>0)?0:1]}
@@ -229,10 +360,11 @@
* the currentTime if an animation has not begun.
*
* Parameters:
- * time - {Object} UTC start time/date
+ * time - {Date|String} UTC start time/date using either a Date object or
+ * ISO 8601 formatted string.
*/
setStart:function(time){
- if(!(time instanceof Date))time=new Date(Date.parse(time)+utcOffset);
+ if(!(time instanceof Date))time=OpenLayers.Date.parse(time);
this.range[(this.step>0)?0:1]=time;
//set current time to this start time if we haven't already started
!this.timer && (this.currentTime=time);
@@ -244,10 +376,11 @@
* the current time.
*
* Parameters:
- * time - {Object} UTC stop time/date
+ * time - {Date|String} UTC stop time/date using either a Date object or
+ * ISO 8601 formatted string.
*/
setEnd:function(time){
- if(!(time instanceof Date))time=new Date(Date.parse(time)+utcOffset);
+ if(!(time instanceof Date))time=OpenLayers.Date.parse(time);
this.range[(this.step>0)?1:0]=time;
},
/**
@@ -255,10 +388,11 @@
* Manually sets the currentTime used in the control's animation.
*
* Parameters: {Object} time
- * time - {Object} UTC current animation time/date
+ * time - {Date|String} UTC current animantion time/date using either a
+ * Date object or ISO 8601 formatted string.
*/
setTime:function(time){
- if(!(time instanceof Date))time=new Date(Date.parse(time)+utcOffset);
+ if(!(time instanceof Date))time=OpenLayers.Date.parse(time);
this.currentTime = time;
},
/**
@@ -271,7 +405,7 @@
reset:function(){
this.timer && clearInterval(this.timer);
this.currentTime = (this.step>0)?new Date(this.range[0].getTime()):new Date(this.range[1].getTime());
- this.events.triggerEvent('reset',{'looped':false});
+ this.events.triggerEvent('reset',{'looped':false,'currentTime':this.currentTime});
return this.currentTime;
},
/**
@@ -288,56 +422,121 @@
this.currentTime['setUTC'+stepUnit](this.currentTime['getUTC'+stepUnit]()+step);
},
/**
- * Private Method: buildChildControls
+ * Private Method: buildTimeHandlers
* Creates the controls "managed" by this control.
*
* Parameters:
* layers - {Array(<OpenLayers.Layer>)}
- * options - {Object} Options to pass to the control constructors
*
* Returns:
* {Array(<OpenLayers.Control.TimeControl>)}
*/
- buildChildControls:function(layers,options){
+ buildTimeHandlers:function(layers){
layers = layers || this.layers;
- var layerTypes = {}, childControls = [];
+ var layerTypes = {}, handlers = [];
//categorize layers and separate into arrays for use in subclasses
for(var i=0,len=layers.length;i<len;i++){
var lyr = layers[i];
- var lyrClass = lyr.CLASS_NAME.match(/\.Layer\.(\w+)/)[1];
- if(OpenLayers.Control.TimeManager[lyrClass]){
- !layerTypes[lyrClass] && (layerTypes[lyrClass]=[])
- layerTypes[lyrClass].push(lyr);
- }
+ //allow user specified overrides and custom behavior
+ if (lyr.timeHandler) {
+ var handler;
+ if(lyr.timeHandler instanceof OpenLayers.TimeHandler){
+ handler = lyr.timeHandler;
+ handler.layers = (handler.layers && handler.layers instanceof Array)?(handler.layers.push(lyr)):handler.layers=[lyr];
+ handler.timeManager=this;
+ }else if(lyr.timeHandler instanceof Function){
+ handler = new OpenLayers.TimeHandler({
+ onTick:lyr.timeHandler,
+ layers:[lyr],
+ timeManager:this
+ })
+ }
+ this.events.on({
+ tick: handler.onTick,
+ scope: this
+ })
+ handlers.push(handler);
+ }
+ else {
+ var lyrClass = lyr.CLASS_NAME.match(/\.Layer\.(\w+)/)[1];
+ if (OpenLayers.TimeHandler[lyrClass]) {
+ !layerTypes[lyrClass] && (layerTypes[lyrClass] = [])
+ layerTypes[lyrClass].push(lyr);
+ }
+ }
}
- //create subclassed child controls
+ //create subclassed time handlers
for(var k in layerTypes){
- var ctlClass = OpenLayers.Control.TimeManager[k];
- var ctlOptions = OpenLayers.Util.applyDefaults({
- layers:layerTypes[k],
- parentControl:this
- },options);
- var ctl = new ctlClass(ctlOptions);
+ var handler = new OpenLayers.TimeHandler[k]({
+ layers: layerTypes[k],
+ timeManager: this
+ });
this.events.on({
- 'tick': ctl.onTick,
- scope: ctl
+ 'tick': handler.onTick,
+ scope: handler
})
- childControls.push(ctl)
+ handlers.push(handler)
}
- return childControls;
+ return handlers;
},
/**
- * APIMethod: destroy
- * Destroys the control
+ * Method: buildIntervals
+ * Builds an array of distinct date/times that the time handlers are
+ * configured with
+ * Parameters:
+ * handlers - {Array(<OpenLayers.TimeHandler>)}
+ * (Optional) An array of time handlers to calculate the intervals from.
+ * Defaults to the control's timeHandlers property.
+ * Returns: {Array(Date)}
*/
- destroy:function(){
- for(var i=this.childControls.length-1;i>-1;i--){
- this.childControls[i].destroy();
- }
- this.layers=null;
- OpenLayers.Control.prototype.destroy.call(this);
- },
-
+ buildIntervals:function(handlers){
+ handlers = handlers || this.timeHandlers;
+ var intervals = [];
+ for(var i=0,len=handlers.length;i<len;i++){
+ var handler = handlers[i];
+ if(handler.intervals){
+ intervals = intervals.concat(handler.intervals);
+ }
+ }
+ intervals =(intervals.length)?this.getUniqueDates(intervals):null;
+ return intervals;
+ },
+ /**
+ * Method: buildRange
+ * Builds an 2 member array with the overall start & stop date/times that
+ * the time handlers are configured with.
+ * Parameters:
+ * handlers - {Array(<OpenLayers.TimeHandler>)}
+ * (Optional) An array of time handlers to calculate the intervals from.
+ * Defaults to the control's timeHandlers property.
+ * Returns: {Array(Date)}
+ */
+ buildRange:function(handlers){
+ handlers = handlers || this.timeHandlers;
+ var range = [];
+ for(var i=0,len=handlers.length;i<len;i++){
+ var subrange = handlers[i].range;
+ if(!subrange[0] || subrange[0]<range[0]){range[0]=new Date(subrange[0].getTime())}
+ if(!subrange[1] || subrange[1]>range[1]){range[1]=new Date(subrange[1].getTime())}
+ }
+ return range;
+ },
+ getUniqueDates:function(dates){
+ //sort the times
+ dates.sort(function(a, b){
+ return b - a;
+ });
+ //filter for unique
+ dates = OpenLayers.Array.filter(dates, function(item, index, array){
+ for (var i = index + 1; i < array.length; i++) {
+ if (item.getTime() == array[i].getTime()) {
+ return false
+ }
+ }
+ return true;
+ });
+ return dates
+ },
CLASS_NAME:'OpenLayers.Control.TimeManager'
});
Added: sandbox/mpriour/temporal_map/openlayers/lib/OpenLayers/TimeHandler.js
===================================================================
--- sandbox/mpriour/temporal_map/openlayers/lib/OpenLayers/TimeHandler.js (rev 0)
+++ sandbox/mpriour/temporal_map/openlayers/lib/OpenLayers/TimeHandler.js 2011-09-07 17:45:46 UTC (rev 12339)
@@ -0,0 +1,133 @@
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
+ * full list of contributors). Published under the Clear BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Control/TimeManager.js
+ */
+
+/**
+ * Class: OpenLayers.TimeHandler.WMS
+ * Class to display and animate WMS layers across time.
+ * This class is created by {OpenLayers.Control.TimeManager} instances
+ *
+ * Inherits From:
+ * - <OpenLayers.Class>
+ */
+OpenLayers.TimeHandler = OpenLayers.Class({
+ /**
+ * Property: timeManager
+ * {<OpenLayers.Control.TimeManager>}
+ */
+ timeManager:null,
+ /**
+ * Property: canTick
+ * {Boolean}
+ */
+ canTick:true,
+ /**
+ * Property: intervals
+ * {Array(Date)}
+ */
+ intervals:null,
+ /**
+ * Property: range
+ * {Array(Date)}
+ */
+ range:null,
+ /**
+ * Property: layers
+ * {Array(<OpenLayers.Layer>)}
+ */
+ layers:null,
+ /**
+ * Constructor: OpenLayers.Control.TimeManager
+ * Create a new time manager control for temporal layers.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * control.
+ */
+ initialize:function(options){
+ this.events = new OpenLayers.Events(this, null);
+ OpenLayers.Util.extend(this,options||{});
+ if(this.eventListeners instanceof Object) {
+ this.events.on(this.eventListeners);
+ }
+ if (this.layers) {
+ var timeConfig = this.buildRangeAndIntervals(this.layers);
+ this.range = timeConfig.range;
+ this.intervals = timeConfig.intervals;
+ }
+ },
+
+ onTick:function(){
+ //Implemented By Subclasses
+ },
+ addLayer: function(layer){
+ this.layers = (!this.layers)?[layer]:this.layers.concat(layer);
+ var timeInterval = layer.metadata.timeInterval;
+ if (timeInterval.length == 2) {
+ if (timeInterval[0] < this.range[0]) {this.range[0] = new Date(timeInterval[0].getDate())}
+ if (timeInterval[1] > this.range[1]) {this.range[1] = new Date(timeInterval[1].getDate())}
+ }
+ else {
+ var timeConfig = this.buildRangeAndIntervals(this.layers);
+ this.range = timeConfig.range;
+ this.intervals = timeConfig.intervals;
+ }
+ },
+ removeLayer:function(layer){
+ for(var i=0,len=this.layers.length;i<length;i++){
+ if(layer==this.layers[i]){
+ this.layers.splice(i,1);
+ break;
+ }
+ }
+ var timeInterval = layer.metadata.timeInterval;
+ /*if we only had a range and this layer wasn't one of the end points
+ then we don't need to do anything, otherwise we might as well rebuild
+ the range & intervals*/
+ if(this.intervals || timeInterval[0].getTime() == this.range[0].getTime() || timeInterval[1].getTime() == this.range[1].getTime()){
+ var timeConfig = this.buildRangeAndIntervals(this.layers);
+ this.range = timeConfig.range;
+ this.intervals = timeConfig.intervals;
+ }
+ },
+ buildRangeAndIntervals:function(layers){
+ var range = [], intervals=[];
+ for(var i=0,len=layers.length;i<len;i++){
+ var timeInterval = (layers[i].metadata)?layers[i].metadata.timeInterval:null;
+ if (timeInterval) {
+ for (var j = 0; j < timeInterval.length; j++) {
+ if (!(timeInterval[j] instanceof Date)) {
+ timeInterval[j] = OpenLayers.Date.parse(timeInterval[j])
+ }
+ }
+ if (timeInterval.length == 2) {
+ if (!range[0] || timeInterval[0] < range[0]) {range[0] = timeInterval[0];}
+ if (!range[1] || timeInterval[1] > range[1]) {range[1] = timeInterval[1];}
+ }
+ else if (timeInterval.length > 2) {
+ for (var j = 0; j < timeInterval.length; j++) {
+ if (OpenLayers.Util.indexOf(intervals, timeInterval[j]) == -1) {
+ intervals.push(timeInterval[j])
+ }
+ }
+ }
+ }
+ }
+ if(intervals.length){
+ intervals = this.timeManager.getUniqueDates(intervals);
+ if (!range[0] || intervals[0] < range[0]) {range[0] = intervals[0];}
+ if (!range[1] || intervals[1] > range[1]) {range[1] = intervals[1];}
+ }else{
+ intervals=null;
+ }
+ return {'range':range,'intervals':intervals}
+ },
+
+ CLASS_NAME:'OpenLayers.TimeHandler'
+});
\ No newline at end of file
More information about the Commits
mailing list