⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 jobvoyager.as

📁 用于flash/flex的 as3的 2D图形图像图表的动态生成
💻 AS
字号:
package flare.apps
{
	import flare.animate.Transitioner;
	import flare.data.DataSet;
	import flare.data.DataSource;
	import flare.display.TextSprite;
	import flare.query.methods.eq;
	import flare.query.methods.iff;
	import flare.util.Orientation;
	import flare.util.Shapes;
	import flare.util.Strings;
	import flare.vis.Visualization;
	import flare.vis.controls.ClickControl;
	import flare.vis.controls.HoverControl;
	import flare.vis.controls.TooltipControl;
	import flare.vis.data.Data;
	import flare.vis.data.DataSprite;
	import flare.vis.data.NodeSprite;
	import flare.vis.events.SelectionEvent;
	import flare.vis.events.TooltipEvent;
	import flare.vis.legend.Legend;
	import flare.vis.legend.LegendItem;
	import flare.vis.operator.filter.VisibilityFilter;
	import flare.vis.operator.label.StackedAreaLabeler;
	import flare.vis.operator.layout.StackedAreaLayout;
	import flare.widgets.ProgressBar;
	import flare.widgets.SearchBox;
	
	import flash.display.Shape;
	import flash.events.Event;
	import flash.filters.DropShadowFilter;
	import flash.geom.Rectangle;
	import flash.net.URLLoader;
	import flash.text.TextFormat;
	
	[SWF(backgroundColor="#ffffff", frameRate="30")]
	public class JobVoyager extends App
	{
		private var _bar:ProgressBar;
		private var _bounds:Rectangle;
		
		private var _vis:Visualization;
		private var _labelMask:Shape;
		private var _title:TextSprite;
		private var _search:SearchBox;
		private var _gender:Legend;
		
		private var _fmt:TextFormat = new TextFormat("Helvetica,Arial",16,0,true);
		private var _dur:Number = 1.25; // animation duration
		
		private var _t:Transitioner;
		
		private var _query:Array;
		private var _filter:String = "All";
		private var _exact:Boolean = false;
		
		private var _url:String = "http://flare.prefuse.org/data/jobs.txt";
		private var _cols:Array = [1850,1860,1870,1880,1900,1910,1920,1930,
								   1940,1950,1960,1970,1980,1990,2000];
		private var _titleText:String =
			"Reported Occupations - U.S. Labor Force, 1850 - 2000 " + 
			"(source: <a href='http://ipums.org'>http://ipums.org</a>)";
		
		protected override function init():void
		{
			addChild(_bar = new ProgressBar());
			_bar.bar.filters = [new DropShadowFilter(1)];
			
			var ds:DataSource = new DataSource(_url, "tab");
			var ldr:URLLoader = ds.load();
			_bar.loadURL(ldr, function():void {
				// get loaded data, reshape for stacked columns
  				var ds:DataSet = ldr.data as DataSet;
            	var dr:Array = reshape(ds.nodes.data, ["occupation","sex"],
            		"year", "people", _cols);
            	visualize(Data.fromArray(dr));
        		_bar = null;
			});
  		}
  		
  		private function visualize(data:Data):void
		{
			// prepare data with default settings and sort
			data.nodes.sortBy("data.occupation","data.sex");
			data.nodes.setProperties({
				shape: Shapes.POLYGON,
				lineColor: 0,
				fillValue: 1,
				fillSaturation: 0.5
			});
			// expression sets male -> blue, female -> red
			data.nodes.setProperty("fillHue", iff(eq("data.sex",1), 0.7, 0));
			
			// define the visualization
			_vis = new Visualization(data);
			// first, set the visibility according to the query
			_vis.operators.add(new VisibilityFilter(filter));
			_vis.operators[0].immediate = true; // filter immediately!
			// second, layout the stacked chart
			_vis.operators.add(new StackedAreaLayout(_cols, 0));
			_vis.operators[1].scale.labelFormat = "0.####%"; // show as percent
			// third, label the stacks
			_vis.operators.add(new StackedAreaLabeler("data.occupation"));
			// fourth, set the color saturation for the current view
			_vis.operators.add(new SaturationEncoder());
			
			// initialize y-axis labels: align and add mask
			_labelMask = new Shape();
			_vis.xyAxes.addChild(_labelMask); // hides extreme labels
			_vis.xyAxes.yAxis.labels.mask = _labelMask;
			_vis.xyAxes.yAxis.verticalAnchor = TextSprite.TOP;
			_vis.xyAxes.yAxis.horizontalAnchor = TextSprite.RIGHT;
			_vis.xyAxes.yAxis.labelOffsetX = 50;  // offset labels to the right
			_vis.xyAxes.yAxis.lineCapX1 = 15; // extra line length to the left
			_vis.xyAxes.yAxis.lineCapX2 = 50; // extra line length to the right
			_vis.xyAxes.showBorder = false;
			
			// place and update
			_vis.update();
			addChild(_vis);
						
			// add mouse-over highlight
			_vis.controls.add(new HoverControl(NodeSprite,
			    // move highlighted node to be drawn on top
				HoverControl.MOVE_AND_RETURN,
				// highlight node to full saturation
				function(e:SelectionEvent):void {
					e.node.props.saturation = e.node.fillSaturation;
					e.node.fillSaturation = 1;
				},
				// return node to previous saturation
				function(e:SelectionEvent):void {
					e.node.fillSaturation = e.node.props.saturation;
				}
			));
				
			// add filter on click
			_vis.controls.add(new ClickControl(NodeSprite, 1,
				// set search query to the occupation name
				function(e:SelectionEvent):void {
					_exact = true; // force an exact search
					_search.query = e.node.data.occupation;
				}
			));
			
			// add tooltips
			_vis.controls.add(new TooltipControl(NodeSprite, null,
			    // update on both roll-over and mouse-move
				updateTooltip, updateTooltip));
			
			// add title and search box
			addControls();
			layout();
		}
		
		private function updateTooltip(e:TooltipEvent):void
		{
			// get current year value from axes, and map to data
			var yr:Number = Number(
				_vis.xyAxes.xAxis.value(_vis.mouseX, _vis.mouseY));
			var year:String = (10 * Math.round(yr/10)).toString();
			var def:Boolean = (e.node.data[year] != undefined);
			
			TextSprite(e.tooltip).htmlText = Strings.format(
				"<b>{0}</b><br/>{1} in {2}: "+(def?"{3:0.###%}":"<i>{3}</i>"),
				e.node.data.occupation, e.node.data.sex==1?"Males":"Females",
				year, (def ? e.node.data[year] : "Missing Data"));
		}
		
		public override function resize(bounds:Rectangle):void
		{
			if (_bar) {
				_bar.x = bounds.width/2 - _bar.width/2;
				_bar.y = bounds.height/2 - _bar.height/2;
			}
			bounds.width -= (15 + 50);
			bounds.height -= (75 + 25);
			bounds.x += 15;
			bounds.y += 75;
			_bounds = bounds;
			layout();
		}
		
		private function layout():void
		{
			if (_vis) {
				// compute the visualization bounds
				_vis.bounds = _bounds;
				// mask the y-axis labels to hide extreme animation
				_labelMask.graphics.clear();
				_labelMask.graphics.beginFill(0);
				_labelMask.graphics.drawRect(_vis.bounds.right,
					 _vis.bounds.top, 60, 1+_vis.bounds.height);
				// update
				_vis.update();
			}
			if (_title) {
				_title.x = -1;
				_title.y = _bounds.top - _title.height - 45;
			}
			if (_search) {
				_search.x = 0;
				_search.y = _title.y + _title.height + 4;
			}
			if (_gender) {
				_gender.x = stage.stageWidth - _gender.width;
				_gender.y = _search.y;
			}
			
		}
		
		/** Filter function for determining visibility. */
		private function filter(d:DataSprite):Boolean
		{
			if (_filter == "Male" && d.data.sex != 1) {
				return false;
			} else if (_filter == "Female" && d.data.sex != 2) {
				return false;
			} else if (!_query || _query.length==0) {
				return true;
			} else {
				var s:String = String(d.data["occupation"]).toLowerCase();
				for each (var q:String in _query) {
					var len:int = q.length;
					if (len == 0) continue;
					if (!_exact && s.substr(0,len)==q) return true;
					if (_exact && q==s) return true;
				}
				return false;
			}
		}
		
		/** Callback for filter events. */
		private function onFilter(evt:Event=null):void
		{
			_query = _search.query.toLowerCase().split(/\|/);
			if (_query.length==1 && _query[0].length==0) _query.pop();
			
			if (_t && _t.running) _t.stop();
			_t = _vis.update(_dur);
			_t.play();
			
			_exact = false; // reset exact match after each search
		}
		
		// --------------------------------------------------------------------
		
		private function addControls():void
		{			
			// create title
			_title = new TextSprite("", _fmt, TextSprite.DEVICE);
			_title.htmlText = _titleText;
			_title.textField.selectable = false;
			addChild(_title);
			
			// create search box
			_search = new SearchBox(_fmt, ">", 250);
			_search.borderColor = 0xdedede;
			_search.input.tabIndex = 0;
			_search.input.restrict = "a-zA-Z \\-";
			_search.addEventListener(SearchBox.SEARCH, onFilter);
			addChild(_search);
			
			// create gender filter
			_gender = Legend.fromValues(null, [
				{label:"All",    color:0xff888888},
				{label:"Male",   color:0xff8888ff},
				{label:"Female", color:0xffff8888}
			]);
			_gender.orientation = Orientation.LEFT_TO_RIGHT;
			_gender.labelTextFormat = _fmt;
			_gender.margin = 3;
			_gender.setItemProperties({buttonMode:true, alpha:0.3});
			_gender.items.getChildAt(0).alpha = 1;
			_gender.update();
			addChild(_gender);
			
			// change alpha value on legend mouse-over
			new HoverControl(LegendItem, 0,
				function(e:SelectionEvent):void { e.object.alpha = 1; },
				function(e:SelectionEvent):void {
					var li:LegendItem = LegendItem(e.object);
					if (li.text != _filter) li.alpha = 0.3;
				}
			).attach(_gender);
			
			// filter by gender on legend click
			new ClickControl(LegendItem, 1, function(e:SelectionEvent):void {
				_gender.setItemProperties({alpha:0.3});
				e.object.alpha = 1;
				_filter = LegendItem(e.object).text;
				onFilter();
			}).attach(_gender);
		}
		
		// --------------------------------------------------------------------
		
		/**
		 * Reshapes a data set, pivoting from rows to columns. For example, if
		 * yearly data is stored in individual rows, this method can be used to
		 * map each year into a column and the full time series into a single
		 * row. This is often needed to use the stacked area layout.
		 * @param tuples an array of data tuples
		 * @param cats the category values to maintain
		 * @param dim the dimension upon which to pivot. The values of this
		 *  property should correspond to the names of newly created columns.
		 * @param measure the numerical value of interest. The values of this
		 *  property will be used as the values of the new columns.
		 * @param cols an ordered array of the new column names. These should
		 *  match the values of the <code>dim</code> property.
		 * @param normalize a flag indicating if the data should be normalized
		 */
		public static function reshape(tuples:Array, cats:Array, dim:String,
			measure:String, cols:Array, normalize:Boolean=true):Array
		{
			var t:Object, d:Object, val:Object, name:String;
			var data:Array = [], names:Array = []
			var totals:Object = {};
			for each (val in cols) totals[val] = 0;
			
			// create data set
			for each (t in tuples) {
				// create lookup hash for tuple
				var hash:String = "";
				for each (name in cats) hash += t[name];
				
				if (names[hash] == null) {
					// create a new data tuple
					data.push(d = {});
					for each (name in cats) d[name] = t[name];
					d[t[dim]] = t[measure];
					names[hash] = d;
				} else {
					// update an existing data tuple
					names[hash][t[dim]] = t[measure];
				}
				totals[t[dim]] += t[measure];
			}
			// zero out missing data
			for each (t in data) {
				var max:Number = 0;
				for each (name in cols) {
					if (!t[name]) t[name] = 0; // zero out null entries
					if (normalize)
						t[name] /= totals[name]; // normalize
					if (t[name] > max) max = t[name];
				}
				t.max = max;
			}
			return data;
		}
		
	} // end of class Stacks	
}

import flare.animate.Transitioner;
import flare.vis.data.DataSprite;
import flare.vis.operator.Operator;

class SaturationEncoder extends Operator
{
	public override function operate(t:Transitioner=null):void
	{
		t = (t ? t : Transitioner.DEFAULT);
		var m:Number=0, f:Number=0;
		
		// first pass: determine maximum visible value
		visualization.data.nodes.visit(function(d:DataSprite):void {
			if (d.data.sex == 1) {
				m = Math.max(m, d.data.max);
			} else {
				f = Math.max(f, d.data.max);
			}
		}, "visible");
		
		// second pass: set saturation
		visualization.data.nodes.visit(function(d:DataSprite):void {
			var s:Number = .3 + .3*d.data.max/((d.data.sex==1)?m:f);
			t.$(d).fillSaturation = s;
		}, "visible");
	}
	
} // end of class SaturationEncoder

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -