GpsGate Server JavaScript API

Interface  1.0.0

GpsGate Server JavaScript API > Interface > imap.js (source view)
Search:
 
Filters
/**
 * Copyright Franson Technology AB, Sweden, 2009 <br/>
 * http://gpsgate.com, http://franson.com <br/>
 * <br />
 * <p>
 * author Fredrik Blomqvist
 * </p>
 *
 *
 * Placeholder for abstract interface specifications.
 * @module Interface
 *
 */

/*
 * <b>example</b><br />
 * typically use a namespace like this for utility functions related to your map-type.
 * @class Franson.MyMap
 * @static
 */
Franson.MyMap =
{
	/*
	 * <b>example</b><br />
	 * expose your map's projection here
	 * pixel<->latlng
	 * @method getProjection
	 * @param {NativeMapType} map
	 * @return {Franson.Map.IProjection}
	 */
	getProjection: function(map)
	{
		return {
			// convert from screen pixels (x,y) to map Latitude/Longitude (lat, lng)
			fromContainerPixelToLatLng: function(pos)
			{
				var latlng = map.convertXYToLatLon(new NativePixelPos(pos.x, pos.y)); // hypotetical example type
				return { lat: latlng.Latitude, lng: latlng.Longitude };
			},
			// convert from map Latitude/Longitude to screen pixels (x,y)
			fromLatLngToContainerPixel: function(latlng)
			{
				var pos = map.convertLatLonToXY(new NativeLatLng(latlng.lat, latlng.lng));
				return { x: pos.x, y: pos.y };
			}
		};
	},

	//--- typical functions you might need:

	/*
	 * <b>example</b><br />
	 * convert from your position object to Franson compatible
	 */
	fromNativeLatLng: function(latlng)
	{
		return { lat: latlng.Latitude, lng: latlng.Longitude };
	},

	/*
	 * <b>example</b><br />
	 * convert from Franson latitude/longitude specification to your position type
	 */
	toNativeLatLng: function(latlng)
	{
		return new NativeLatLng(latlng.lat, latlng.lng);
	},

	/*
	 * <b>example</b><br />
	 * convert from your latitude/longitude bounds object to Franson.Geo.Bounds
	 */
	fromNativeGeoBounds: function(bounds)
	{
		return new Franson.Geo.Bounds(
			Franson.IMap.fromNativeLatLng(bounds.southWest()),
			Franson.IMap.fromNativeLatLng(bounds.northEast())
		);
	}

	// ....
};

/**
 * interface for the projection object returned from Map.getProjection()
 * @class Franson.Map.IProjection
 * @constructor
 */
Franson.Map.IProjection = function()
{
	//
};

// methods
Franson.Map.IProjection.prototype =
{
	/**
	 * Convert from map Latitude/Longitude to screen pixels (x,y)
	 * @method fromLatLngToContainerPixel
	 * @param {LatLng} latLng (lat,lng)
	 * @return {Point} (x,y)
	 */
	fromLatLngToContainerPixel: function(latLng) // todo: should we extend these to take a zoom also like GMap? (would be more powerful)
	{
		// [...]
		return { x: 123, y: 123 };
	},

	/**
	 * Convert from screen pixels (x,y) to map Latitude/Longitude (lat, lng)
	 * @method fromContainerPixelToLatLng
	 * @param {Point} point (x,y)
	 * @return {LatLng}
	 */
	fromContainerPixelToLatLng: function(point)
	{
		// [...]
		return { lat: 123, lng: 123 };
	}

	// todo: should we document the generated DivPixel versions also?
};



/**
 * <p>
 * Interface specification for the abstract Map interface.
 * </p>
 * <p>
 * Reference stub for creating a map interface compatible with GpsGate Server applications (v2.1.1+)
 * Instantiated via the Custom Scripts plugin mechanism.
 * </p>
 * <p>
 * The interface is largely inspired by Google Maps since it is a proven and well documented technology for web-maps, thus
 * most tile based web maps should be possible to fit to it.<br />
 * ref: <a href="http://code.google.com/apis/maps/documentation/reference.html">Google Maps API Reference</a>
 * </p>
 * reference:
 *
 * - Franson.Geo module (Franson.Geo/geo.js)
 * Vector math:
 * - Franson.Common (Franson.Common/Vec2.js)
 *
 * other notes:
 * - all external events are handled using <a href="http://mochikit.com/doc/html/MochiKit/Signal.html">MochiKit.Signal</a>
 * - ..
 *
 * @class Franson.Map.IMap
 * @param {string|div} div if passsed a div the div must have an <code>.id</code> property
 * @param [options]
 * @constructor
 */
Franson.Map.IMap = function(div, options) // todo: hmm, should remove constructor stuff from an Interface spec..
{
	/**
	 * per object
	 * @type string
	 */
	this.id = 'imap'; // mostly for use with the StateMgr

	//-- typical

	/**
	 * could extract from map also (if exposed)
	 * @type DIV
	 * @private
	 */
	this._container = $(div);

	//----instantiate your map here ---
	/**
	 * @private
	 * @type NativeMapType
	 */
	this._map = new NativeMap(this._container);
	// .. do other setups you need here
	//--------

	/**
	 * @private
	 * @type Franson.Map.MapSurface
	 */
	this._mapSurface = new Franson.Map.MapSurface(Franson.IMap.getProjection(this._map));

//	this._mapSurface.init(map-canvas-div, MochiKit.Style.getElementDimensions(this.getContainer()));

	// attach to map's event handlers for 'onmoved', onzoom etc and call surface.redraw (and fwd those events)

	// todo: fwd mouse events from surface to this level from mapSurface (if we don't use the Native Map's events)

	// todo: optionally attach mousehweel zoom-support and dblclick and keyboard handling etc

	// default impl. (in case map startup is not asynchronous, otherwise you need to signal this from the native "onload"-equivalent)
	if (this.isLoaded())
	{
		signal(this, 'onload');
	}
};


// methods
Franson.Map.IMap.prototype =
{
	/**
	 * unique id for the map adapter type (per <i>class</i> level)
	 * @type string
	 * @final
	 * @static
	 */
	type: 'imap',

	/**
	 * destructor <br />
	 * frees all used resources.
	 * Disconnects DOM-nodes, event-handlers etc
	 * destructor
	 * @method destroy
	 */
	destroy: function()
	{
		// [*destroy your native map resources here*]
		//-----
		disconnectAll(this);
		this._mapSurface.destroy();
		this._mapSurface = null;
		this._map = null;
	},

	/**
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.isLoaded">GMap.isLoaded()</a>
	 * @method isLoaded
	 * @return {boolean}
	 */
	isLoaded: function()
	{
		// default impl. (if map doesn't have asynchronous startup procedure (or you load the script dynamically))
		return true;
	},

	/**
	 * Returns the geographical coordinates of the center point of the map view.
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.getCenter">GMap.getCenter()</a>
	 * @method getCenter
	 * @return {LatLng}
	 */
	getCenter: function()
	{
		// [*RETURN MAP CENTER HERE*]
		// (could return surface.getCenter (but will only give precision on a pixel-grid level)
	},

	/**
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.setCenter">GMap.setCenter()</a>
	 * @method setCenter
	 * @param {LatLng} center
	 * @param {number} [zoom]
	 */
	setCenter: function(center, zoom)
	{
		// set map center and zoom here ..
	},

	/**
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.panTo">GMap.panTo()</a>
	 * @method panTo
	 * @param {LatLng} center
	 */
	panTo: function(center)
	{
		// default impl. (if map has no smooth-pan function)
		this.setCenter(center);
	},

	/**
	 * Returns the size of the map view in pixels.
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.getSize">GMap.getSize()</a>
	 * @method getSize
	 * @return {(w, h)}
	 */
	getSize: function()
	{
		// [*RETURN MAP SIZE*]
		// (could return surface.getSize)
	},

	/**
	 * Returns the current zoom level.
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.getZoom">GMap.getZoom()</a>
	 * @method getZoom
	 * @return {integer}
	 */
	getZoom: function()
	{
		// [*RETURN ZOOM LEVEL*]
	},

	/**
	 * set zoom level. larger values => zoom in
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.setZoom">GMap.setZoom()</a>
	 * @method setZoom
	 * @param {integer} zoom
	 */
	setZoom: function(zoom)
	{
		var oldLevel = this.getZoom();

		// [*SET ZOOM LEVEL*]

		var newLevel = this.getZoom(); // we don't just use 'zoom' to account for possible clamping of the value
		signal(this, 'onzoomend', oldLevel, newLevel);
	},

	/**
	 * Returns the the visible rectangular region of the map view in geographical coordinates.
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.getBounds">GMap.getBounds()</a>
	 * @method getBounds
	 * @return {Franson.Geo.Bounds}
	 */
	getBounds: function()
	{
		// [*return map bounds here*]

		// could return surface.getBounds but gives possible lower precision
	},

	/**
	 * Returns the zoom level at which the given rectangular region fits in the map view.
	 * The zoom level is computed for the currently selected map type. If no map type is selected yet, the first on the list of map types is used.
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.getBoundsZoomLevel">GMap.getBoundsZoomLevel()</a>
	 * or just expose a zoomToFit(bounds) ?
	 * todo: we need some way of signalling wheter the bounds/zoom fit our not.. (other maps will quite often "fail" here due to lack of resolution..)
	 * @method getBoundsZoomLevel
	 * @param {Franson.Geo.Bounds} bounds
	 * @return {integer}
	 */
	getBoundsZoomLevel: function(bounds)
	{
		// [*CALCULATE AT WHiCH ZOOM LEVEL THE BOUNDS WILL BEST FIT INSIDE*]
	},

	/**
	 * Notifies the map of a change of the size of its container.<br />
	 * Call this method after the size of the container DOM object has changed, so that the map can adjust itself to fit the new size.
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.checkResize">GMap.checkResize()</a>
	 * @method checkResize
	 */
	checkResize: function()
	{
		// [* *]

		// if getSize is implemented using surface.getSize
		// we Can't call getSize here but need to use native map's size
		// or we could let the surface periodically poll the div for size? (map24 does this)
	},

	//--------

	/**
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.zoomIn">GMap.zoomIn()</a>
	 * @method zoomIn
	 */
	zoomIn: function() // todo: hmm, google zoomIn can take a latlng..
	{
		// default impl.
		var oldLevel = this.getZoom();
		this.setZoom(this.getZoom() + 1);
		var newLevel = this.getZoom(); // we don't just use oldLevel+1 to account for possible clamping
		signal(this, 'onzoomend', oldLevel, newLevel);
	},

	/**
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.zoomOut">GMap.zoomOut()</a>
	 * @method zoomOut
	 */
	zoomOut: function() // todo: hmm, google zoomOut can take a latlng..
	{
		// default impl.
		var oldLevel = this.getZoom();
		this.setZoom(this.getZoom() - 1);
		var newLevel = this.getZoom(); // we don't just use oldLevel-1 to account for possible clamping
		signal(this, 'onzoomend', oldLevel, newLevel);
	},

	/**
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.panBy">GMap.panBy()</a>
	 * @method panBy
	 * @param {(w,h)} distance .w and .h params default to 0, i.e you can supply only .w for example
	 */
	panBy: function(distance)
	{
		// default impl.
		var center = this.getCenter();
		var centerPx = this.getProjection().fromLatLngToContainerPixel(center);
		var newCenterPx = Franson.Vec2.sub(centerPx, { x: distance.w || 0, y: distance.h || 0 });
		var newCenter = this.getProjection().fromContainerPixelToLatLng(newCenterPx);
		this.panTo(newCenter);
	},

	/**
	 * Starts a pan animation by half the width of the map in the indicated directions. +1 is right and down, -1 is left and up, respectively.
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.panDirection">GMap.panDirection()</a>
	 * @method panDirection
	 * @param {integer} dx
	 * @param {integer} dy
	 */
	panDirection: function(dx, dy)
	{
		// default impl.
		var size = this.getSize();
		this.panBy({ w: dx*size.w/2, h: dy*size.h/2 });
	},

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

	/**
	 * <i>you should not need to modify this</i><br />
	 * returns the wrapped map-type.<br />
	 * (should rarely need to be used, typically users inspects the .type property also)
	 * @method getNativeMap
	 * @return {NativeMapType}
	 */
	getNativeMap: function()
	{
		return this._map;
	},

	/**
	 * <i>you should not need to modify this</i><br />
	 * @method getProjection
	 * @return {Franson.Map.IProjection}
	 */
	getProjection: function()
	{
		// we return the projection from the surface since it includes the panning margins and DivPixel-versions of the projection methods
		return this.getSurface().getProjection();
	},

	/**
	 * <i>you should not need to modify this</i><br />
	 * Returns the DOM object that contains the map
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.getContainer">GMap.getContainer()</a>
	 * @method getContainer
	 * @return {DOM}
	 */
	getContainer: function()
	{
		return this._container;
	},

	/**
	 * <i>you should not need to modify this</i><br />
	 * @method getSurface
	 * @return {Franson.Map.MapSurface}
	 */
	getSurface: function()
	{
		return this._mapSurface;
	},

	//--------

	/**
	 * <i>you should not need to modify this</i><br />
	 * @method addLayer
	 * @param {Franson.Map.ILayer} layer
	 */
	addLayer: function(layer)
	{
		layer._map = this; // force in map reference (for now, should rather change layer.initialize call to receive the map instead of mapsurface)
		this.getSurface().addLayer(layer);
		signal(this, 'onaddlayer', layer);
	},

	/**
	 * <i>you should not need to modify this</i><br />
	 * currently O(N)
	 * @method removeLayer
	 * @param {Franson.Map.ILayer|string} layer a layer object or a string (layer-id)
	 */
	removeLayer: function(layer)
	{
		this.getSurface().removeLayer(layer);
		signal(this, 'onremovelayer', layer); // if we're allowing a string as input this needs to be normalized to alway return layer anyway...
	},

	/**
	 * <i>you should not need to modify this</i><br />
	 * currently O(N)
	 * @method getLayer
	 * @param {string} id
	 * @return {Franson.Map.ILayer}
	 */
	getLayer: function(id)
	{
		return this.getSurface().getLayer(id);
	},

	/**
	 * <i>you should not need to modify this</i><br />
	 * @method getLayers
	 * @return {Franson.Map.ILayer[]}
	 */
	getLayers: function()
	{
		return this.getSurface().getLayers();
	},

	/**
	 * <i>you should not need to modify this</i><br />
	 * note that this doesn't .destroy the layers (but it disconnects events from the layers)
	 * @method clearLayers
	 */
	clearLayers: function()
	{
		this.getSurface().clearLayers();
		signal(this, 'onclearlayers');
	},

	/**
	 * [*not implemented yet*]
	 * Opens a simple info window at the given point. Pans the map such that the opened info window is fully visible.
	 * The content of the info window is given as a DOM node.
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.openInfoWindow">GMap.openInfoWindow()</a>
	 * note that we don't support tabs etc (and currently not the html version)
	 * @method openInfoWindow
	 * @param {LatLng} latlng
	 * @param {DOM} node content DOM node
	 * @param {object} [options]
	 */
	openInfoWindow: function(latlng, node, options)
	{
		// not implemented by mapsurface yet..
		signal(this, 'oninfowindowopen');
	},

	/**
	 * [*not implemented yet*]
	 * Closes the currently open info window.
	 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.closeInfoWindow">GMap.closeInfoWindow()</a>
	 * @method closeInfoWindow
	 */
	closeInfoWindow: function()
	{
		// not implemented by mapsurface yet..
		signal(this, 'oninfowindowclose'); // todo: do we need 'onbeforeinfowindowclose' etc?
	},

	// -----

	// todo: should extract this as an IStateful(?) interface? (+ a .id property requirement)
	/**
	 * <i>you should not need to modify this</i><br />
	 * not strictly necessary. Used by StateManager.
	 * @method _saveState
	 * @return {object} must be JSON-serializable.
	 * @protected
	 */
	_saveState: function()
	{
		// default impl.
		return Franson.Map.saveMapState(this);
	},

	/**
	 * <i>you should not need to modify this</i><br />
	 * not strictly necessary. Used by StateManager
	 * @method _restoreState
	 * @param {object} state
	 * @protected
	 */
	_restoreState: function(state)
	{
		// default impl.
		if (this.type == state.type)
			Franson.Map.restoreMapState(this, state);
	},

	/**
	 * <i>you should not need to modify this</i><br />
	 * signal connection interceptor. currently only used to monitor delayed 'onload' connections.<br />
	 * called indirectly by <a href="http://mochikit.com/doc/html/MochiKit/Signal.html#fn-connect">MochiKit.Signal.connect()</a>
	 * todo: should inject this as a mixin.
	 * @method __connect__
	 * @private
	 */
	__connect__: function(ident, eventName, objOrFunc, funcOrStr)
	{
		// support case of attaching after onload has initially fired
		if (eventName == 'onload' && this.isLoaded())
		{
			// immediately notify the handler
			bind(funcOrStr, objOrFunc)();
		}
	}

}; // Franson.Map.IMap


//------- event specification ----------

/**
 * fired when map setup is complete and <a href="#method_isLoaded">isLoaded()</a> would return <code>true</code>.<br />
 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.load">GMap.load</a>
 * @event onload
 */

/**
 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.mouseover">GMap.mouseover</a>
 * @event onmouseover
 * @param {LatLng} latlng
 */

/**
 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.mouseout">GMap.mouseout</a>
 * @event onmouseout
 * @param {LatLng} latlng
 */

/**
 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.mousemove">GMap.mousemove</a>
 * @event onmousemove
 * @param {LatLng} latlng
 */

/**
 * fired in <a href="#method_setZoom">setZoom()</a>, <a href="#method_zoomIn">zoomIn()</a>, <a href="#method_zoomOut">zoomOut()</a> (and <a href="#method_setCenter">setCenter()</a> if zoom was changed)<br />
 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.zoomend">GMap.zoomend</a>
 * @event onzoomend
 * @param {integer} oldLevel
 * @param {integer} newLevel
 */

/**
 * fired in <a href="#method_addLayer">addLayer()</a>
 * @event onaddlayer
 * @param {Franson.Map.ILayer} layer the added layer
 */

/**
 * fired in <a href="#method_removeLayer">removeLayer()</a>
 * @event onremovelayer
 * @param {Franson.Map.ILayer} layer the removed layer
 */

/**
 * fired in <a href="#method_clearLayers">clearLayers()</a>
 * @event onclearlayers
 */

/**
 * fired in <a href="#method_openInfoWindow">openInfoWindow()</a> <br />
 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.infowindowopen">GMap.infowindowopen</a>
 * @event oninfowindowopen
 */

/**
 * fired in <a href="#method_closeInfoWindow">closeInfoWindow()</a> <br />
 * see <a href="http://code.google.com/apis/maps/documentation/reference.html#GMap2.infowindowclose">GMap.infowindowclose</a>
 * @event oninfowindowclose
 */

/**
 * @event onclick
 * @param {LatLng} latlng
 */

/**
 * @event ondblclick
 * @param {LatLng} latlng
 */

/*
 * //event onsinglerightclick
 * //param ...
 */



//----------------------------------------------------------------------------------------
// todo move these specs to a "types.js"?

/**
 * specification for the LatLng literal
 * @class LatLng
 */
/**
 * Latitude
 * @property lat
 * @type number
 */
/**
 * Longitude
 * @property lng
 * @type number
 */


/**
 * specification for the (x,y) Point literal.
 * Also referred to as Coordinates, Position or (2d)Vector.
 * see also <a href="http://mochikit.com/doc/html/MochiKit/Style.html#fn-coordinates">MochiKit.Style.Coordinates</a>
 * @class Point
 */
/**
 * @property x
 * @type number
 */
/**
 * @property y
 * @type number
 */


/**
 * specification for the (w,h) Size literal.
 * Also referred to as Dimensions.<br />
 * see also <a href="http://mochikit.com/doc/html/MochiKit/Style.html#fn-dimensions">MochiKit.Style.Dimensions</a>
 * @class Size
 */
/**
 * width
 * @property w
 * @type number
 */
/**
 * height
 * @property h
 * @type number
 */


/**
 * specification for the (x,y,w,h) Bounds literal.<br />
 * Union of Point and Size (Position and Dimensions).
 * (a concrete class is available as Franson.Geometry.Bounds also).
 * @class Bounds
 */
/**
 * @property x
 * @type number
 */
/**
 * @property y
 * @type number
 */
/**
 * width
 * @property w
 * @type number
 */
/**
 * height
 * @property h
 * @type number
 */


// todo: document an IControl interface etc

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