/**
* 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
*/