/*
* Copyright 2015 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @private
* The google maps panel, supporting address lookup as well as marker and shapes edition
*/
Ext.define('Ametys.plugins.maps.GMapPanel', {
extend: 'Ext.ux.GMapPanel',
statics :{
/**
* @private
* @readonly
* @property {Number} ADD_MARKER_MODE The mode when adding a marker
*/
ADD_MARKER_MODE: 0,
/**
* @private
* @readonly
* @property {Number} MOVE_MAP_MODE The mode when moving the map
*/
MOVE_MAP_MODE: 1,
/**
* @private
* @readonly
* @property {Number} ADD_SHAPE_MAP_MODE The mode when adding a shape
*/
ADD_SHAPE_MAP_MODE: 2,
/**
* @private
* @readonly
* @property {String} The default color code for the shapes
*/
DEFAULT_SHAPE_COLOR: 'FF0000',
/**
* @private
* @readonly
* @property {Object} LOOKUP_ERRORS The array of error code/error message mappings
*/
LOOKUP_ERRORS:
[
{
code: 'UNKNOWN_ERROR',
// msg: 'A geocoding or directions request could not be successfully processed, yet the exact reason for the failure is not known.'
msg : "{{i18n PLUGINS_MAPS_GMAP_UNKNOWN_ERROR}}"
},
{
code: 'ERROR',
// msg: 'There was a problem contacting the Google servers.'
msg: "{{i18n PLUGINS_MAPS_GMAP_ERROR}}"
},
{
code: 'ZERO_RESULTS',
//msg: 'The request did not encounter any errors but returns zero results.'
msg: "{{i18n PLUGINS_MAPS_GMAP_ZERO_RESULTS}}"
},
{
code: 'INVALID_REQUEST',
//msg: 'This request was invalid.'
msg: "{{i18n PLUGINS_MAPS_GMAP_ERROR}}"
},
{
code: 'REQUEST_DENIED',
//msg: 'The webpage is not allowed to use the geocoder for some reason.'
msg: "{{i18n PLUGINS_MAPS_GMAP_ERROR}}"
},
{
code: 'OVER_QUERY_LIMIT',
//msg: 'The webpage has gone over the requests limit in too short a period of time.'
msg: "{{i18n PLUGINS_MAPS_GMAP_ERROR}}"
}
],
/**
* @private
* @readonly
* @property {Object} LOCATION_TYPES The array of accuracy code/accuracy level mappings
*/
LOCATION_TYPES:
[
{
level: 1,
code: 'APPROXIMATE'
},
{
level: 2,
code: 'GEOMETRIC_CENTER'
},
{
level: 3,
code: 'RANGE_INTERPOLATED'
},
{
level: 4,
code: 'ROOFTOP'
}
]
},
/**
* @cfg {Number} zoomLevel the zoom level of the map
*/
/**
* @cfg {google.maps.LatLng} center the center object of the map
*/
/**
* @cfg {String} mapTypeId the id of the map type
*/
/**
* @cfg {Ext.data.Store} store the store of the map panel
*/
/**
* @cfg {Object} mapOpts the selected map options when instantiating the google map
*/
/* ---------------------------------------------------------------------------------------------- */
/**
* @private
* @property {Ext.data.Record} _selectedRecord the selected record
*/
/**
* @private
* @property {Array} _shapeMarkers the array of google.maps.Marker
*/
/**
* @private
* @property {google.maps.Polyline} _drawingShape the polyline holding the shape points
*/
/**
* @private
* @property {google.maps.Polyline} _nextShapePath the polyline used to visualize the next path of the drawing shape
*/
/**
* @private
* @property {Ametys.plugins.maps.EditShapeDialog} _shapePropertiesWindow the window allowing to edit the properties of a shape
*/
/**
* @private
* @property {google.maps.MarkerImage} _vertexNormal the default marker image
*/
/**
* @private
* @property {google.maps.Map} _gmap the google map
*/
/**
* @private
* @property {google.maps.Geocoder} _geocoder the geocoder
*/
/**
* @private
* @property {google.maps.LatLng} _lastCenter the last center of the map
*/
/**
* @private
* @property {Number} _currentMode the current mode (move, edit marker, or edit shape)
*/
/**
* @private
* @property {Ext.XTemplate} _infoWindowTemplate the template used for the information window of the markers/shapes
*/
_infoWindowTemplate: new Ext.XTemplate (
"<tpl for='.'>",
'<div class="infowindow">',
'<span class="title">{title}</span>',
'<p class="description">{description}</p>',
'</div>',
'</tpl>',
{
// XTemplate configuration:
compiled: true,
disableFormats: true
}
),
initComponent : function()
{
Ext.applyIf(this,{
markers: [],
cache: {
marker: [],
polyline: [],
polygon: [],
infowindow: []
}
});
this.callParent(arguments);
// bind listeners
if (this.store)
{
this.store.on('add', Ext.bind(this._recordAddedHandler, this));
this.store.on('remove', Ext.bind(this._recordRemovedHandler, this));
this.store.on('update', Ext.bind(this._recordUpdatedHandler, this));
}
},
afterRender: function()
{
var wh = this.ownerCt.getSize();
Ext.applyIf(this, wh);
this.callParent(arguments);
// Not loading properly otherwise...
Ext.defer(this._initMap, 200, this);
},
constructor: function(config)
{
this._shapeMarkers = [];
var me = this;
config = Ext.apply({
gmapType: 'map',
border: true,
mapOpts: {
mapTypeControl: true,
zoomControl: true,
disableDoubleClickZoom: true,
streetViewControl: false
},
dockedItems: [
{
dock: 'top',
xtype: 'toolbar',
items: [
{
itemId: 'handButton',
xtype: 'button',
height: 26,
width: 26,
iconCls: 'ametysicon-hand-drag',
toggleGroup: 'actionGroup',
width: 'auto',
enableToggle: true,
scope: this,
toggleHandler: function(btn, state)
{
if (state)
{
this._moveMap();
}
},
pressed: false
},
{
itemId : 'markerButton',
xtype: 'button',
tooltip: "{{i18n PLUGINS_MAPS_SERVICE_CONFIGURATION_POI_BUTTON}}",
height: 26,
width: 26,
iconCls: 'ametysicon-placeholder19',
toggleGroup: 'actionGroup',
width: 'auto',
enableToggle: true,
scope: this,
toggleHandler: function(btn, state)
{
if (state)
{
this._placeMarker();
}
},
pressed: false
},
{
itemId : 'shapeButton',
xtype: 'button',
iconCls: 'ametysicon-shape',
height: 26,
width: 26,
tooltip: "{{i18n PLUGINS_MAPS_SERVICE_CONFIGURATION_SHAPE_BUTTON}}",
toggleGroup: 'actionGroup',
width: 'auto',
enableToggle: true,
scope: this,
toggleHandler: function(btn, state)
{
if (state)
{
this._startShape();
}
},
pressed: false
},
{
itemId: 'centerAddressField',
xtype: 'textfield',
height: 26,
flex: 1,
emptyText: "{{i18n PLUGINS_MAPS_SERVICE_CONFIGURATION_CENTER_ON_ADDRESS_PLACEHOLDER}}",
enableKeyEvents: true,
listeners: {
keyup: function (field, event)
{
if (event.isSpecialKey() && (event.getCharCode() == event.RETURN || event.getCharCode() == event.ENTER))
{
event.stopEvent();
var btn = me.down('#centerButton');
btn.handler.call(btn.scope, btn, null);
}
},
specialkey: function(field, event)
{
if (event.getKey() == event.ENTER)
{
event.preventDefault();
event.stopPropagation();
}
}
}
},
{
iconCls: 'ametysicon-magnifier12',
height: 26,
tooltip: "{{i18n PLUGINS_MAPS_SERVICE_CONFIGURATION_CENTER_ON_ADDRESS_BTN}}",
itemId:'centerButton',
xtype: 'button',
scope: this,
handler: function(btn)
{
var addressField = me.down('#centerAddressField');
if (!Ext.isEmpty(addressField.getValue()))
{
this._geoCodeLookup(addressField.getValue(), null, false, true, null, true);
}
}
}
]
}
]
}
, config);
this._currentMode = Ametys.plugins.maps.GMapPanel.MOVE_MAP_MODE;
this.store = Ext.StoreMgr.lookup(this.store);
this.callParent(arguments);
},
/**
* Get the google map
* @return {google.maps.Map} the google map
*/
getMap: function()
{
return this._gmap;
},
/**
* Get the zoom level of the map
* @return {Number} the zoom level of the map
*/
getZoomLevel: function()
{
return this.getMap().getZoom();
},
/**
* Get the id of the map type
* @return {String} the id of the map type
*/
getMapTypeId: function()
{
return this.getMap().getMapTypeId();
},
/**
* Get the last center of the map
* @return {google.maps.LatLng} _lastCenter the last center of the map
*/
getLastCenter: function()
{
return this._lastCenter;
},
/**
* @private
* Initialize the map
*/
_initMap: function()
{
var mapOptions =
{
zoom: this.zoomLevel,
mapTypeId: this.mapTypeId
};
if (Ext.isObject(this.mapOpts))
{
Ext.applyIf(mapOptions, this.mapOpts);
}
this._gmap = new google.maps.Map(this.body.dom, mapOptions);
google.maps.event.addListenerOnce(this._gmap, 'tilesloaded', Ext.bind(this._onMapReady, this));
if (Ext.isObject(this.center))
{
var point = new google.maps.LatLng(this.center.lat, this.center.lng);
this._gmap.setCenter(point, this.zoomLevel);
this._lastCenter = point;
}
else
{
// no center defined => center on Greenwich
var point = new google.maps.LatLng(0.0, 0.0);
this.zoomLevel = 1;
this._gmap.setCenter(point, this.zoomLevel);
this._lastCenter = point;
}
},
/**
* @private
* Function invoked when the map is ready
* @param {google.maps.Map} map the google map
*/
_onMapReady: function (map)
{
this.down('#handButton').toggle();
this._initializeMarkerImages();
if (this.store)
{
this.store.each( function(record)
{
if (record.get('gtype') == "marker")
{
this._addMarkerForRecord(record);
}
else if (record.get('gtype') == "polygon")
{
this._addShapeForRecord(record);
}
}, this);
}
},
/**
* @private
* Initialize the marker images
*/
_initializeMarkerImages: function()
{
this._vertexNormal = new google.maps.MarkerImage(
Ametys.getPluginResourcesPrefix('maps') + '/img/vertex.png',
new google.maps.Size(11, 11),
new google.maps.Point(0, 0),
new google.maps.Point(6, 6)
);
},
/**
* @private
* Seek the geocode corresponding to the given address
* @param {String} addr the address to lookup.
* @param {google.maps.Marker} marker the marker to add (optional).
* @param {Boolean} clear clear other markers before creating this marker
* @param {Boolean} center true to set this point as the center of the map.
* @param {Object} listeners a listeners config
* @param {Boolean} adaptZoom true to set the zoom to position accuracy
*/
_geoCodeLookup: function(addr, marker, clear, center, listeners, adaptZoom)
{
if (!this._geocoder)
{
this._geocoder = Ext.create('google.maps.Geocoder', {});
}
this._geocoder.geocode(
{
address: addr
},
Ext.bind(this._centerMap, this, [addr, marker, clear, center, listeners, adaptZoom], true)
);
},
/**
* @private
* Center the map
* @param {Object} response the server's response
* @param {String} status the status of the response
* @param {String} addr the address to lookup.
* @param {google.maps.Marker} marker the marker to add (optional).
* @param {Boolean} clear clear other markers before creating this marker
* @param {Boolean} center true to set this point as the center of the map.
* @param {Object} listeners a listeners config
* @param {Boolean} adaptZoom true to set the zoom to position accuracy
*/
_centerMap: function(response, status, addr, marker, clear, center, listeners, adaptZoom)
{
if (!response || status !== 'OK')
{
this._showErrorMsg(status);
}
else
{
var accuracy = this._getAccuracy(response[0].geometry.location_type);
if (adaptZoom)
{
this.getMap().setZoom(accuracy*10);
this.zoomLevel = accuracy*8;
}
var location = response[0].geometry.location,
point = new google.maps.LatLng(location.lat(),location.lng());
if (center)
{
this.getMap().setCenter(point, this.zoomLevel);
this._lastCenter = point;
}
if (Ext.isObject(marker))
{
if (!marker.title)
{
marker.title = response.formatted_address;
}
var mkr = this._addMarker(point, marker, clear, false, listeners);
if (marker.callback)
{
marker.callback(this, mkr, point);
}
}
}
},
/**
* @private
* Show the error message corresponding to the given code
* @param {String} code the code of the error
*/
_showErrorMsg : function(code)
{
Ext.each(Ametys.plugins.maps.GMapPanel.LOOKUP_ERRORS, function(obj)
{
if (code == obj.code)
{
Ext.MessageBox.alert("{{i18n PLUGINS_MAPS_GMAP_LOOKUP_ERROR_TITLE}}", obj.msg);
}
}, this);
},
/**
* @private
* Get the accuracy corresponding to the given location type
* @param {String} serverLocationType the location type given by the server
* @return
*/
_getAccuracy: function(serverLocationType)
{
var level = 1;
Ext.each(Ametys.plugins.maps.GMapPanel.LOCATION_TYPES, function(locationType)
{
if (locationType.code === serverLocationType)
{
level = locationType.level;
return false;
}
});
return level;
},
/**
* @private
* Function invoked when records are added
* @param {Ext.data.Store} store the store
* @param {Array} records the array of {@link Ext.data.Record}
* @param {Number} index the index at which the records were inserted
*/
_recordAddedHandler: function(store, records, index)
{
if (records)
{
Ext.each(records, function(record)
{
if (record.get('gtype') == "marker")
{
this._addMarkerForRecord(record);
}
else if (record.get('gtype') == "polygon")
{
this._addShapeForRecord(record);
}
}, this);
}
},
/**
* @private
* Function invoked when records are updated
* @param {Ext.data.Store} store the store
* @param {Array} records the array of {@link Ext.data.Record}
* @param {Number} index the index at which the records were inserted
*/
_recordUpdatedHandler: function(store, records, index)
{
Ext.each(records, function(record)
{
if (record.get('gtype') == "marker")
{
this._recordRemovedHandler(store, record, index);
this._addMarkerForRecord(record);
}
else if (record.get('gtype') == "polygon")
{
this._recordRemovedHandler(store, record, index);
this._addShapeForRecord(record);
}
}, this);
},
/**
* @private
* Function invoked when a record is deleted
* @param {Ext.data.Store} store the store
* @param {Array} records the array of {@link Ext.data.Record}
* @param {Number} index the index at which the records were inserted
*/
_recordRemovedHandler: function(store, records, index)
{
var me = this;
Ext.Array.each(records, function(record)
{
if (record.get('gtype') == 'marker')
{
me._removeEntryFromCache(me.cache.marker, record);
}
else if (record.get('gtype') == 'polygon')
{
me._removeEntryFromCache(me.cache.polygon, record);
}
});
},
/**
* @private
* Remove the given displayed item link to the given record
* @param {Array} entries the items to remove (Marker or Polygon)
* @param {Ext.data.Record} record the record to remove
*/
_removeEntryFromCache:function (entries, record)
{
if (record)
{
for (var i = 0; i < entries.length; i++)
{
if(entries[i].recordId == record.id)
{
entries[i].setMap(null);
entries.splice(i,1);
break;
}
}
}
},
/**
* @private
* Set up the move map mode
*/
_moveMap: function()
{
// clean up listeners
google.maps.event.clearListeners(this.getMap(), 'click');
google.maps.event.clearListeners(this.getMap(), 'dblclick');
google.maps.event.clearListeners(this.getMap(), 'mousemove');
},
/**
* @private
* Add marker for a record
* @param {Ext.data.Record} record the record to add a marker for
*/
_addMarkerForRecord: function (record)
{
if (record)
{
var point = new google.maps.LatLng(record.get('lat'), record.get('lng'))
// add marker property
var marker =
{
animation: google.maps.Animation.DROP,
title: record.get('title'),
draggable: true,
icon : this._getMarkerIconUrl(record),
infoWindow :
{
content : this._infoWindowTemplate.apply({
title: record.get('title'),
description: Ext.util.Format.nl2br(record.get('description'))
})
},
recordId: record.id
};
// register listeners
var listeners = {
'dragstart': Ext.bind(this._markerDragStartHandler, this),
'dragend': Ext.bind(this._markerDragEndHandler, this)
};
this._addMarker(point, marker, false, null, listeners);
}
},
/**
* @private
* Creates a single marker.
* @param {google.maps.LatLng} point a GLatLng point
* @param {google.maps.Marker} marker a marker object consisting of at least lat and lng
* @param {Boolean} clear clear other markers before creating this marker
* @param {Boolean} center true to center the map on this marker
* @param {Object} listeners a listeners config
*/
_addMarker: function (point, marker, clear, center, listeners)
{
Ext.applyIf(marker,{});
if (clear === true)
{
this._clearMarkers();
}
if (center === true)
{
this.getMap().setCenter(point, this.zoomLevel)
this._lastCenter = point;
}
var mark = new google.maps.Marker(Ext.apply(marker, {
position: point
}));
if (marker.infoWindow)
{
this._createInfoWindow(marker.infoWindow, point, mark);
}
this.cache.marker.push(mark);
mark.setMap(this.getMap());
if (Ext.isObject(listeners))
{
for (evt in listeners)
{
google.maps.event.addListener(mark, evt, listeners[evt]);
}
}
return mark;
},
/**
* @private
* Create an Info Window.
* @param {Object} inwin an Info Window configuration
* @param {google.maps.LatLng} point the point to show the Info Window at
* @param {google.maps.Marker} marker a marker to attach the Info Window to
*/
_createInfoWindow : function(inwin, point, marker)
{
var me = this;
var infoWindow = Ext.create('google.maps.InfoWindow',
{
content: inwin.content,
position: point
});
if (marker)
{
google.maps.event.addListener(marker, 'click', function(){
me._hideAllInfoWindows();
infoWindow.open(me.getMap());
});
}
this.cache.infowindow.push(infoWindow);
return infoWindow;
},
/**
* @private
* Hide all information windows
*/
_hideAllInfoWindows: function()
{
for (var i = 0; i < this.cache.infowindow.length; i++)
{
this.cache.infowindow[i].close();
}
},
/**
* @private
* Clear all the markers
*/
_clearMarkers : function()
{
this._hideAllInfoWindows();
this._hideMarkers();
},
/**
* @private
* Hide all the markers
*/
_hideMarkers : function()
{
Ext.each(this.cache.marker, function(mrk){
mrk.setMap(null);
});
},
/**
* @private
* Show the markers
*/
_showMarkers : function()
{
var me = this;
Ext.each(this.cache.marker, function(mrk){
mrk.setMap(me.getMap());
});
},
/**
* @private
* Create the marker for the given mapEntry.
* @param {Ext.data.Model} record The record to edit
*/
_addShapeForRecord : function (record)
{
if (record)
{
// add marker property
var polygonOptions =
{
clickable:true,
fillColor: '#'+record.get('color'),
strokeColor: '#'+record.get('color'),
infoWindow : {
content : this._infoWindowTemplate.apply({
title:record.get('title'),
description:Ext.util.Format.nl2br(record.get('description'))
})
},
recordId: record.id
}
this._addPolygon(record.get('points'), polygonOptions, null);
}
},
/**
* @private
* Creates a single polygon.
* @param {Array} points an array of polygon points
* @param {Object} polygonOptions an object defining the style to use
* @param {Object} listeners the listeners to add to the polygon
*/
_addPolygon: function(points, polygonOptions, listeners)
{
var polygonPoints = new google.maps.MVCArray();
Ext.applyIf(polygonOptions,{
strokeColor: '#FF0000',
strokeOpacity: 1.0,
strokeWeight: 2,
fillColor: "#FF0000",
fillOpacity: 0.4
});
var latLng;
var bounds = new google.maps.LatLngBounds();
Ext.each(points, function(point)
{
latLng = new google.maps.LatLng(point.lat, point.lng)
polygonPoints.push(latLng);
bounds.extend(latLng);
});
var polygon = new google.maps.Polygon(Ext.apply({
path: polygonPoints
}, polygonOptions));
if (polygonOptions.infoWindow)
{
this._createInfoWindow(polygonOptions.infoWindow, bounds.getCenter(), polygon);
}
this.cache.polygon.push(polygon);
polygon.setMap(this.getMap());
if (typeof listeners === 'object')
{
for (event in listeners)
{
google.maps.event.addListener(polygon, event, listeners[event]);
}
}
return polygon;
},
/**
* @private
* Select the button matching the given mode in the toolbar
* @param {Number} newMode the mode to switch to
*/
_selectMapMode: function (newMode)
{
// Cancel the drawing of the shape
if (this._currentMode == Ametys.plugins.maps.GMapPanel.ADD_SHAPE_MAP_MODE && newMode != this._currentMode)
{
this._clearDrawingShape();
}
switch (newMode)
{
case Ametys.plugins.maps.GMapPanel.MOVE_MAP_MODE:
this.down('#handButton').toggle(true);
this.getMap().setOptions({draggableCursor: null});
break;
case Ametys.plugins.maps.GMapPanel.ADD_MARKER_MODE:
this.down('#markerButton').toggle(true);
this.getMap().setOptions({draggableCursor: 'crosshair'});
break;
case Ametys.plugins.maps.GMapPanel.ADD_SHAPE_MAP_MODE:
this.down('#shapeButton').toggle(true);
this.getMap().setOptions({draggableCursor: 'crosshair'});
break;
default:
throw 'Unknown mode' + newMode;
}
this._currentMode = newMode;
},
/*
* Markers
*/
/**
* @private
* Place a new marker on map click
*/
_placeMarker : function()
{
this._selectMapMode(Ametys.plugins.maps.GMapPanel.ADD_MARKER_MODE);
// Cancel previous click handlers
google.maps.event.clearListeners(this.getMap(), 'click');
google.maps.event.addListenerOnce(this.getMap(), "click", Ext.bind(this._placeMarkerHandler, this));
},
/**
* @private
* Open dialog to edit a marker
* @param record The current marker. Can NOT be null.
*/
_editMarkerProperties: function (record)
{
if (!this._markerPropertiesWindow)
{
this._markerPropertiesWindow = Ext.create('Ametys.plugins.maps.EditMarkerDialog', {saveFn: Ext.bind(this._saveMarkerProperties, this) });
}
this._editingMarker = record;
this._markerPropertiesWindow._initForm(record.get("title"), record.get("description"), record.get("icon"));
this._markerPropertiesWindow.show();
},
/**
* @private
* Save marker properties
* @param title the title of the marker
* @param description the description of the marker
* @param icon the path of the icon representing the marker
*/
_saveMarkerProperties: function (title, description, icon)
{
this._editingMarker.set("title", title);
this._editingMarker.set("description", description);
this._editingMarker.set("icon", icon);
if (!this.store.getById(this._editingMarker.id))
{
this.store.addSorted(this._editingMarker);
}
this.store.commitChanges();
this._editingMarker = null;
this._selectMapMode(Ametys.plugins.maps.GMapPanel.MOVE_MAP_MODE);
},
/**
* @private
* Place a new marker on a the map
* @param {Event} event the 'click' event
*/
_placeMarkerHandler: function (event)
{
if (event.latLng)
{
this._selectMapMode(Ametys.plugins.maps.GMapPanel.MOVE_MAP_MODE);
this._selectedRecord = Ext.create ('Ametys.plugins.maps.GMapConfiguration.GMapElement', {
lat: event.latLng.lat(),
lng: event.latLng.lng(),
title: '',
description: '',
icon: 'pin',
// color : this.DEFAULT_SHAPE_COLOR,
gtype:'marker'
});
// Edit marker properties
this._editMarkerProperties(this._selectedRecord);
}
},
/**
* @private
* Handles dragEnd handler on marker
* @param {Event} event the 'dragend' event
*/
_markerDragEndHandler: function (event)
{
if (this._selectedRecord && event)
{
this._selectedRecord.set("lat",event.latLng.lat());
this._selectedRecord.set("lng",event.latLng.lng());
this.store.commitChanges();
this._selectedRecord = null;
}
},
/**
* @private
* Handles dragStart handler on marker find the record that is dragged and set the _selectedRecord property
* @param {Event} event the 'dragstart' event
*/
_markerDragStartHandler: function (event)
{
if (event)
{
var recordIdx = this.store.findBy(function (record, id)
{
return record.get('lat') == event.latLng.lat() && record.get('lng') == event.latLng.lng();
});
var record = this.store.getAt(recordIdx);
if(record)
{
this._selectedRecord = record;
}
}
},
/**
* @private
* Get the icon url for given record
* @param {Ext.data.Record} record the marker's record
*/
_getMarkerIconUrl: function (record)
{
var icon = record.get('icon') || 'pin';
return Ametys.getPluginResourcesPrefix('maps') + '/img/poi/' + icon + '.png';
},
/*
* Shape
*/
/**
* @private
* Enter in shape mode
*/
_startShape: function ()
{
this._selectMapMode(Ametys.plugins.maps.GMapPanel.ADD_SHAPE_MAP_MODE);
this.shapeMarkers = [];
var polyOptions = {
path: [],
strokeColor: "#" + Ametys.plugins.maps.GMapPanel.DEFAULT_SHAPE_COLOR,
strokeOpacity: 1,
strokeWeight: 2,
fillColor: "#" + Ametys.plugins.maps.GMapPanel.DEFAULT_SHAPE_COLOR,
fillOpacity: 0.4
};
this._drawingShape = new google.maps.Polyline(polyOptions);
this._drawingShape.setMap(this.getMap());
polyOptions =
{
path: [],
strokeColor: "#" + Ametys.plugins.maps.GMapPanel.DEFAULT_SHAPE_COLOR,
strokeOpacity: 1,
strokeWeight: 2,
clickable : false
};
this._nextShapePath = new google.maps.Polyline(polyOptions);
this._nextShapePath.setMap(this.getMap());
// Clear previous listeners
google.maps.event.clearListeners(this.getMap(), 'click');
google.maps.event.addListener(this.getMap(), "click", Ext.bind(this._addLineMarkerHandler, this));
google.maps.event.addListener(this._drawingShape, "dblclick", Ext.bind(this._stopShape, this));
},
/**
* @private
* Stop the drawing of the shape
* @param {Object} event the 'dblclick' event
*/
_stopShape: function (event)
{
if (event)
{
// Do not add the marker cause it is managed by the click event
// this.addLineMarkerHandler(event);
google.maps.event.clearListeners(this.getMap(), 'click');
google.maps.event.clearListeners(this._drawingShape, 'dblclick');
google.maps.event.clearListeners(this.getMap(), 'mousemove');
var polyOptions =
{
path: this._drawingShape.getPath(),
strokeColor: "#" + Ametys.plugins.maps.GMapPanel.DEFAULT_SHAPE_COLOR,
strokeOpacity: 1,
strokeWeight: 2,
fillColor: "#" + Ametys.plugins.maps.GMapPanel.DEFAULT_SHAPE_COLOR,
fillOpacity: 0.4
};
// Remove the line
this._drawingShape.setMap(null);
// create the polygon
this._drawingShape = new google.maps.Polygon(polyOptions);
this._drawingShape.setMap(this.getMap());
var points = [];
this._drawingShape.getPath().forEach(function (latLng){
points.push({lat:latLng.lat(), lng:latLng.lng()});
})
this._selectedRecord = Ext.create('Ametys.plugins.maps.GMapConfiguration.GMapElement', {
title:"",
description : "",
color: Ametys.plugins.maps.GMapPanel.DEFAULT_SHAPE_COLOR,
points: points,
gtype:'polygon'
});
this._editShapeProperties(this._selectedRecord);
// Switch mode
this._selectMapMode(Ametys.plugins.maps.GMapPanel.MOVE_MAP_MODE);
}
},
/**
* @private
* Handler invoked when the map is clicked
* @param {Event} event the 'click' event
*/
_addLineMarkerHandler: function (event)
{
if(event && this._drawingShape)
{
this._addLineMarker(event.latLng);
this._drawingShape.getPath().push(event.latLng);
this._nextShapePath.getPath().clear();
this._nextShapePath.getPath().push(event.latLng);
if (this._drawingShape.getPath().getLength() == 1)
{
google.maps.event.addListener(this.getMap(), "mousemove", Ext.bind(this._mouseMoveOnShapeDrawHandler, this));
}
}
},
/**
* @private
* Add a marker on the given point for a shape
* @param {google.maps.LatLng} point the position of the marker
*/
_addLineMarker: function (point)
{
var marker = Ext.create ('google.maps.Marker',
{
position: point,
map: this.getMap(),
icon: this._vertexNormal,
raiseOnDrag: false,
draggable: false,
clickable : false
});
this._shapeMarkers.push(marker);
},
/**
* @private
* Handler invoked when the mouse is moved on the map
* @param {Object} event the 'mousemove' event
*/
_mouseMoveOnShapeDrawHandler: function (event)
{
if (event && this._nextShapePath)
{
if(this._nextShapePath.getPath().getLength() == 2)
{
this._nextShapePath.getPath().pop();
}
this._nextShapePath.getPath().push(event.latLng);
}
},
/**
* @private
* Open dialog to edit a shape
* @param {Ext.data.Model} record The current shape. Can NOT be null.
*/
_editShapeProperties: function (record)
{
if (!this._shapePropertiesWindow)
{
this._shapePropertiesWindow = Ext.create('Ametys.plugins.maps.EditShapeDialog', {saveFn: Ext.bind(this._saveShapeProperties, this)});
}
this._editingShape = record;
this._shapePropertiesWindow._initForm(record.get("title"), record.get("description"), record.get("color"));
this._shapePropertiesWindow.show();
},
/**
* @private
* Save shape properties
* @param title the title of the shape
* @param description the description of the shape
* @param color the colot of the shape
*/
_saveShapeProperties: function (title, description, color)
{
this._editingShape.set("title", title);
this._editingShape.set("description", description);
this._editingShape.set("color", color);
if (!this.store.getById(this._editingShape.id))
{
this.store.addSorted(this._editingShape);
this._clearDrawingShape();
}
this.store.commitChanges();
this._editingShape = null;
},
/**
* @private
* Clear the shape being drawn
*/
_clearDrawingShape: function()
{
// remove drawing shape & next shape
if (this._drawingShape)
{
this._drawingShape.setMap(null);
this._drawingShape = null;
}
if (this._nextShapePath)
{
this._nextShapePath.setMap(null);
this._nextShapePath = null;
}
// clean addlineMarkers
if (this._shapeMarkers)
{
Ext.each(this._shapeMarkers, function(marker)
{
marker.setMap(null);
});
}
}
});