/**
* @namespace ajs.maps.gmapdraw
* @memberof ajs.maps
*/
ajs.extend({ gmapdraw: {} }, ajs.maps);
// load the css stylesheet
ajs.css('gmapdraw.css');
/** Incremental variable which assures the creation of a new instance id every time a new instance is created */
ajs.maps.gmapdraw.instances_ids = 0;
/** Whether or not to display debug information in the console */
ajs.maps.gmapdraw.debug = false;
// look at initialize method for class description
ajs.maps.gmapdraw.map = (function() {
// private members
var _private = {};
// options
var _options = {};
// mootools class
return new Class({
/**
* @summary Google maps drawing class, provides tools for drawing over a google map instance, and export drawed data.
* @classdesc <p>This class handles the drawing tools used to draw over a google map and allows the drawed data exportation.</p>
* <p>The map manages also some controllers</p>
* <ul>
* <li>clear map controller</li>
* <li>export map controller</li>
* <li>geocoder text field controller</li>
* <li>tips controller</li>
* </ul>
* <p>Moreover every drawing tool has its own controller, which may be specifically set or used in its default form.</p>
* <p>Each map controller may be specified custom, may be removed setting the related option to <code>null</code> or used in its default form.</p>
* <p>Once instantiated the class and set the tools by options or instantiating direclty the drawing tool classes and adding them to the map,
* see {@link ajs.maps.gmapdraw.map#addTool}, call the render method to render the widget.
* Then it is possible to continue configuring the widget adding or removing tools,
* customizing the google map instance which is returned by the {@link ajs.maps.gmapdraw.map#gmap} method.</p>
* <p>When defining specific map controllers, be sure to make them handle the proper map methods.</p>
* <p>Very important: be sure to load the google maps library yourself in the head of the document!</p>
*
* @constructs ajs.maps.gmapdraw.map
* @param {String} canvas The id attribute of the map container
* @param {Object} [options] A class options object
* @param {Array} [options.center=new Array(45, 7)] The initial map center coordinates, (lat, lng).
* @param {Number} [options.zoom=8] The the initial map zoom level.
* @param {Object} [options.tools={}] The object containing the tool's names and optionsa to be activated when initializing the map.
* It's a shortcut to easily define set and active tools objects.
* @param {Object} [options.tools.point=undefined] The point tool init object
* @param {String|Element} [options.tools.point.ctrl=undefined] The id attribute or the element itself which controls the tool, default the built-in menu voice
* @param {Object} [options.tools.point.options=undefined] The tool options object, see {@link ajs.maps.gmapdraw.pointTool} for available properties
* @param {Object} [options.tools.polyline=undefined] The polyline tool init object
* @param {String|Element} [options.tools.polyline.ctrl=undefined] The id attribute or the element itself which controls the tool, default the built-in menu voice
* @param {Object} [options.tools.polyline.options=undefined] The tool options object, see {@link ajs.maps.gmapdraw.polylineTool} for available properties
* @param {Object} [options.tools.polygon=undefined] The polygon tool init object
* @param {String|Element} [options.tools.polygon.ctrl=undefined] The id attribute or the element itself which controls the tool, default the built-in menu voice
* @param {Object} [options.tools.polygon.options=undefined] The tool options object, see {@link ajs.maps.gmapdraw.polygonTool} for available properties
* @param {Object} [options.tools.circle=undefined] The circle tool init object
* @param {String|Element} [options.tools.circle.ctrl=undefined] The id attribute or the element itself which controls the tool, default the built-in menu voice
* @param {Object} [options.tools.circle.options=undefined] The tool options object, see {@link ajs.maps.gmapdraw.circleTool} for available properties
* @param {String|Element} [options.clear_map_ctrl='default'] The clear map controller (clears all drawings over the map).
* If 'default' the built-in controller is used, if <code>null</code> the clear map
* functionality is removed. If id attribute or an element the clear map functionality is attached to the element.
* @param {String|Element} [options.export_map_ctrl='default'] The export map controller (exports all shapes drawed over the map).
* If 'default' the built-in controller is used, if <code>null</code> the export map
* functionality is removed. If id attribute or an element the clear map functionality is attached to the element.
* @param {Function} [options.export_map_callback=null] The callback function to call when the export map button is pressed. The callback function receives one argument, the exported data as
* returned by the ajs.maps.gmapdraw.map#exportMap method.
* @param {Boolean} [options.geocoder_map_field=true] Whether or not to add the gecoder functionality which allows to center the map in a point defined through an address, or to
* pass the lat,lng coordinates found to the map click handlers (exactly as click over the map in a lat,lng point).
* @param {String|Element} [options.tips_map_ctrl='default'] The help tips map controller (shows tips about drawing tools).
* If 'default' the built-in controller is used, if <code>null</code> the tips box is not shown,
* if id attribute or an element the functionality is attached to the element.
*
* @example
* var mymap = new ajs.maps.gmapdraw.map('my_map_canvas_id', {
* tools: {
* point: {
* options: {
* max_items: 5
* }
* },
* circle: {}
* }
* });
*
*/
initialize: function(canvas, options) {
this.id = ajs.maps.gmapdraw.instances_ids++;
_private[this.id] = {};
if(typeOf(document.id(canvas)) != 'element') {
throw new Error('Canvas container not found');
}
else {
_private[this.id].super_canvas = document.id(canvas);
}
var super_canvas_coords = _private[this.id].super_canvas.getCoordinates();
_private[this.id].canvas = new Element('div', {id: 'gmapdraw_gmap_canvas'}).setStyles({
height: super_canvas_coords.height + 'px'
}).inject(_private[this.id].super_canvas);
_private[this.id].super_canvas.setStyles({
position: 'relative',
height: 'auto'
});
_options[this.id] = {
center: [45, 7],
zoom: 8,
tools: {},
clear_map_ctrl: 'default',
export_map_ctrl: 'default',
export_map_callback: null,
geocoder_map_field: true,
tips_map_ctrl: 'default'
}
_options[this.id] = Object.merge(_options[this.id], options);
_private[this.id].map_types = {
'hybrid': google.maps.MapTypeId.HYBRID,
'roadmap': google.maps.MapTypeId.ROADMAP,
'satellite': google.maps.MapTypeId.SATELLITE,
'terrain': google.maps.MapTypeId.TERRAIN
};
_private[this.id].supported_tools = ['point', 'polyline', 'polygon', 'circle'];
_private[this.id].drawing_tool = null;
_private[this.id].tools = [];
_private[this.id].ctrl_container = null;
_private[this.id].map = null;
_private[this.id].clear_map_ctrl = null;
_private[this.id].clear_map_ctrl_event = null;
_private[this.id].export_map_ctrl = null;
_private[this.id].export_map_ctrl_event = null;
_private[this.id].tips_map_ctrl = null;
_private[this.id].geocoder = null;
_private[this.id].geocoder_field = null;
_private[this.id].geocoder_center_button = null;
_private[this.id].geocoder_draw_button = null;
this.processOptions();
this.addControllersContainer();
},
/**
* @summary Adds an empty container over the map which may contain default controllers if any
* @memberof ajs.maps.gmapdraw.map.prototype
* @method
* @protected
* @return void
*/
addControllersContainer: function() {
_private[this.id].ctrl_container = new Element('div', {id: 'gmapdraw_controllers_container'});
// inject controllers container
var canvas_coord = _private[this.id].canvas.getCoordinates();
_private[this.id].ctrl_container.inject(_private[this.id].super_canvas, 'top');
}.protect(),
/**
* @summary Adds a controller in the default controllers container
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {Element} ctrl The controller to be added
* @return void
*/
addDefaultCtrl: function(ctrl) {
if(typeOf(ctrl) != 'element') {
throw new Error('The given controller is not a DOM element');
}
ctrl.inject(_private[this.id].ctrl_container, 'top');
},
/**
* @summary Processes the option object setting properly some class properties
* @memberof ajs.maps.gmapdraw.map.prototype
* @method
* @protected
* @return void
*/
processOptions: function() {
// init tools
_private[this.id].supported_tools.each(function(tool_name) {
if(_options[this.id].tools.hasOwnProperty(tool_name)) {
var handler = null;
var ctrl = _options[this.id].tools[tool_name].ctrl || null;
// set tool
if(ctrl) {
handler = typeOf(ctrl) === 'string' ? document.id(ctrl) : ctrl;
if(typeOf(handler) != 'element') {
throw new Error('The given control handler for the ' + tool_name + ' tool is not a DOM element');
}
}
// add the tool
this.addTool(new ajs.maps.gmapdraw[tool_name + 'Tool'](this, handler, _options[this.id].tools[tool_name].options || null));
}
}.bind(this));
}.protect(),
/**
* @summary Adds a drawing tool
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {ajs.maps.gmapdraw.tool} tool The tool object
* @return void
*/
addTool: function(tool) {
if(!instanceOf(tool, ajs.maps.gmapdraw.tool)) {
throw new Error('The ' + tool_name + ' tool object given is not of the proper type');
}
if(!_private[this.id].supported_tools.contains(tool.getToolName())) {
throw new Error('The ' + tool_name + ' tool is not supported');
}
_private[this.id].tools[tool.getToolName()] = tool;
},
/**
* @summary Gets a tool object giving its name
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {String} tool_name One of the supported tools name
* @return {ajs.maps.gmapdraw.tool | null} The tool object if set or null
*/
getTool: function(tool_name) {
if(!_private[this.id].supported_tools.contains(tool_name)) {
throw new Error('The ' + tool_name + ' tool is not supported');
}
if(typeof _private[this.id].tools[tool_name] === 'undefined') {
return null;
}
return _private[this.id].tools[tool_name];
},
/**
* @summary Removes a drawing tool
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {String} tool_name The name of the tool to be removed
* @param {ajs.maps.gmapdraw.tool} tool The tool object
* @return void
*/
removeTool: function(tool_name) {
if(!_private[this.id].supported_tools.contains(tool_name)) {
throw new Error('The ' + tool_name + ' tool is not supported');
}
if(_private[this.id].tools[tool_name]) {
_private[this.id].tools[tool_name].deactivate(true);
delete _private[this.id].tools[tool_name];
}
},
/**
* @summary Renders the widget
* @memberof ajs.maps.gmapdraw.map.prototype
* @return void
*/
render: function() {
// map initialization
this.initMap();
// add controllers
this.initControllers();
// init tools
this.initTools();
},
/**
* Initializes the google map and its events
* @memberof ajs.maps.gmapdraw.map.prototype
* @return void
*/
initMap: function() {
var map_center = new google.maps.LatLng(_options[this.id].center[0], _options[this.id].center[1]);
var myOptions = {
center: map_center,
zoom: _options[this.id].zoom,
zoomControlOptions: {
style: google.maps.ZoomControlStyle.LARGE,
position: google.maps.ControlPosition.RIGHT_CENTER
},
panControl: false,
mapTypeControl: true,
mapTypeControlOptions: {
position: google.maps.ControlPosition.LEFT_BOTTOM
},
mapTypeId: google.maps.MapTypeId.ROADMAP
};
_private[this.id].map = new google.maps.Map(_private[this.id].canvas, myOptions);
_private[this.id].geocoder = new google.maps.Geocoder();
google.maps.event.addListener(_private[this.id].map, 'click', this.mapClick.bind(this));
},
/**
* @summary Initializes all the map controllers
* @memberof ajs.maps.gmapdraw.map.prototype
* @return void
*/
initControllers: function() {
if(_options[this.id].clear_map_ctrl) this.setClearMapController();
if(_options[this.id].export_map_ctrl && _options[this.id].export_map_callback) this.setExportMapController();
if(_options[this.id].tips_map_ctrl) this.setTipsMapController();
if(_options[this.id].geocoder_map_field) this.setGeocoderMapFieldController();
},
/**
* @summary Sets the clear map controller depending on the options.clear_map_ctrl value
* @memberof ajs.maps.gmapdraw.map.prototype
* @method
* @protected
* @return void
*/
setClearMapController: function() {
if(_options[this.id].clear_map_ctrl === 'default') {
_private[this.id].clear_map_ctrl = new Element('div', {id: 'gmapdraw_clear_map_ctrl'}).set('text', 'clear map');
_private[this.id].clear_map_ctrl.inject(_private[this.id].ctrl_container);
}
else if(_options[this.id].clear_map_ctrl) {
_private[this.id].clear_map_ctrl = typeOf(_options[this.id].clear_map_ctrl) === 'element' ? _options[this.id].clear_map_ctrl : document.id(_options[this.id].clear_map_ctrl);
if(typeOf(_private[this.id].clear_map_ctrl) != 'element') {
throw new Error('The given clear map controller is not a DOM element');
}
}
_private[this.id].clear_map_ctrl_event = this.clearMap;
_private[this.id].clear_map_ctrl.addEvent('click', _private[this.id].clear_map_ctrl_event.bind(this));
}.protect(),
/**
* @summary Removes the clear map controller depending on the options.clear_map_ctrl value
* @memberof ajs.maps.gmapdraw.map.prototype
* @method
* @protected
* @return void
*/
removeClearMapController: function() {
_private[this.id].clear_map_ctrl.removeEvent('click', _private[this.id].clear_map_ctrl_event);
if(_options[this.id].clear_map_ctrl === 'default') _private[this.id].clear_map_ctrl.dispose();
}.protect(),
/**
* @summary Sets the export map controller depending on the options.export_map_ctrl value
* @memberof ajs.maps.gmapdraw.map.prototype
* @method
* @protected
* @return void
*/
setExportMapController: function() {
if(_options[this.id].export_map_ctrl === 'default') {
_private[this.id].export_map_ctrl = new Element('div', {id: 'gmapdraw_export_map_ctrl'}).set('text', 'export map');
_private[this.id].export_map_ctrl.inject(_private[this.id].ctrl_container);
}
else if(_options[this.id].export_map_ctrl) {
_private[this.id].export_map_ctrl = typeOf(_options[this.id].export_map_ctrl) === 'element' ? _options[this.id].export_map_ctrl : document.id(_options[this.id].export_map_ctrl);
if(typeOf(_private[this.id].export_map_ctrl) != 'element') {
throw new Error('The given export map controller is not a DOM element');
}
}
_private[this.id].export_map_ctrl_event = function() { _options[this.id].export_map_callback(this.exportMap());}.bind(this);
_private[this.id].export_map_ctrl.addEvent('click', _private[this.id].export_map_ctrl_event.bind(this));
}.protect(),
/**
* @summary Removes the export map controller depending on the options.clear_map_ctrl value
* @memberof ajs.maps.gmapdraw.map.prototype
* @method
* @protected
* @return void
*/
removeExportMapController: function() {
_private[this.id].export_map_ctrl.removeEvent('click', _private[this.id].export_map_ctrl_event);
if(_options[this.id].export_map_ctrl === 'default') _private[this.id].export_map_ctrl.dispose();
}.protect(),
/**
* @summary Sets the help tips map controller depending on the options.tips_map_ctrl value
* @memberof ajs.maps.gmapdraw.map.prototype
* @method
* @protected
* @return void
*/
setTipsMapController: function() {
if(_options[this.id].tips_map_ctrl === 'default') {
_private[this.id].tips_map_ctrl = new Element('div', {id: 'gmapdraw_tips_map_ctrl'});
_private[this.id].tips_map_ctrl.inject(_private[this.id].ctrl_container);
}
else if(_options[this.id].tips_map_ctrl) {
_private[this.id].tips_map_ctrl = typeOf(_options[this.id].tips_map_ctrl) === 'element' ? _options[this.id].tips_map_ctrl : document.id(_options[this.id].tips_map_ctrl);
if(typeOf(_private[this.id].tips_map_ctrl) != 'element') {
throw new Error('The given tips map controller is not a DOM element');
}
}
if(_private[this.id].tips_map_ctrl) this.updateTips(this.initMapTips());
}.protect(),
/**
* @summary Removes the tips map controller depending on the options.tips_map_ctrl value
* @memberof ajs.maps.gmapdraw.map.prototype
* @method
* @protected
* @return void
*/
removeTipsMapController: function() {
if(_options[this.id].tips_map_ctrl === 'default') _private[this.id].tips_map_ctrl.dispose();
}.protect(),
/**
* @summary Sets the geocoder input text field and its controllers
* @memberof ajs.maps.gmapdraw.map.prototype
* @method
* @protected
* @return void
*/
setGeocoderMapFieldController: function() {
_private[this.id].geocoder_field = new Element('input', {id:'gmapdraw_geocoder_field', type: 'text', placeholder: 'insert an address'});
_private[this.id].geocoder_center_button = new Element('input', {id: 'gmapdraw_geocoder_center_button', type: 'button', value: 'set map center'});
_private[this.id].geocoder_draw_button = new Element('input', {id: 'gmapdraw_geocoder_draw_button', type: 'button', value: 'draw'});
_private[this.id].geocoder_center_button.addEvent('click', this.geocoderCenter.bind(this));
_private[this.id].geocoder_draw_button.addEvent('click', this.geocoderDraw.bind(this));
_private[this.id].ctrl_container.adopt(_private[this.id].geocoder_field, _private[this.id].geocoder_center_button, _private[this.id].geocoder_draw_button);
}.protect(),
/**
* @summary Removes the geocoder input text field and its controllers
* @memberof ajs.maps.gmapdraw.map.prototype
* @method
* @protected
* @return void
*/
removeGeocoderMapField: function() {
_private[this.id].geocoder_center_button.removeEvents();
_private[this.id].geocoder_draw_button.removeEvents();
[_private[this.id].geocoder_field, _private[this.id].geocoder_center_button, _private[this.id].geocoder_draw_button].each(function(el) { el.dispose(); });
}.protect(),
/**
* @summary Sets the active drawing tool name
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {ajs.maps.gmapdraw.tool|null} tool The actual drawing tool, null to have no active tool
* @return void
*/
setDrawingTool: function(tool) {
if(tool != null && !_private[this.id].tools.hasOwnProperty(tool.getToolName())) {
throw new Error('Can\'t set the drawing tool since it\'s not active');
}
_private[this.id].drawing_tool = tool;
},
/**
* @summary Gets the active drawing tool
* @memberof ajs.maps.gmapdraw.map.prototype
* @return {ajs.maps.gmapdraw.tool} The drawing tool
*/
getDrawingTool: function() {
return _private[this.id].drawing_tool;
},
/**
* @summary Initializes the map set tools
* @memberof ajs.maps.gmapdraw.map.prototype
* @return void
*/
initTools: function() {
for(var k in _private[this.id].tools) {
if(_private[this.id].supported_tools.contains(k)) {
_private[this.id].tools[k].activate();
}
}
},
/**
* @summary Updates the text displayed in the tips controller
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {String} text The tip text
* @return void
*/
updateTips: function(text) {
if(_private[this.id].tips_map_ctrl) {
_private[this.id].tips_map_ctrl.set('html', text);
}
},
/**
* @summary Returns the init text shown in the tips controller
* @memberof ajs.maps.gmapdraw.map.prototype
* @return {String} text The initial tip text
*/
initMapTips: function() {
return 'Displays help tips about drawing tools';
},
/**
* @summary Handles the click event over the map, calling the active tool handler
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {Object} point The callback parameter returned by the google.maps event handler
* @description This method is public since it has to be called by google maps api
* @return void
*/
mapClick: function(point) {
if(ajs.maps.gmapdraw.debug) console.log('map click event triggered');
if(_private[this.id].drawing_tool === null) {
return false;
}
else {
_private[this.id].drawing_tool.clickHandler(point);
}
},
/**
* @summary Sets the map center converting the geocoder_field input address in a LatLng point
* @memberof ajs.maps.gmapdraw.map.prototype
* @return void
*/
geocoderCenter: function() {
var request = {address: _private[this.id].geocoder_field.get('value')}
_private[this.id].geocoder.geocode(request, function(results, status) {
var result = results[0];
if(status === 'OK') {
_private[this.id].map.setCenter(result.geometry.location);
}
else {
if(ajs.maps.gmapdraw.debug) console.log('geocoder response status: ' + status);
alert(status);
}
}.bind(this));
},
/**
* @summary Fires a map click in a LatLng point converted from the geocoder_field input address
* @memberof ajs.maps.gmapdraw.map.prototype
* @return void
*/
geocoderDraw: function() {
var request = {address: _private[this.id].geocoder_field.get('value')}
_private[this.id].geocoder.geocode(request, function(results, status) {
var result = results[0];
if(status === 'OK') {
if(_private[this.id].drawing_tool === null) alert('select a drawing tool');
this.mapClick({latLng: result.geometry.location});
}
else {
if(ajs.maps.gmapdraw.debug) console.log('geocoder response status: ' + status);
alert(status);
}
}.bind(this));
},
/**
* @summary Clears the map
* @memberof ajs.maps.gmapdraw.map.prototype
* @return void
*/
clearMap: function() {
for(var k in _private[this.id].tools) {
if(_private[this.id].supported_tools.contains(k)) {
_private[this.id].tools[k].clear();
}
}
if(ajs.maps.gmapdraw.debug) {
console.log('map cleared');
}
},
/**
* @summary Exports the map drawed shapes as data points
* @memberof ajs.maps.gmapdraw.map.prototype
* @return {Object} data The drawed data in an object format
* @example
* {
* 'point': [
* {lat: 45, lng: 12},
* {lat: 43, lng: 16}
* ],
* 'polyline': [
* [
* {lat: 45, lng: 12},
* {lat: 42, lng: 12},
* {lat: 42.6, lng: 11}
* ],
* [
* {lat: 36.7, lng: 11.2},
* {lat: 39, lng: 12}
* ],
* ],
* 'circle': [
* {lat: 45, lng: 12, radius: 10000},
* {lat: 44, lng: 11, radius: 230000}
* ]
* }
*/
exportMap: function() {
var data = {};
for(var k in _private[this.id].tools) {
if(_private[this.id].supported_tools.contains(k)) {
data[k] = _private[this.id].tools[k].exportData();
}
}
if(ajs.maps.gmapdraw.debug) {
console.log(data);
console.log('map exported');
}
return data;
},
/**
* @summary Imports data to the map
* @description Data must be in the same form as the exported ones, see {@link ajs.maps.gmapdraw.map#exportMap}
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {Object} data The drawed data in an object format
*/
importMap: function(data) {
if(typeOf(data.point) != 'null') {
if(this.getTool('point') === null) {
var ptool = new ajs.maps.gmapdraw.pointTool(this, null);
this.addTool(ptool);
ptool.activate();
}
this.getTool('point').importData(data.point);
}
if(typeOf(data.polyline) != 'null') {
if(this.getTool('polyline') === null) {
var ptool = new ajs.maps.gmapdraw.polylineTool(this, null);
this.addTool(ptool);
ptool.activate();
}
this.getTool('polyline').importData(data.polyline);
}
if(typeOf(data.polygon) != 'null') {
if(this.getTool('polygon') === null) {
var ptool = new ajs.maps.gmapdraw.polygonTool(this, null);
this.addTool(ptool);
ptool.activate();
}
this.getTool('polygon').importData(data.polygon);
}
if(typeOf(data.circle) != 'null') {
if(this.getTool('circle') === null) {
var ctool = new ajs.maps.gmapdraw.circleTool(this, null);
this.addTool(ctool);
ctool.activate();
}
this.getTool('circle').importData(data.circle);
}
},
/**
* @summary Returns the google map instance google.maps.Map
* @memberof ajs.maps.gmapdraw.map.prototype
* @description The google map class instance allows to customize direclty some map properties using the google.maps.Map public interface
* @return {google.maps.Map} The google map instance
* @example
* var mygmap = ajs.maps.gmapdraw.map.gmap();
* mygmap.setCenter(new google.maps.LatLng(45, 7));
*/
gmap: function() {
return _private[this.id].map;
},
/**
* @summary Sets the center of the map
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {Array} center The [lat, lng] coordinates array
* @return void
*/
setCenter: function(center) {
_options[this.id].center = center;
if(_private[this.id].map) {
_private[this.id].map.setCenter(new google.maps.LatLng(center[0], center[1]));
}
},
/**
* @summary Sets the zoom of the map
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {Number} zoom The zoom level
* @return void
*/
setZoom: function(center) {
_options[this.id].zoom = zoom;
if(_private[this.id].map) {
_private[this.id].map.setZoom(zoom);
}
},
/**
* @summary Sets the clear map controller
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {String|Element} ctrl The clear map controller.
* If 'default' the built-in controller is used, if <code>null</code> the clear map
* functionality is removed. If id attribute or an element the clear map functionality is attached to the element.
* @return void
*/
setClearMapCtrl: function(ctrl) {
if(ctrl != _options[this.id].clear_map_ctrl) {
this.removeClearMapController();
}
_options[this.id].clear_map_ctrl = ctrl;
this.setClearMapController();
},
/**
* @summary Sets the export map controller
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {String|Element} ctrl The export map controller.
* If 'default' the built-in controller is used, if <code>null</code> the export map
* functionality is removed. If id attribute or an element the export map functionality is attached to the element.
* @return void
*/
setExportMapCtrl: function(ctrl) {
if(ctrl != _options[this.id].export_map_ctrl) {
this.removeExportMapController();
}
_options[this.id].export_map_ctrl = ctrl;
this.setExportMapController();
},
/**
* @summary Sets the geocoder map field option
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {Boolean} set Whether or not to activate the geocoder functionality
* @return void
*/
setGeocoderMapField: function(set) {
_options[this.id].geocoder_map_field = set;
if(!set) {
this.removeGeocoderMapField();
}
else {
this.setGeocoderMapFieldController()
}
},
/**
* @summary Sets the tips map controller
* @memberof ajs.maps.gmapdraw.map.prototype
* @param {String|Element} ctrl The help tips map controller (shows tips about drawing tools).
* If 'default' the built-in controller is used, if <code>null</code> the tips box is not shown,
* if id attribute or an element the functionality is attached to the element.
* @return void
*/
setTipsMapCtrl: function(ctrl) {
if(ctrl != _options[this.id].tips_map_ctrl) {
this.removeTipsMapController();
}
_options[this.id].tips_map_ctrl = ctrl;
this.setTipsMapController();
}
});
}());
ajs.maps.gmapdraw.tool = (function() {
// protected properties
var _protected_prop = {};
var _options = {};
return new Class({
/**
* @summary Google maps drawing tool class.
* @classdesc <p>This class is the superclass for all gmapdraw tools, extended by all specific tools.</p>
* <p><b>DO NOT INSTANTIATE THIS CLASS DIRECLTY</b>, use its children instead.</p>
* @constructs ajs.maps.gmapdraw.tool
* @param {Number} id The identifier of the child class
* @param {ajs.maps.gmapdraw.map} map The gmapdraw map instance which handles the tool
* @param {String|Element} ctrl The id attribute or the element itself which controls the tool when clicking over it
* @param {String} tool_name The drawing tool name
*
*/
initialize: function(id, map, ctrl, tool_name) {
this.id = id;
_protected_prop[this.id] = {};
_protected_prop[this.id].active = false;
_protected_prop[this.id].map = null;
_protected_prop[this.id].ctrl = null;
_protected_prop[this.id].ctrl_param = null;
_protected_prop[this.id].tool_name = null;
_protected_prop[this.id].items = [];
_protected_prop[this.id].map = map;
_protected_prop[this.id].tool_name = tool_name;
// store the ctrl given, will be used when the tool is activated.
_protected_prop[this.id].ctrl_param = ctrl;
// next click has to begin a new shape?
_protected_prop[this.id].next_shape = false;
// array storing all the drawed items
_protected_prop[this.id].items_array = [];
_options[this.id] = {};
_options[this.id].max_items_allowed = 3;
},
/**
* @summary Returns the class options
* @memberof ajs.maps.gmapdraw.tool.prototype
* @method
* @protected
* @return {Object} The class options object
*/
options: function() {
return _options[this.id];
}.protect(),
/**
* @summary Returns the class protected properties
* @memberof ajs.maps.gmapdraw.tool.prototype
* @method
* @protected
* @return {Object} The class protected properties object
*/
protectedProp: function() {
return _protected_prop[this.id];
}.protect(),
/**
* @summary Sets the tool controller
* @memberof ajs.maps.gmapdraw.tool.prototype
* @method
* @param {String/Element} [ctrl=null] The id attribute or the element itself which serves as the tool controller, if <code>null</code> the default controller is used.
* @protected
* @return void
*/
setController: function(ctrl) {
if(typeOf(ctrl) === 'string' || typeOf(ctrl) === 'element') {
if(typeOf(ctrl) === 'string') _protected_prop[this.id].ctrl = document.id(ctrl);
if(typeOf(_protected_prop[this.id].ctrl != 'element')) {
throw new Error('the given ctrl for the ' + _protected_prop[this.id].tool_name + 'tool is not a DOM element')
}
}
// default
else {
_protected_prop[this.id].ctrl = new Element('div', {id: 'gmapdraw_' + _protected_prop[this.id].tool_name + '_tool'}).set('text', _protected_prop[this.id].tool_name);
_protected_prop[this.id].map.addDefaultCtrl(_protected_prop[this.id].ctrl);
}
}.protect(),
/**
* @summary Removes the default tool controller
* @memberof ajs.maps.gmapdraw.tool.prototype
* @method
* @protected
* @return void
*/
removeController: function() {
_protected_prop[this.id].ctrl.dispose();
_protected_prop[this.id].ctrl = null;
}.protect(),
/**
* @summary Activates the tool
* @memberof ajs.maps.gmapdraw.tool.prototype
* @return void
*/
activate: function() {
_protected_prop[this.id].active = true;
this.setController(_protected_prop[this.id].ctrl_param);
_protected_prop[this.id].ctrl.addEvent('click', this.setDrawing.bind(this));
_protected_prop[this.id].ctrl.removeClass('inactive');
_protected_prop[this.id].ctrl.addClass('active');
if(ajs.maps.gmapdraw.debug) console.log(_protected_prop[this.id].tool_name + ' tool activated')
},
/**
* @summary Removes the tool
* @memberof ajs.maps.gmapdraw.tool.prototype
* @param {Boolean} [remove_ctrl=false] Whether or not to remove the tool control if the default one
* @return void
*/
deactivate: function(remove_ctrl) {
if(!remove_ctrl) remove_ctrl = false;
if(_protected_prop[this.id].active === true) {
_protected_prop[this.id].active = false;
_protected_prop[this.id].ctrl.removeClass('active');
_protected_prop[this.id].ctrl.addClass('inactive');
_protected_prop[this.id].ctrl.removeEvent('click', this.setDrawing);
if(_protected_prop[this.id].map.getDrawingTool() === this) {
_protected_prop[this.id].map.setDrawingTool(null);
}
if(remove_ctrl && _protected_prop[this.id].ctrl_param == null) {
this.removeController();
}
if(ajs.maps.gmapdraw.debug) console.log(_protected_prop[this.id].tool_name + ' tool deactivated')
}
else {
if(remove_ctrl && _protected_prop[this.id].ctrl_param == null) {
this.removeController();
}
if(ajs.maps.gmapdraw.debug) console.log(_protected_prop[this.id].tool_name + ' tool already deactivated')
}
},
/**
* @summary Sets the current drawing tool
* @memberof ajs.maps.gmapdraw.tool.prototype
* @return void
*/
setDrawing: function() {
if(ajs.maps.gmapdraw.debug) console.log('drawing tool: ' + _protected_prop[this.id].tool_name);
this.prepareTool();
_protected_prop[this.id].map.setDrawingTool(this);
},
/**
* @summary Prepares the current drawing tool
* @memberof ajs.maps.gmapdraw.tool.prototype
* @return void
*/
prepareTool: function() {
_protected_prop[this.id].map.updateTips(this.tipsText());
},
/**
* @summary Adds an item to the items
* @memberof ajs.maps.gmapdraw.tool.prototype
* @param {Object} item a google map shape
* @return void
*/
addItem: function(item) {
_protected_prop[this.id].items.push(item);
},
/**
* @summary Sets the maximum number of items that the tool may draw
* @memberof ajs.maps.gmapdraw.tool.prototype
* @param max The maximum number of drawable items
* @return void
*/
setMaxItemsAllowed: function(max) {
_options[this.id].max_items_allowed = max.toInt();
},
/**
* @summary Sets the value of the next shape property (a new click starts a new shape if true)
* @memberof ajs.maps.gmapdraw.tool.prototype
* @param next_shape Whether or not next click has to start a new shape
* @return void
*/
setNextShape: function(next_shape) {
_protected_prop[this.id].next_shape = !!next_shape;
},
/**
* @summary Returns the tool name
* @memberof ajs.maps.gmapdraw.tool.prototype
* @return {String} The tool name
*/
getToolName: function() {
return _protected_prop[this.id].tool_name;
}
});
}());
ajs.maps.gmapdraw.pointTool = (function() {
// protected properties
var _protected_prop = {};
// class options
var _specific_options = {};
var _options = {};
return new Class({
Extends: ajs.maps.gmapdraw.tool,
/**
* @summary Google maps drawing point tool class. Provides methods to draw over the {@link ajs.maps.gmapdraw.map} instance
* @classdesc <p>The point drawing tool class, which allows to draw points over the gmapdraw map instance.</p>
* @constructs ajs.maps.gmapdraw.pointTool
* @extends ajs.maps.gmapdraw.tool
* @param {ajs.maps.gmapdraw.map} map The gmapdraw map instance which handles the tool
* @param {String|Element} ctrl The id attribute or the element itself which controls the tool when clicking over it
* @param {Object} options A class options object
* @param {Number} [options.max_items_allowed=3] The maximum number of shapes the tool may draw.
*
*/
initialize: function(map, ctrl, options) {
this.id = ajs.maps.gmapdraw.instances_ids++;
this.parent(this.id, map, ctrl, 'point'); // also sets options
_specific_options[this.id] = {};
_options[this.id] = {};
// merge given options with deafult ones
_options[this.id] = Object.merge(this.options(), _specific_options[this.id], options);
// constructs protected properties
_protected_prop[this.id] = this.protectedProp();
},
/**
* @summary Returns the tool help tip text
* @memberof ajs.maps.gmapdraw.pointTool.prototype
* @return {String} The tips text
*/
tipsText: function() {
return 'Click on the map to set draggable markers points. Right click on a marker to delete it';
},
/**
* @summary Handles the click event over the map when the tool is the drawing one
* @memberof ajs.maps.gmapdraw.pointTool.prototype
* @return void
*/
clickHandler: function(evt) {
// maximum number of points reached
if(!(_protected_prop[this.id].items.length < _options[this.id].max_items_allowed)) {
if(ajs.maps.gmapdraw.debug) console.log('maximum number of points drawed');
alert('Maximum number of insertable points reached');
return null;
}
var marker = new google.maps.Marker({
position: evt.latLng,
draggable: true,
map: _protected_prop[this.id].map.gmap()
});
_protected_prop[this.id].items.push(marker);
if(ajs.maps.gmapdraw.debug) {
google.maps.event.addListener(marker, 'dragend', this.updateInfo.bind(this));
}
google.maps.event.addListener(marker, 'rightclick', function() {
marker.setMap(null);
_protected_prop[this.id].items.erase(marker);
if(ajs.maps.gmapdraw.debug) this.updateInfo();
}.bind(this));
if(ajs.maps.gmapdraw.debug) {
console.log('point drawed');
this.updateInfo();
}
},
/**
* @summary Displays information about rawed points in the console
* @memberof ajs.maps.gmapdraw.pointTool.prototype
* @return void
*/
updateInfo: function() {
var info = '';
_protected_prop[this.id].items.each(function(point, index) {
info += 'point #' + (index+1) + ' (lat, lng): (' + point.getPosition().lat() + ', ' + point.getPosition().lng() + ')\n';
});
console.log(info);
console.log('updated points info');
},
/**
* @summary Clears all drawed points
* @memberof ajs.maps.gmapdraw.pointTool.prototype
* @return void
*/
clear: function() {
_protected_prop[this.id].items.each(function(marker) {
marker.setMap(null);
});
_protected_prop[this.id].items = [];
if(ajs.maps.gmapdraw.debug) {
console.log('points cleared');
}
},
/**
* @summary Returns all the drawed points data
* @memberof ajs.maps.gmapdraw.pointTool.prototype
* @return {Array} data An array of objects representing the drawed points coordinates
* @example
* [{lat: 45, lng: 7}, {lat: 33, lng: 15}, {lat: 42, lng: 5}]
*/
exportData: function() {
var data = [];
_protected_prop[this.id].items.each(function(marker) {
var dobj = {lat: marker.getPosition().lat(), lng: marker.getPosition().lng()};
data.push(dobj);
});
return data;
},
/**
* @summary Imports the data as points
* @memberof ajs.maps.gmapdraw.pointTool.prototype
* @param {Array} data An array of objects representing the points coordinates
* @example
* [{lat: 45, lng: 7}, {lat: 33, lng: 15}, {lat: 42, lng: 5}]
*/
importData: function(data) {
for(var i = 0; i < data.length; i++) {
var point = data[i];
this.clickHandler({latLng: new google.maps.LatLng(point.lat, point.lng)});
}
}
});
}());
ajs.maps.gmapdraw.polylineTool = (function() {
// protected properties
var _protected_prop = {};
// class options
var _specific_options = {};
var _options = {};
return new Class({
Extends: ajs.maps.gmapdraw.tool,
/**
* @summary Google maps drawing polyline tool class. Provides methods to draw over the {@link ajs.maps.gmapdraw.map} instance
* @classdesc <p>The polyline drawing tool class, which allows to draw polylines over the gmapdraw map instance.</p>
* @constructs ajs.maps.gmapdraw.polylineTool
* @extends ajs.maps.gmapdraw.tool
* @param {ajs.maps.gmapdraw.map} map The gmapdraw map instance which handles the tool
* @param {String|Element} ctrl The id attribute or the element itself which controls the tool when clicking over it
* @param {Object} options A class options object
* @param {Number} [options.max_items_allowed=3] The maximum number of shapes the tool may draw.
*
*/
initialize: function(map, ctrl, options) {
this.id = ajs.maps.gmapdraw.instances_ids++;
this.parent(this.id, map, ctrl, 'polyline'); // also sets options
_specific_options[this.id] = {};
_protected_prop[this.id] = {
next_shape: false,
active_polyline_index: null
};
// merge given options with deafult ones
_options[this.id] = Object.merge(this.options(), _specific_options[this.id], options);
// constructs protected properties
_protected_prop[this.id] = Object.merge(this.protectedProp(), _protected_prop[this.id]);
},
/**
* @summary Returns the tool help tip text
* @memberof ajs.maps.gmapdraw.polylineTool.prototype
* @return {String} The tips text
*/
tipsText: function() {
return 'Click on the map to add polyline points, click the menu voice again to create a new polyline. Right click on existing polylines to delete them';
},
/**
* @summary Prepares the tool
* @memberof ajs.maps.gmapdraw.polylineTool.prototype
* @return void
*/
prepareTool: function() {
this.parent();
_protected_prop[this.id].next_shape = true;
},
/**
* @summary Handles the click event over the map when the tool is the drawing one
* @memberof ajs.maps.gmapdraw.polylineTool.prototype
* @return void
*/
clickHandler: function(evt) {
// if next shape && maximum shape number is not reached
if(_protected_prop[this.id].next_shape && _protected_prop[this.id].items.length < _options[this.id].max_items_allowed) {
var polylinePath = new google.maps.MVCArray([evt.latLng]); // store the point of the polyline
var polyline = new google.maps.Polyline({
editable: true,
path: polylinePath,
map: _protected_prop[this.id].map.gmap()
});
var polyline_item = {path: polylinePath, shape: polyline};
_protected_prop[this.id].items.push(polyline_item);
_protected_prop[this.id].active_polyline_index = _protected_prop[this.id].items.indexOf(polyline_item);
// right click to delete one
google.maps.event.addListener(polyline, 'rightclick', function() {
polyline.setMap(null);
_protected_prop[this.id].items.erase(polyline_item);
_protected_prop[this.id].active_polyline_index--; // one item has been removed, indexes shift down
_protected_prop[this.id].next_shape = true; // otherwise next click will populate the last polyline
if(ajs.maps.gmapdraw.debug) this.updateInfo();
}.bind(this));
if(ajs.maps.gmapdraw.debug) {
google.maps.event.addListener(polylinePath, 'insert_at', this.updateInfo.bind(this));
google.maps.event.addListener(polylinePath, 'remove_at', this.updateInfo.bind(this));
google.maps.event.addListener(polylinePath, 'set_at', this.updateInfo.bind(this));
console.log('polyline point added');
this.updateInfo();
}
_protected_prop[this.id].next_shape = false;
}
// maximum number exceeded
else if(_protected_prop[this.id].next_shape) {
if(ajs.maps.gmapdraw.debug) console.log('maximum number of polylines drawed');
alert('Maximum number of insertable polylines reached');
return null;
}
// add a point to the current polyline
else {
_protected_prop[this.id].items[_protected_prop[this.id].active_polyline_index].path.push(evt.latLng);
}
},
/**
* @summary Displays information about rawed points in the console
* @memberof ajs.maps.gmapdraw.polylineTool.prototype
* @return void
*/
updateInfo: function() {
var info = '';
_protected_prop[this.id].items.each(function(polyline, index) {
info += 'Polyline #' + (index + 1) + '\n';
polyline.path.forEach(function(point, index) {
info += '\tpoint #' + (index + 1) + ' (lat, lng): (' + point.lat() + ', ' + point.lng() + ')\n';
});
}.bind(this));
console.log(info);
console.log('updated polyline info');
},
/**
* @summary Clears all drawed points
* @memberof ajs.maps.gmapdraw.polylineTool.prototype
* @return void
*/
clear: function() {
_protected_prop[this.id].items.each(function(polyline) {
polyline.shape.setMap(null);
});
_protected_prop[this.id].items = [];
if(ajs.maps.gmapdraw.debug) {
console.log('polylines cleared');
}
},
/**
* @summary Returns all the drawed points data
* @memberof ajs.maps.gmapdraw.polylineTool.prototype
* @return {Array} data An array of arrays of objects representing the polylines' points coordinates
* @example
* // two polylines, the first with 2 points, the second with 3 points.
* [[{lat: 45, lng:7}, {lat:46, lng:7}], [{lat: 42, lng: 11}, {lat: 41, lng: 10.8}, {lat: 44, lng: 8}]]
*/
exportData: function() {
var data = [];
_protected_prop[this.id].items.each(function(polyline) {
var darr = [];
polyline.path.forEach(function(point, index) {
var dobj = {lat: point.lat(), lng: point.lng()};
darr.push(dobj);
});
data.push(darr);
});
return data;
},
/**
* @summary Imports the data as polylines
* @memberof ajs.maps.gmapdraw.polylineTool.prototype
* @param {Array} data An array of arrays of objects representing the polylines' points coordinates
* @example
* // two polylines, the first with 2 points, the second with 3 points.
* [[{lat: 45, lng:7}, {lat:46, lng:7}], [{lat: 42, lng: 11}, {lat: 41, lng: 10.8}, {lat: 44, lng: 8}]]
*/
importData: function(data) {
for(var i = 0; i < data.length; i++) {
var polyline = data[i];
this.prepareTool();
for(var ii = 0; ii < polyline.length; ii++) {
var point = polyline[ii];
this.clickHandler({latLng: new google.maps.LatLng(point.lat, point.lng)});
}
}
}
});
}());
ajs.maps.gmapdraw.polygonTool = (function() {
// protected properties
var _protected_prop = {};
// class options
var _specific_options = {};
var _options = {};
return new Class({
Extends: ajs.maps.gmapdraw.tool,
/**
* @summary Google maps drawing polygon tool class. Provides methods to draw over the {@link ajs.maps.gmapdraw.map} instance
* @classdesc <p>The polygon drawing tool class, which allows to draw polygons over the gmapdraw map instance.</p>
* @constructs ajs.maps.gmapdraw.polygonTool
* @extends ajs.maps.gmapdraw.tool
* @param {ajs.maps.gmapdraw.map} map The gmapdraw map instance which handles the tool
* @param {String|Element} ctrl The id attribute or the element itself which controls the tool when clicking over it
* @param {Object} options A class options object
* @param {Number} [options.max_items_allowed=3] The maximum number of shapes the tool may draw.
*
*/
initialize: function(map, ctrl, options) {
this.id = ajs.maps.gmapdraw.instances_ids++;
this.parent(this.id, map, ctrl, 'polygon'); // also sets options
_specific_options[this.id] = {};
_protected_prop[this.id] = {
next_shape: false,
active_polygon_index: null
};
// merge given options with deafult ones
_options[this.id] = Object.merge(this.options(), _specific_options[this.id], options);
// constructs protected properties
_protected_prop[this.id] = Object.merge(this.protectedProp(), _protected_prop[this.id]);
},
/**
* @summary Returns the tool help tip text
* @memberof ajs.maps.gmapdraw.polygonTool.prototype
* @return {String} The tips text
*/
tipsText: function() {
return 'Click on the map to add polygon\'s vertices, click the menu voice again to create a new shape. Right click on existing polygons to delete them';
},
/**
* @summary Prepares the tool
* @memberof ajs.maps.gmapdraw.polygonTool.prototype
* @return void
*/
prepareTool: function() {
this.parent();
_protected_prop[this.id].next_shape = true;
},
/**
* @summary Handles the click event over the map when the tool is the drawing one
* @memberof ajs.maps.gmapdraw.polygonTool.prototype
* @return void
*/
clickHandler: function(evt) {
// if next shape && maximum shape number is not reached
if(_protected_prop[this.id].next_shape && _protected_prop[this.id].items.length < _options[this.id].max_items_allowed) {
var polygonPath = new google.maps.MVCArray([evt.latLng]); // store the point of the polyline
var polygon = new google.maps.Polygon({
editable: true,
path: polygonPath,
map: _protected_prop[this.id].map.gmap()
});
var polygon_item = {path: polygonPath, shape: polygon};
_protected_prop[this.id].items.push(polygon_item);
_protected_prop[this.id].active_polygon_index = _protected_prop[this.id].items.indexOf(polygon_item);
// right click to delete one
google.maps.event.addListener(polygon, 'rightclick', function() {
polygon.setMap(null);
_protected_prop[this.id].items.erase(polygon_item);
_protected_prop[this.id].active_polygon_index--; // one item has been removed, indexes shift down
_protected_prop[this.id].next_shape = true; // otherwise next click will populate the last polyline
if(ajs.maps.gmapdraw.debug) this.updateInfo();
}.bind(this));
if(ajs.maps.gmapdraw.debug) {
google.maps.event.addListener(polygonPath, 'insert_at', this.updateInfo.bind(this));
google.maps.event.addListener(polygonPath, 'remove_at', this.updateInfo.bind(this));
google.maps.event.addListener(polygonPath, 'set_at', this.updateInfo.bind(this));
console.log('polygon vertex added');
this.updateInfo();
}
_protected_prop[this.id].next_shape = false;
}
// maximum number exceeded
else if(_protected_prop[this.id].next_shape) {
if(ajs.maps.gmapdraw.debug) console.log('maximum number of polygons drawed');
alert('Maximum number of insertable polygons reached');
return null;
}
// add a point to the current polyline
else {
_protected_prop[this.id].items[_protected_prop[this.id].active_polygon_index].path.push(evt.latLng);
}
},
/**
* @summary Displays information about rawed points in the console
* @memberof ajs.maps.gmapdraw.polygonTool.prototype
* @return void
*/
updateInfo: function() {
var info = '';
_protected_prop[this.id].items.each(function(polygon, index) {
info += 'Polygon #' + (index + 1) + '\n';
polygon.path.forEach(function(point, index) {
info += '\tpoint #' + (index + 1) + ' (lat, lng): (' + point.lat() + ', ' + point.lng() + ')\n';
});
}.bind(this));
console.log(info);
console.log('updated polygon info');
},
/**
* @summary Clears all drawed points
* @memberof ajs.maps.gmapdraw.polygonTool.prototype
* @return void
*/
clear: function() {
_protected_prop[this.id].items.each(function(polygon) {
polygon.shape.setMap(null);
});
_protected_prop[this.id].items = [];
if(ajs.maps.gmapdraw.debug) {
console.log('polygons cleared');
}
},
/**
* @summary Returns all the drawed points data
* @memberof ajs.maps.gmapdraw.polygonTool.prototype
* @return {Array} data An array of arrays of objects representing the polygons' vertex coordinates
* @example
* // two polygons, the first with 3 vertexes, the second with 4 vertexes.
* [[{lat: 45, lng:7}, {lat:46, lng:7}, {lat: 42, lng: 11}], [{lat: 42, lng: 11}, {lat: 41, lng: 10.8}, {lat: 44, lng: 8}, {lat: 33, lng: 12}]]
*/
exportData: function() {
var data = [];
_protected_prop[this.id].items.each(function(polygon) {
var darr = [];
polygon.path.forEach(function(point, index) {
var dobj = {lat: point.lat(), lng: point.lng()};
darr.push(dobj);
});
data.push(darr);
});
return data;
},
/**
* @summary Imports the data as polygons
* @memberof ajs.maps.gmapdraw.polygonTool.prototype
* @param {Array} data An array of arrays of objects representing the polygons' vertex coordinates
* @example
* // two polygons, the first with 3 vertexes, the second with 4 vertexes.
* [[{lat: 45, lng:7}, {lat:46, lng:7}, {lat: 42, lng: 11}], [{lat: 42, lng: 11}, {lat: 41, lng: 10.8}, {lat: 44, lng: 8}, {lat: 33, lng: 12}]]
*/
importData: function(data) {
for(var i = 0; i < data.length; i++) {
var polygon = data[i];
this.prepareTool();
for(var ii = 0; ii < polygon.length; ii++) { var point = polygon[ii];
this.clickHandler({latLng: new google.maps.LatLng(point.lat, point.lng)});
}
}
}
});
}());
ajs.maps.gmapdraw.circleTool = (function() {
// protected properties
var _protected_prop = {};
// class options
var _specific_options = {};
var _options = {};
return new Class({
Extends: ajs.maps.gmapdraw.tool,
/**
* @summary Google maps drawing circle tool class. Provides methods to draw over the {@link ajs.maps.gmapdraw.map} instance
* @classdesc <p>The circle drawing tool class, which allows to draw circles over the gmapdraw map instance.</p>
* @constructs ajs.maps.gmapdraw.circleTool
* @extends ajs.maps.gmapdraw.tool
* @param {ajs.maps.gmapdraw.map} map The gmapdraw map instance which handles the tool
* @param {String|Element} ctrl The id attribute or the element itself which controls the tool when clicking over it
* @param {Object} options A class options object
* @param {Number} [options.max_items_allowed=3] The maximum number of shapes the tool may draw.
*
*/
initialize: function(map, ctrl, options) {
this.id = ajs.maps.gmapdraw.instances_ids++;
this.parent(this.id, map, ctrl, 'circle'); // also sets options
_specific_options[this.id] = {};
_protected_prop[this.id] = {
next_shape: false,
circle_drawing: null,
map_move_listener: null,
circle_move_listener: null
};
// merge given options with deafult ones
_options[this.id] = Object.merge(this.options(), _specific_options[this.id], options);
// constructs protected properties
_protected_prop[this.id] = Object.merge(this.protectedProp(), _protected_prop[this.id]);
},
/**
* @summary Returns the tool help tip text
* @memberof ajs.maps.gmapdraw.circleTool.prototype
* @return {String} The tips text
*/
tipsText: function() {
return 'Click on the map to add circles. Right click on existing circles to delete them';
},
/**
* @summary Prepares the tool
* @memberof ajs.maps.gmapdraw.circleTool.prototype
* @return void
*/
prepareTool: function() {
this.parent();
_protected_prop[this.id].next_shape = true;
},
/**
* @summary Handles the click event over the map when the tool is the drawing one
* @memberof ajs.maps.gmapdraw.circleTool.prototype
* @return void
*/
clickHandler: function(evt) {
// if next shape && maximum shape number is not reached
if(!_protected_prop[this.id].circle_drawing && _protected_prop[this.id].items.length < _options[this.id].max_items_allowed) {
var circle = new google.maps.Circle({
center: evt.latLng,
map: _protected_prop[this.id].map.gmap(),
radius: 1,
editable: true
});
_protected_prop[this.id].circle_drawing = true;
// enlarge
_protected_prop[this.id].map_move_listener = google.maps.event.addListener(_protected_prop[this.id].map.gmap(), 'mousemove', function(evt2) {
circle.setRadius(this.distance(evt.latLng, evt2.latLng));
}.bind(this));
// reduce
_protected_prop[this.id].circle_move_listener = google.maps.event.addListener(circle, 'mousemove', function(evt2) {
circle.setRadius(this.distance(evt.latLng, evt2.latLng));
}.bind(this));
// end
google.maps.event.addListenerOnce(circle, 'click', function(evt2) {
google.maps.event.removeListener(_protected_prop[this.id].map_move_listener);
google.maps.event.removeListener(_protected_prop[this.id].circle_move_listener);
_protected_prop[this.id].circle_drawing = false;
if(ajs.maps.gmapdraw.debug) this.updateInfo();
}.bind(this));
_protected_prop[this.id].items.push(circle);
// right click to delete one
google.maps.event.addListener(circle, 'rightclick', function() {
circle.setMap(null);
_protected_prop[this.id].items.erase(circle);
_protected_prop[this.id].next_shape = true; // otherwise next click will populate the last polyline
if(ajs.maps.gmapdraw.debug) this.updateInfo();
}.bind(this));
}
else if(!_protected_prop[this.id].circle_drawing && _protected_prop[this.id].items.length >= _options[this.id].max_items_allowed) {
if(ajs.maps.gmapdraw.debug) console.log('maximum number of circles drawed');
alert('Maximum number of insertable circles reached');
return null;
}
// currently drawing
else {
google.maps.event.removeListener(_protected_prop[this.id].map_move_listener);
google.maps.event.removeListener(_protected_prop[this.id].circle_move_listener);
_protected_prop[this.id].circle_drawing = false;
if(ajs.maps.gmapdraw.debug) this.updateInfo();
}
},
/**
* @summary Displays information about rawed points in the console
* @memberof ajs.maps.gmapdraw.circleTool.prototype
* @return void
*/
updateInfo: function() {
var info = '';
_protected_prop[this.id].items.each(function(circle, index) {
info += 'Circle #' + (index + 1) + '\n';
info += '\tcenter (lat, lng): (' + circle.getCenter().lat() + ', ' + circle.getCenter().lng() + ')\n';
info += '\tradius: ' + circle.getRadius() + '\n';
}.bind(this));
console.log(info);
console.log('updated circle info');
},
/**
* @summary Clears all drawed points
* @memberof ajs.maps.gmapdraw.circleTool.prototype
* @return void
*/
clear: function() {
_protected_prop[this.id].items.each(function(circle) {
circle.setMap(null);
});
_protected_prop[this.id].items = [];
if(ajs.maps.gmapdraw.debug) {
console.log('circles cleared');
}
},
/**
* @summary Returns the distance between 2 google.maps.LatLng points
* @memberof ajs.maps.gmapdraw.circleTool.prototype
* @param {google.maps.LatLng} point1 The first point
* @param {google.maps.LatLng} point2 The second point
* @return {Number} The distance in meters
*/
distance: function(point1, point2) {
var R = 6371000; // earth's radius in meters
var d_lat = (point2.lat() - point1.lat()) * Math.PI / 180;
var d_lon = (point2.lng() - point1.lng()) * Math.PI / 180;
var a = Math.sin(d_lat/2) * Math.sin(d_lat/2) +
Math.cos(point1.lat() * Math.PI / 180 ) * Math.cos(point2.lat() * Math.PI / 180 ) *
Math.sin(d_lon/2) * Math.sin(d_lon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d;
},
/**
* @summary Returns all the drawed points data
* @memberof ajs.maps.gmapdraw.circleTool.prototype
* @return {Array} data An array objects representing the circle's properties
* @example
* [{lat: 45, lng: 7, radius: 40000}, {lat: 35, lng: 15, radius: 650000}]
*/
exportData: function() {
var data = [];
_protected_prop[this.id].items.each(function(circle, index) {
var dobj = {lat: circle.getCenter().lat(), lng: circle.getCenter().lng(), radius: circle.getRadius()};
data.push(dobj);
}.bind(this));
return data;
},
/**
* @summary Imports all data as circles
* @memberof ajs.maps.gmapdraw.circleTool.prototype
* @param {Array} data An array objects representing the circle's properties
* @example
* [{lat: 45, lng: 7, radius: 40000}, {lat: 35, lng: 15, radius: 650000}]
*/
importData: function(data) {
for(var i = 0; i < data.length; i++) {
var circle = data[i];
var dcircle = new google.maps.Circle({
center: new google.maps.LatLng(circle.lat, circle.lng),
map: _protected_prop[this.id].map.gmap(),
radius: circle.radius,
editable: true
});
this.addItem(dcircle);
}
}
});
}());