GpsGate Server JavaScript API

MochiKit  1.0.0

GpsGate Server JavaScript API > MochiKit > polyline.js (source view)
Search:
 
Filters
/**
 * Copyright Franson Technology AB, Sweden, 2009
 * http://gpsgate.com, http://franson.com
 *
 * author Fredrik Blomqvist
 *
 * @module Map
 *
 */
var Franson = Franson || {};
/*
 * namespace
 * //class Franson.Map
 * //static
 */
Franson.Map = Franson.Map || {};

/**
 * aprox match of the <a href="http://code.google.com/apis/maps/documentation/reference.html#GPolyline">GPolyline</a> interface.<br />
 * simple base (no simplification. should use the more advanced Track class)
 * todo: not sure its necessary to mimic all of Gmap here.. (or perhaps this could be simple enough to be editable?)
 * @param {LatLng[]} latlngs
 * @param {string|number[]|dojo.Color} [color='blue']
 * @param {number} [weight=5]
 * @param {number} [opacity=0.45]
 * @param {literal} [options]
 * @class Franson.Map.Polyline
 * @extends Franson.Map.IOverlay
 * @constructor
 */
Franson.Map.Polyline = function(latlngs, color, weight, opacity, options)
{
	this._options = MochiKit.Base.setdefault(options, {
		'clickable': true // todo: if using delayed attachment this is not necessary!
		// todo: show directional arrows, breadcrumb mode, simplify level etc
		// editable: false etc
		// todo: should either support iterable latlngs or take property-acccessors for item & length here also (see Shape.createSimplifiedTrack)

		// performance tuning (also some browsers doesn't handle very large polylines)
		, segmentSize: 150 // max number of points in each sub-poly
	});

	/**
	 * @type LatLng[]
	 * @private
	 */
	this._latlngs = latlngs;

	/**
	 * @private @type Franson.Geo.Bounds
	 */
	this._bounds = Franson.Geo.createBounds(this._latlngs);

	color = dojox.gfx.normalizeColor(color || 'blue');
		color.a = opacity || 0.45; // allow opacity=0? (this will override 0)
	/**
	 *
	 * @private @type dojox.gfx.Stroke (literal)
	 */
	this._stroke = Franson.Graphics.normalizeStroke({
		'color': color,
		'width': weight || 5, // allow weight=0? (this will override 0)
		// todo: inner sub-polylines should have different caps than border polylines
		'cap': 'round',
		'join': 'round'
	});

	/**
	 * @private @type dojox.gfx.shape.Group
	 */
	this._root = null;

	/** @private */
	this._layer = null; // .. ok?

	/** @private */
	this._clickHandler = null;

	//------------------------

	// split track into smaller segments for better culling (todo: insert into hierarchial tree (need array of geobounds similar to the segment-array)
	//-- generate indices into the latlng list to be used for sub-polys (to be able to merge visible spans of sub-polys) ---------
	var numSplits = Math.ceil(this._latlngs.length / this._options.segmentSize); // todo: if nr of remaing points is "small" add anyway?

	/**
	 * @private @type Array[..]
	 */
	this._segments = MochiKit.Base.map(
		method(this, function(index)
		{
			// adjacent segments need to use same start and end indices to not cause gaps
			// todo: ! don't allow sub-tracks with only 1pt => gaps
			var startIndex = index*this._options.segmentSize;
			var endIndex = Math.min(startIndex + this._options.segmentSize + 1, this._latlngs.length);

			// todo: using this structure a bounds culler could sort the visible segments and merge adjacent(continous) segments spans and feed into a simplifier (track)
			return {
				startIndex: startIndex,
				endIndex: endIndex,

				bounds: Franson.Geo.createBounds(
					MochiKit.Iter.islice(this._latlngs, startIndex, endIndex)
				),

				poly: null // ref to each polysegment here to be able to drop when out-of-view
			};
		}),
		MochiKit.Iter.range(numSplits)
	);
};


// methods
Franson.Map.Polyline.prototype =
{
	/**
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GPolyline.getVertexCount">GPolyline.getVertexCount()</a>
	 * @method getVertexCount
	 * @return {integer}
	 */
	getVertexCount: function()
	{
		return this._latlngs.length;
	},

	/**
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GPolyline.getVertex">GPolyline.getVertex()</a>
	 * @methode getVertex
	 * @param {integer} index
	 * @return {LatLng}
	 */
	getVertex: function(index)
	{
		return this._latlngs[index];
	},

	/**
	 * ok? (not part of GPolyline interface)
	 * @method getVertices
	 * @return {LatLng[]}
	 */
	getVertices: function()
	{
		return this._latlngs;
	},

	/**
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GPolyline.getBounds">GPolyline.getBounds()</a>
	 * @method getBounds
	 * @return {Franson.Geo.Bounds}
	 */
	getBounds: function()
	{
		return this._bounds;
	},

	/**
	 * @method hide
	 */
	hide: function()
	{
		Franson.Graphics.hide(this._root);
		signal(this, 'onvisibilitychanged', false);
	},

	/**
	 * @method show
	 */
	show: function()
	{
		Franson.Graphics.show(this._root);
		signal(this, 'onvisibilitychanged', true);
	},

	/**
	 * @method isHidden
	 * @return {boolean}
	 */
	isHidden: function()
	{
		return Franson.Graphics.isHidden(this._root);
	},

	/**
	 * @method initialize
	 * @param {Franson.Map.ILayer} layer
	 * @protected
	 */
	initialize: function(layer)
	{
		this._layer = layer;

		this._root = this._layer.getRoot().createGroup();

		if (this._options.clickable) // todo: make this lazy using __connect__ listener
		{
			// todo: if we wish to extract the index also we might benefit from connecting to the sub-polys to reduce search size
			this._clickHandler = this._root.connect('onclick', this, function(e)
			{
				// todo: the mapsurface should help with fixing mouseevets like this I'd say
				var mousePos = Franson.Graphics.getMouseSurfacePosition(this._layer.getRoot(), e);

				var latlng = this._layer.getProjection().fromContainerPixelToLatLng(mousePos);

				signal(this, 'onclick', latlng);
			});

			Franson.Graphics.setCursor(this._root, Franson.Util.isIE() ? 'hand' : 'pointer');
		}
	},

	/**
	 * @method redraw
	 * @param {boolean} [force=false]
	 * @protected
	 */
	redraw: function(force)
	{
		if (!force)
			return;

		this._root.clear(); // reset

		var projection = this._layer.getProjection();
		var cullBounds = this._layer.getSurface()._getCullBounds();

		// todo: bounds culling (see track etc)
		for (var i = 0; i < this._segments.length; ++i)
		{
			var segment = this._segments[i];

			if (segment.bounds.intersects(cullBounds))
			{
				var polyline = this._root.createPolyline(
					// todo: should modify dojo to be able to take iterable as input (it just pushes objs into low-level array anyway)
					MochiKit.Base.map(
						function(latlng)
						{
							return projection.fromLatLngToDivPixel(latlng);
						},
						MochiKit.Iter.islice(this._latlngs, segment.startIndex, segment.endIndex)
					)
				);
				polyline.setStroke(this._stroke); // todo: should set stroke end-style (cap) of inner segments to

				segment.poly = polyline;
			}
			else
			{
				if (Franson.Graphics.isHidden(segment.poly)) // flickers otherwise (slow..) (change to use a flag)
					Franson.Graphics.show(segment.poly);
			}
		}
	},

	/**
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GOverlay.remove">GOverlay.remove()</a>
	 * @protected
	 */
	remove: function()
	{
		if (this._root)
		{
			if (this._clickHandler != null)
			{
				this.root.disconnect(this._clickHandler);
				this._clickHandler = null;
			}
			this._root.removeShape();
			signal(this, 'onremove');
		}
	},

	/**
	 * destructor
	 * @method destroy
	 */
	destroy: function()
	{
		this.remove();
		disconnectAll(this);
		this._root = null;
		this._segments = null;
		this._stroke = null;
	},

	/**
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GPolyline.setStrokeStyle">GPolyline.setStrokeStyle()</a>
	 * @param {object} stroke dojox.gfx.Stroke spec literal (todo: simplify and follow GPolyline?)
	 */
	setStrokeStyle: function(stroke)
	{
		this._stroke = Franson.Graphics.normalizeStroke(stroke);
	}

}; // Franson.Map.Polyline.prototype


/**
 * fired in remove()
 * @event onremove
 */

/**
 * (if options.clickable)
 * @event onclick
 * @param {LatLng} latlng
 */

/**
 * fired in hide() and show()
 * @event onvisibilitychanged
 * @param {boolean} visible
 */

Copyright © 2009 Franson Technology AB, Sweden. All rights reserved.