cordova-plugin-googlemaps
Version:
Google Maps native SDK for Android and iOS, and Google Maps JavaScript API v3 for browser.
1,590 lines (1,394 loc) • 48.2 kB
JavaScript
var utils = require('cordova/utils'),
cordova_exec = require('cordova/exec'),
common = require('./Common'),
Overlay = require('./Overlay'),
BaseClass = require('./BaseClass'),
BaseArrayClass = require('./BaseArrayClass'),
LatLng = require('./LatLng'),
MapTypeId = require('./MapTypeId'),
event = require('./event'),
VisibleRegion = require('./VisibleRegion'),
Marker = require('./Marker'),
Circle = require('./Circle'),
Polyline = require('./Polyline'),
Polygon = require('./Polygon'),
TileOverlay = require('./TileOverlay'),
GroundOverlay = require('./GroundOverlay'),
KmlOverlay = require('./KmlOverlay'),
KmlLoader = require('./KmlLoader'),
MarkerCluster = require('./MarkerCluster');
/**
* Google Maps model.
*/
var exec;
var Map = function(__pgmId, _exec) {
var self = this;
exec = _exec;
Overlay.call(self, self, {}, 'Map', _exec, {
__pgmId: __pgmId
});
delete self.map;
self.set('myLocation', false);
self.set('myLocationButton', false);
self.MARKERS = {};
self.OVERLAYS = {};
var infoWindowLayer = document.createElement('div');
infoWindowLayer.style.position = 'absolute';
infoWindowLayer.style.left = 0;
infoWindowLayer.style.top = 0;
infoWindowLayer.style.width = 0;
infoWindowLayer.style.height = 0;
infoWindowLayer.style.overflow = 'visible';
infoWindowLayer.style['z-index'] = 1;
Object.defineProperty(self, '_layers', {
value: {
info: infoWindowLayer
},
enumerable: false,
writable: false
});
self.on(event.MAP_CLICK, function() {
self.set('active_marker', undefined);
});
self.on('active_marker_changed', function(prevMarker, newMarker) {
var newMarkerId = newMarker ? newMarker.getId() : null;
if (prevMarker) {
prevMarker.hideInfoWindow.call(prevMarker);
}
self.exec.call(self, null, null, self.__pgmId, 'setActiveMarkerId', [newMarkerId]);
});
};
utils.extend(Map, Overlay);
/**
* @desc Recalculate the position of HTML elements
*/
Map.prototype.refreshLayout = function() {
// Webkit redraw mandatory
// http://stackoverflow.com/a/3485654/697856
document.body.style.display = 'inline-block';
document.body.offsetHeight;
document.body.style.display = '';
this.exec.call(this, null, null, this.__pgmId, 'resizeMap', []);
};
Map.prototype.getMap = function(meta, div, options) {
var self = this,
args = [meta];
options = options || {};
self.set('clickable', options.clickable === false ? false : true);
self.set('visible', options.visible === false ? false : true);
if (options.controls) {
this.set('myLocation', options.controls.myLocation === true);
this.set('myLocationButton', options.controls.myLocationButton === true);
}
if (options.preferences && options.preferences.gestureBounds) {
if (utils.isArray(options.preferences.gestureBounds) ||
options.preferences.gestureBounds.type === 'LatLngBounds') {
options.preferences.gestureBounds = common.convertToPositionArray(options.preferences.gestureBounds);
}
}
if (!common.isDom(div)) {
self.set('visible', false);
options = div;
options = options || {};
if (options.camera) {
if (options.camera.latLng) {
options.camera.target = options.camera.latLng;
delete options.camera.latLng;
}
this.set('camera', options.camera);
if (options.camera.target) {
this.set('camera_target', options.camera.target);
}
if (options.camera.bearing) {
this.set('camera_bearing', options.camera.bearing);
}
if (options.camera.zoom) {
this.set('camera_zoom', options.camera.zoom);
}
if (options.camera.tilt) {
this.set('camera_tilt', options.camera.tilt);
}
}
args.push(options);
} else {
var positionCSS = common.getStyle(div, 'position');
if (!positionCSS || positionCSS === 'static') {
// important for HtmlInfoWindow
div.style.position = 'relative';
}
options = options || {};
if (options.camera) {
if (options.camera.latLng) {
options.camera.target = options.camera.latLng;
delete options.camera.latLng;
}
this.set('camera', options.camera);
if (options.camera.target) {
this.set('camera_target', options.camera.target);
}
if (options.camera.bearing) {
this.set('camera_bearing', options.camera.bearing);
}
if (options.camera.zoom) {
this.set('camera_zoom', options.camera.zoom);
}
if (options.camera.tilt) {
this.set('camera_tilt', options.camera.tilt);
}
}
if (utils.isArray(options.styles)) {
options.styles = JSON.stringify(options.styles);
}
args.push(options);
div.style.overflow = 'hidden';
self.set('div', div);
if (div.offsetWidth < 100 || div.offsetHeight < 100) {
// If the map Div is too small, wait a little.
var callee = arguments.callee;
setTimeout(function() {
callee.call(self, meta, div, options);
}, 250 + Math.random() * 100);
return;
}
// Gets the map div size.
// The plugin needs to consider the viewport zoom ratio
// for the case window.innerHTML > body.offsetWidth.
var elemId = common.getPluginDomId(div);
args.push(elemId);
}
exec.call({
_isReady: true
}, function() {
//------------------------------------------------------------------------
// Clear background colors of map div parents after the map is created
//------------------------------------------------------------------------
var div = self.get('div');
if (common.isDom(div)) {
// Insert the infoWindow layer
if (self._layers.info.parentNode) {
try {
self._layers.info.parentNode.removeChild(self._layers.info.parentNode);
} catch (e) {
// ignore
}
}
var positionCSS;
for (var i = 0; i < div.children.length; i++) {
positionCSS = common.getStyle(div.children[i], 'position');
if (positionCSS === 'static') {
div.children[i].style.position = 'relative';
}
}
div.insertBefore(self._layers.info, div.firstChild);
while (div.parentNode) {
div.style.backgroundColor = 'rgba(0,0,0,0) !important';
// Add _gmaps_cdv_ class
common.attachTransparentClass(div);
div = div.parentNode;
}
}
cordova.fireDocumentEvent('plugin_touch', {
force: true
});
//------------------------------------------------------------------------
// In order to work map.getVisibleRegion() correctly, wait a little.
//------------------------------------------------------------------------
var waitCnt = 0;
var waitCameraSync = function() {
if (!self.getVisibleRegion() && (waitCnt++ < 10)) {
setTimeout(function() {
common.nextTick(waitCameraSync);
}, 100);
return;
}
self._privateInitialize();
delete self._privateInitialize;
self.refreshLayout();
self.trigger(event.MAP_READY, self);
};
setTimeout(function() {
common.nextTick(waitCameraSync);
}, 100);
}, self.errorHandler, 'CordovaGoogleMaps', 'getMap', args, {
sync: true
});
};
Map.prototype.setOptions = function(options) {
options = options || {};
if (options.controls) {
var myLocation = this.get('myLocation');
if ('myLocation' in options.controls) {
myLocation = options.controls.myLocation === true;
}
var myLocationButton = this.get('myLocationButton');
if ('myLocationButton' in options.controls) {
myLocationButton = options.controls.myLocationButton === true;
}
this.set('myLocation', myLocation);
this.set('myLocationButton', myLocationButton);
if (myLocation === true || myLocation === false) {
options.controls.myLocation = myLocation;
}
if (myLocationButton === true || myLocationButton === false) {
options.controls.myLocationButton = myLocationButton;
}
}
if (options.camera) {
if (options.camera.latLng) {
options.camera.target = options.camera.latLng;
delete options.camera.latLng;
}
this.set('camera', options.camera);
if (options.camera.target) {
this.set('camera_target', options.camera.target);
}
if (options.camera.bearing) {
this.set('camera_bearing', options.camera.bearing);
}
if (options.camera.zoom) {
this.set('camera_zoom', options.camera.zoom);
}
if (options.camera.tilt) {
this.set('camera_tilt', options.camera.tilt);
}
}
if (options.preferences && options.preferences.gestureBounds) {
if (utils.isArray(options.preferences.gestureBounds) ||
options.preferences.gestureBounds.type === 'LatLngBounds') {
options.preferences.gestureBounds = common.convertToPositionArray(options.preferences.gestureBounds);
}
}
if (utils.isArray(options.styles)) {
options.styles = JSON.stringify(options.styles);
}
this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setOptions', [options]);
return this;
};
Map.prototype.getMyLocation = function(params, success_callback, error_callback) {
return window.plugin.google.maps.LocationService.getMyLocation.call(this, params, success_callback, error_callback);
};
Map.prototype.setCameraTarget = function(latLng) {
this.set('camera_target', latLng);
this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setCameraTarget', [latLng.lat, latLng.lng]);
return this;
};
Map.prototype.setCameraZoom = function(zoom) {
this.set('camera_zoom', zoom);
this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setCameraZoom', [zoom], {
sync: true
});
return this;
};
Map.prototype.panBy = function(x, y) {
x = parseInt(x, 10);
y = parseInt(y, 10);
this.exec.call(this, null, this.errorHandler, this.__pgmId, 'panBy', [x, y], {
sync: true
});
return this;
};
/**
* Clears all markup that has been added to the map,
* including markers, polylines and ground overlays.
*/
Map.prototype.clear = function(callback) {
var self = this;
if (self._isRemoved) {
// Simply ignore because this map is already removed.
return Promise.resolve();
}
// Close the active infoWindow
var active_marker = self.get('active_marker');
if (active_marker) {
active_marker.trigger(event.INFO_CLOSE);
}
var clearObj = function(obj) {
var ids = Object.keys(obj);
var id, instance;
for (var i = 0; i < ids.length; i++) {
id = ids[i];
instance = obj[id];
if (instance) {
if (typeof instance.remove === 'function') {
instance.remove();
}
instance.off();
delete obj[id];
}
}
obj = {};
};
clearObj(self.OVERLAYS);
clearObj(self.MARKERS);
self.trigger('map_clear');
var resolver = function(resolve, reject) {
self.exec.call(self,
resolve.bind(self),
reject.bind(self),
self.__pgmId, 'clear', [], {
sync: true
});
};
if (typeof callback === 'function') {
resolver(callback, self.errorHandler);
} else {
return new Promise(resolver);
}
};
/**
* @desc Change the map type
* @param {String} mapTypeId Specifies the one of the follow strings:
* MAP_TYPE_HYBRID
* MAP_TYPE_SATELLITE
* MAP_TYPE_TERRAIN
* MAP_TYPE_NORMAL
* MAP_TYPE_NONE
*/
Map.prototype.setMapTypeId = function(mapTypeId) {
if (mapTypeId !== MapTypeId[mapTypeId.replace('MAP_TYPE_', '')]) {
return this.errorHandler('Invalid MapTypeId was specified.');
}
this.set('mapTypeId', mapTypeId);
this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setMapTypeId', [mapTypeId]);
return this;
};
/**
* @desc Change the map view angle
* @param {Number} tilt The angle
*/
Map.prototype.setCameraTilt = function(tilt) {
this.set('camera_tilt', tilt);
this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setCameraTilt', [tilt], {
sync: true
});
return this;
};
/**
* @desc Change the map view bearing
* @param {Number} bearing The bearing
*/
Map.prototype.setCameraBearing = function(bearing) {
this.set('camera_bearing', bearing);
this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setCameraBearing', [bearing], {
sync: true
});
return this;
};
Map.prototype.moveCameraZoomIn = function(callback) {
var self = this;
var cameraPosition = self.get('camera');
cameraPosition.zoom++;
cameraPosition.zoom = cameraPosition.zoom < 0 ? 0 : cameraPosition.zoom;
return self.moveCamera(cameraPosition, callback);
};
Map.prototype.moveCameraZoomOut = function(callback) {
var self = this;
var cameraPosition = self.get('camera');
cameraPosition.zoom--;
cameraPosition.zoom = cameraPosition.zoom < 0 ? 0 : cameraPosition.zoom;
return self.moveCamera(cameraPosition, callback);
};
Map.prototype.animateCameraZoomIn = function(callback) {
var self = this;
var cameraPosition = self.get('camera');
cameraPosition.zoom++;
cameraPosition.zoom = cameraPosition.zoom < 0 ? 0 : cameraPosition.zoom;
cameraPosition.duration = 500;
return self.animateCamera(cameraPosition, callback);
};
Map.prototype.animateCameraZoomOut = function(callback) {
var self = this;
var cameraPosition = self.get('camera');
cameraPosition.zoom--;
cameraPosition.zoom = cameraPosition.zoom < 0 ? 0 : cameraPosition.zoom;
cameraPosition.duration = 500;
return self.animateCamera(cameraPosition, callback);
};
/**
* Move the map camera with animation
* @name animateCamera
* @param {CameraPosition} cameraPosition - New camera position
* @param {Function} [callback] - This callback is involved when the animation is completed.
* @return {Promise} if you omit `callback`.
*/
Map.prototype.animateCamera = function(cameraPosition, callback) {
var self = this,
error;
var target = cameraPosition.target;
if (!target && 'position' in cameraPosition) {
target = cameraPosition.position;
}
if (!target) {
error = new Error('No target field is specified.');
if (typeof callback === 'function') {
throw error;
} else {
return Promise.reject(error);
}
}
if ('heading' in cameraPosition) {
cameraPosition.heading = cameraPosition.heading % 360;
}
if ('tilt' in cameraPosition) {
cameraPosition.tilt = Math.min(Math.max(0, cameraPosition.tilt), 90);
}
// if (!('padding' in cameraPosition)) {
// cameraPosition.padding = 10;
// }
if (utils.isArray(target) || target.type === 'LatLngBounds') {
target = common.convertToPositionArray(target);
if (target.length === 0) {
// skip if no point is specified
error = new Error('No point is specified.');
if (typeof callback === 'function') {
throw error;
} else {
return Promise.reject(error);
}
}
}
cameraPosition.target = target;
var resolver = function(resolve, reject) {
self.exec.call(self,
resolve.bind(self),
reject.bind(self),
self.__pgmId, 'animateCamera', [cameraPosition], {
sync: true
});
};
if (typeof callback === 'function') {
resolver(callback, self.errorHandler);
} else {
return new Promise(resolver);
}
};
/**
* Move the map camera without animation
*
* @name moveCamera
* @param {CameraPosition} - cameraPosition New camera position
* @param {Function} [callback] - This callback is involved when the animation is completed.
* @return {Promise} if you omit `callback`.
*/
Map.prototype.moveCamera = function(cameraPosition, callback) {
var self = this;
var target = cameraPosition.target;
if (!target && 'position' in cameraPosition) {
target = cameraPosition.position;
}
if (!target) {
return Promise.reject('No target field is specified.');
}
// if (!('padding' in cameraPosition)) {
// cameraPosition.padding = 10;
// }
if (utils.isArray(target) || target.type === 'LatLngBounds') {
target = common.convertToPositionArray(target);
if (target.length === 0) {
// skip if no point is specified
if (typeof callback === 'function') {
callback.call(self);
return;
} else {
return Promise.reject('No point is specified.');
}
}
}
cameraPosition.target = target;
var resolver = function(resolve, reject) {
self.exec.call(self,
resolve.bind(self),
reject.bind(self),
self.__pgmId, 'moveCamera', [cameraPosition], {
sync: true
});
};
if (typeof callback === 'function') {
resolver(callback, self.errorHandler);
} else {
return new Promise(resolver);
}
};
Map.prototype.setMyLocationButtonEnabled = function(enabled) {
var self = this;
enabled = common.parseBoolean(enabled);
this.set('myLocationButton', enabled);
self.exec.call(self, null, this.errorHandler, this.__pgmId, 'setMyLocationEnabled', [{
myLocationButton: enabled,
myLocation: self.get('myLocation') === true
}], {
sync: true
});
return this;
};
Map.prototype.setMyLocationEnabled = function(enabled) {
var self = this;
enabled = common.parseBoolean(enabled);
this.set('myLocation', enabled);
self.exec.call(self, null, this.errorHandler, this.__pgmId, 'setMyLocationEnabled', [{
myLocationButton: self.get('myLocationButton') === true,
myLocation: enabled
}], {
sync: true
});
return this;
};
Map.prototype.setIndoorEnabled = function(enabled) {
enabled = common.parseBoolean(enabled);
this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setIndoorEnabled', [enabled]);
return this;
};
Map.prototype.setTrafficEnabled = function(enabled) {
enabled = common.parseBoolean(enabled);
this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setTrafficEnabled', [enabled]);
return this;
};
Map.prototype.setCompassEnabled = function(enabled) {
var self = this;
enabled = common.parseBoolean(enabled);
self.exec.call(self, null, self.errorHandler, this.__pgmId, 'setCompassEnabled', [enabled]);
return this;
};
Map.prototype.getFocusedBuilding = function(callback) {
var self = this;
var resolver = function(resolve, reject) {
self.exec.call(self,
resolve.bind(self),
reject.bind(self),
self.__pgmId, 'getFocusedBuilding', []);
};
if (typeof callback === 'function') {
resolver(callback, self.errorHandler);
} else {
return new Promise(resolver);
}
};
Map.prototype.getVisible = function() {
return this.get('visible');
};
Map.prototype.setVisible = function(isVisible) {
cordova.fireDocumentEvent('plugin_touch');
var self = this;
isVisible = common.parseBoolean(isVisible);
self.set('visible', isVisible);
self.exec.call(self, null, self.errorHandler, this.__pgmId, 'setVisible', [isVisible]);
return this;
};
Map.prototype.setClickable = function(isClickable) {
cordova.fireDocumentEvent('plugin_touch');
var self = this;
isClickable = common.parseBoolean(isClickable);
self.set('clickable', isClickable);
self.exec.call(self, null, self.errorHandler, this.__pgmId, 'setClickable', [isClickable]);
return this;
};
Map.prototype.getClickable = function() {
return this.get('clickable');
};
/**
* Sets the preference for whether all gestures should be enabled or disabled.
*/
Map.prototype.setAllGesturesEnabled = function(enabled) {
var self = this;
enabled = common.parseBoolean(enabled);
self.exec.call(self, null, self.errorHandler, this.__pgmId, 'setAllGesturesEnabled', [enabled]);
return this;
};
/**
* Return the current position of the camera
* @return {CameraPosition}
*/
Map.prototype.getCameraPosition = function() {
return this.get('camera');
};
/**
* Cancel the camera animation
* @return {CameraPosition}
*/
Map.prototype.stopAnimation = function() {
var self = this;
if (self._isReady) {
cordova_exec(null, null, self.__pgmId, 'stopAnimation', []);
}
};
/**
* Remove the map completely.
*/
Map.prototype.remove = function(callback) {
var self = this;
if (self._isRemoved) {
return;
}
Object.defineProperty(self, '_isRemoved', {
value: true,
writable: false
});
self.stopAnimation();
self.trigger('remove');
// var div = self.get('div');
// if (div) {
// while (div) {
// if (div.style) {
// div.style.backgroundColor = '';
// }
// if (div.classList) {
// div.classList.remove('_gmaps_cdv_');
// } else if (div.className) {
// div.className = div.className.replace(/_gmaps_cdv_/g, '');
// div.className = div.className.replace(/\s+/g, ' ');
// }
// div = div.parentNode;
// }
// }
// self.set('div', undefined);
// Close the active infoWindow
var active_marker = self.get('active_marker');
if (active_marker) {
active_marker.trigger(event.INFO_CLOSE);
}
var clearObj = function(obj) {
var ids = Object.keys(obj);
var id, instance;
for (var i = 0; i < ids.length; i++) {
id = ids[i];
instance = obj[id];
if (instance) {
if (typeof instance.remove === 'function') {
instance.remove();
}
instance.off();
delete obj[id];
}
}
obj = {};
};
clearObj(self.OVERLAYS);
clearObj(self.MARKERS);
var resolver = function(resolve, reject) {
self.exec.call(self,
resolve.bind(self),
reject.bind(self),
'CordovaGoogleMaps', 'removeMap', [self.__pgmId],
{
sync: true,
remove: true
});
};
if (typeof callback === 'function') {
resolver(callback, self.errorHandler);
} else {
return new Promise(resolver);
}
};
Map.prototype.toDataURL = function(params, callback) {
var args = [params || {}, callback];
if (typeof args[0] === 'function') {
args.unshift({});
}
params = args[0];
callback = args[1];
params.uncompress = params.uncompress === true;
var self = this;
var resolver = function(resolve, reject) {
self.exec.call(self,
resolve.bind(self),
reject.bind(self),
self.__pgmId, 'toDataURL', [params]);
};
if (typeof callback === 'function') {
resolver(callback, self.errorHandler);
} else {
return new Promise(resolver);
}
};
/**
* Show the map into the specified div.
*/
Map.prototype.getDiv = function() {
return this.get('div');
};
/**
* Show the map into the specified div.
*/
Map.prototype.setDiv = function(div) {
var self = this,
args = [];
if (!common.isDom(div)) {
div = self.get('div');
if (common.isDom(div)) {
div.removeAttribute('__pluginMapId');
}
self.set('div', null);
} else {
div.setAttribute('__pluginMapId', self.__pgmId);
// Insert the infoWindow layer
if (self._layers.info.parentNode) {
try {
self._layers.info.parentNode.removeChild(self._layers.info.parentNode);
} catch(e) {
//ignore
}
}
var positionCSS;
for (var i = 0; i < div.children.length; i++) {
positionCSS = common.getStyle(div.children[i], 'position');
if (positionCSS === 'static') {
div.children[i].style.position = 'relative';
}
}
div.insertBefore(self._layers.info, div.firstChild);
// Webkit redraw mandatory
// http://stackoverflow.com/a/3485654/697856
// div.style.display = 'none';
// div.offsetHeight;
// div.style.display = '';
document.body.style.transform = 'rotateZ(0deg)';
self.set('div', div);
if (cordova.platform === 'browser') {
return;
}
positionCSS = common.getStyle(div, 'position');
if (!positionCSS || positionCSS === 'static') {
div.style.position = 'relative';
}
var elemId = common.getPluginDomId(div);
args.push(elemId);
while (div.parentNode) {
div.style.backgroundColor = 'rgba(0,0,0,0)';
// Add _gmaps_cdv_ class
common.attachTransparentClass(div);
div = div.parentNode;
}
}
self.exec.call(self, function() {
cordova.fireDocumentEvent('plugin_touch', {
force: true,
action: 'setDiv'
});
self.refreshLayout();
}, self.errorHandler, self.__pgmId, 'setDiv', args, {
sync: true
});
return self;
};
/**
* Return the visible region of the map.
*/
Map.prototype.getVisibleRegion = function(callback) {
var self = this;
var cameraPosition = self.get('camera');
if (!cameraPosition || !cameraPosition.southwest || !cameraPosition.northeast) {
return null;
}
var latLngBounds = new VisibleRegion(
cameraPosition.southwest,
cameraPosition.northeast,
cameraPosition.farLeft,
cameraPosition.farRight,
cameraPosition.nearLeft,
cameraPosition.nearRight
);
if (typeof callback === 'function') {
console.log('[deprecated] getVisibleRegion() is changed. Please check out the https://goo.gl/yHstHQ');
callback.call(self, latLngBounds);
}
return latLngBounds;
};
/**
* Maps an Earth coordinate to a point coordinate in the map's view.
*/
Map.prototype.fromLatLngToPoint = function(latLng, callback) {
var self = this;
if ('lat' in latLng && 'lng' in latLng) {
var resolver = function(resolve, reject) {
self.exec.call(self,
resolve.bind(self),
reject.bind(self),
self.__pgmId, 'fromLatLngToPoint', [latLng.lat, latLng.lng]);
};
if (typeof callback === 'function') {
resolver(callback, self.errorHandler);
} else {
return new Promise(resolver);
}
} else {
var rejector = function(resolve, reject) {
reject('The latLng is invalid');
};
if (typeof callback === 'function') {
rejector(callback, self.errorHandler);
} else {
return new Promise(rejector);
}
}
};
/**
* Maps a point coordinate in the map's view to an Earth coordinate.
*/
Map.prototype.fromPointToLatLng = function(pixel, callback) {
var self = this;
if (typeof pixel === 'object' && 'x' in pixel && 'y' in pixel) {
pixel = [pixel.x, pixel.y];
}
if (pixel.length == 2 && utils.isArray(pixel)) {
var resolver = function(resolve, reject) {
self.exec.call(self,
function(result) {
var latLng = new LatLng(result[0] || 0, result[1] || 0);
resolve.call(self, latLng);
},
reject.bind(self),
self.__pgmId, 'fromPointToLatLng', [pixel[0], pixel[1]]);
};
if (typeof callback === 'function') {
resolver(callback, self.errorHandler);
} else {
return new Promise(resolver);
}
} else {
var rejector = function(resolve, reject) {
reject('The pixel[] argument is invalid');
};
if (typeof callback === 'function') {
rejector(callback, self.errorHandler);
} else {
return new Promise(rejector);
}
}
};
Map.prototype.setPadding = function(p1, p2, p3, p4) {
if (arguments.length === 0 || arguments.length > 4) {
return this;
}
var padding = {};
padding.top = parseInt(p1, 10);
switch (arguments.length) {
case 4:
// top right bottom left
padding.right = parseInt(p2, 10);
padding.bottom = parseInt(p3, 10);
padding.left = parseInt(p4, 10);
break;
case 3:
// top right&left bottom
padding.right = parseInt(p2, 10);
padding.left = padding.right;
padding.bottom = parseInt(p3, 10);
break;
case 2:
// top & bottom right&left
padding.bottom = parseInt(p1, 10);
padding.right = parseInt(p2, 10);
padding.left = padding.right;
break;
case 1:
// top & bottom right & left
padding.bottom = padding.top;
padding.right = padding.top;
padding.left = padding.top;
break;
}
this.exec.call(this, null, self.errorHandler, this.__pgmId, 'setPadding', [padding]);
return this;
};
Map.prototype.addKmlOverlay = function(kmlOverlayOptions, callback) {
var self = this;
kmlOverlayOptions = kmlOverlayOptions || {};
kmlOverlayOptions.url = kmlOverlayOptions.url || null;
kmlOverlayOptions.clickable = common.defaultTrueOption(kmlOverlayOptions.clickable);
kmlOverlayOptions.suppressInfoWindows = kmlOverlayOptions.suppressInfoWindows === true;
if (kmlOverlayOptions.url) {
var link = document.createElement('a');
link.href = kmlOverlayOptions.url;
kmlOverlayOptions.url = link.protocol+'//'+link.host+link.pathname + link.search;
var invisible_dot = self.get('invisible_dot');
if (!invisible_dot || invisible_dot._isRemoved) {
// Create an invisible marker for kmlOverlay
self.set('invisible_dot', self.addMarker({
position: {
lat: 0,
lng: 0
},
icon: 'skyblue',
visible: false
}));
}
if ('icon' in kmlOverlayOptions) {
self.get('invisible_dot').setIcon(kmlOverlayOptions.icon);
}
var resolver = function(resolve, reject) {
var loader = new KmlLoader(self, self.exec, kmlOverlayOptions);
loader.parseKmlFile(function(camera, kmlData) {
if (kmlData instanceof BaseClass) {
kmlData = new BaseArrayClass([kmlData]);
}
var kmlId = 'kmloverlay_' + Math.floor(Math.random() * Date.now());
var kmlOverlay = new KmlOverlay(self, kmlId, camera, kmlData, kmlOverlayOptions);
self.OVERLAYS[kmlId] = kmlOverlay;
resolve.call(self, kmlOverlay);
}, reject);
};
if (typeof callback === 'function') {
resolver(callback, self.errorHandler);
} else {
return new Promise(resolver);
}
} else {
if (typeof callback === 'function') {
throw new Error('KML file url is required.');
} else {
return Promise.reject('KML file url is required.');
}
}
};
//-------------
// Ground overlay
//-------------
Map.prototype.addGroundOverlay = function(groundOverlayOptions, callback) {
var self = this;
groundOverlayOptions = groundOverlayOptions || {};
groundOverlayOptions.anchor = groundOverlayOptions.anchor || [0.5, 0.5];
groundOverlayOptions.bearing = 'bearing' in groundOverlayOptions ? groundOverlayOptions.bearing : 0;
groundOverlayOptions.url = groundOverlayOptions.url || null;
groundOverlayOptions.clickable = groundOverlayOptions.clickable === true;
groundOverlayOptions.visible = common.defaultTrueOption(groundOverlayOptions.visible);
groundOverlayOptions.zIndex = groundOverlayOptions.zIndex || 0;
groundOverlayOptions.bounds = common.convertToPositionArray(groundOverlayOptions.bounds);
groundOverlayOptions.noCaching = true;
var groundOverlay = new GroundOverlay(self, groundOverlayOptions, exec);
var groundOverlayId = groundOverlay.getId();
self.OVERLAYS[groundOverlayId] = groundOverlay;
groundOverlay.one(groundOverlayId + '_remove', function() {
groundOverlay.off();
delete self.OVERLAYS[groundOverlayId];
groundOverlay = undefined;
});
self.exec.call(self, function() {
groundOverlay._privateInitialize();
delete groundOverlay._privateInitialize;
if (typeof callback === 'function') {
callback.call(self, groundOverlay);
}
}, self.errorHandler, self.__pgmId, 'loadPlugin', ['GroundOverlay', groundOverlayOptions, groundOverlay.hashCode]);
return groundOverlay;
};
//-------------
// Tile overlay
//-------------
Map.prototype.addTileOverlay = function(tilelayerOptions, callback) {
var self = this;
tilelayerOptions = tilelayerOptions || {};
tilelayerOptions.tileUrlFormat = tilelayerOptions.tileUrlFormat || null;
if (typeof tilelayerOptions.tileUrlFormat === 'string') {
console.log('[deprecated] the tileUrlFormat property is now deprecated. Use the getTile property.');
tilelayerOptions.getTile = function(x, y, zoom) {
return tilelayerOptions.tileUrlFormat.replace(/<x>/gi, x)
.replace(/<y>/gi, y)
.replace(/<zoom>/gi, zoom);
};
}
if (typeof tilelayerOptions.getTile !== 'function') {
throw new Error('[error] the getTile property is required.');
}
tilelayerOptions.visible = common.defaultTrueOption(tilelayerOptions.visible);
tilelayerOptions.zIndex = tilelayerOptions.zIndex || 0;
tilelayerOptions.tileSize = tilelayerOptions.tileSize || 512;
tilelayerOptions.opacity = (tilelayerOptions.opacity === null || tilelayerOptions.opacity === undefined) ? 1 : tilelayerOptions.opacity;
tilelayerOptions.debug = tilelayerOptions.debug === true;
tilelayerOptions.userAgent = tilelayerOptions.userAgent || navigator.userAgent;
var tileOverlay = new TileOverlay(self, tilelayerOptions, exec);
var tileOverlayId = tileOverlay.getId();
self.OVERLAYS[tileOverlayId] = tileOverlay;
var hashCode = tileOverlay.hashCode;
tileOverlay.one(tileOverlayId + '_remove', function() {
document.removeEventListener(tileOverlayId + '-' + hashCode + '-tileoverlay', onNativeCallback);
tileOverlay.off();
delete self.OVERLAYS[tileOverlayId];
tileOverlay = undefined;
});
var options = {
visible: tilelayerOptions.visible,
zIndex: tilelayerOptions.zIndex,
tileSize: tilelayerOptions.tileSize,
opacity: tilelayerOptions.opacity,
userAgent: tilelayerOptions.userAgent,
debug: tilelayerOptions.debug
};
var onNativeCallback = function(params) {
var url = tilelayerOptions.getTile(params.x, params.y, params.zoom);
if (!url || url === '(null)' || url === 'undefined' || url === 'null') {
url = '(null)';
}
if (url instanceof Promise) {
common.promiseTimeout(5000, url)
.then(function(finalUrl) {
var link = document.createElement('a');
link.href = finalUrl;
finalUrl = link.protocol+'//'+link.host+link.pathname + link.search;
cordova_exec(null, self.errorHandler, self.__pgmId + '-tileoverlay', 'onGetTileUrlFromJS', [hashCode, params.key, finalUrl]);
})
.catch(function() {
cordova_exec(null, self.errorHandler, self.__pgmId + '-tileoverlay', 'onGetTileUrlFromJS', [hashCode, params.key, '(null)']);
});
} else {
var link = document.createElement('a');
link.href = url;
url = link.protocol+'//'+link.host+link.pathname + link.search;
cordova_exec(null, self.errorHandler, self.__pgmId + '-tileoverlay', 'onGetTileUrlFromJS', [hashCode, params.key, url]);
}
};
document.addEventListener(self.__pgmId + '-' + hashCode + '-tileoverlay', onNativeCallback);
self.exec.call(self, function() {
tileOverlay._privateInitialize();
delete tileOverlay._privateInitialize;
if (typeof callback === 'function') {
callback.call(self, tileOverlay);
}
}, self.errorHandler, self.__pgmId, 'loadPlugin', ['TileOverlay', options, hashCode]);
return tileOverlay;
};
//-------------
// Polygon
//-------------
Map.prototype.addPolygon = function(polygonOptions, callback) {
var self = this;
polygonOptions.points = polygonOptions.points || [];
var _orgs = polygonOptions.points;
polygonOptions.points = common.convertToPositionArray(polygonOptions.points);
polygonOptions.holes = polygonOptions.holes || [];
if (polygonOptions.holes.length > 0 && !Array.isArray(polygonOptions.holes[0])) {
polygonOptions.holes = [polygonOptions.holes];
}
polygonOptions.holes = polygonOptions.holes.map(function(hole) {
if (!utils.isArray(hole)) {
return [];
}
return hole.map(function(position) {
return {
'lat': position.lat,
'lng': position.lng
};
});
});
polygonOptions.strokeColor = common.HTMLColor2RGBA(polygonOptions.strokeColor || '#FF000080', 0.75);
if (polygonOptions.fillColor) {
polygonOptions.fillColor = common.HTMLColor2RGBA(polygonOptions.fillColor || '#FF000080', 0.75);
} else {
polygonOptions.fillColor = common.HTMLColor2RGBA('#FF000080', 0.75);
}
polygonOptions.strokeWidth = 'strokeWidth' in polygonOptions ? polygonOptions.strokeWidth : 10;
polygonOptions.visible = common.defaultTrueOption(polygonOptions.visible);
polygonOptions.clickable = polygonOptions.clickable === true;
polygonOptions.zIndex = polygonOptions.zIndex || 0;
polygonOptions.geodesic = polygonOptions.geodesic === true;
var opts = JSON.parse(JSON.stringify(polygonOptions));
polygonOptions.points = _orgs;
var polygon = new Polygon(self, polygonOptions, exec);
var polygonId = polygon.getId();
self.OVERLAYS[polygonId] = polygon;
polygon.one(polygonId + '_remove', function() {
polygon.off();
delete self.OVERLAYS[polygonId];
polygon = undefined;
});
self.exec.call(self, function() {
polygon._privateInitialize();
delete polygon._privateInitialize;
if (typeof callback === 'function') {
callback.call(self, polygon);
}
}, self.errorHandler, self.__pgmId, 'loadPlugin', ['Polygon', opts, polygon.hashCode]);
return polygon;
};
//-------------
// Polyline
//-------------
Map.prototype.addPolyline = function(polylineOptions, callback) {
var self = this;
polylineOptions.points = polylineOptions.points || [];
var _orgs = polylineOptions.points;
polylineOptions.points = common.convertToPositionArray(polylineOptions.points);
polylineOptions.color = common.HTMLColor2RGBA(polylineOptions.color || '#FF000080', 0.75);
polylineOptions.width = 'width' in polylineOptions ? polylineOptions.width : 10;
polylineOptions.visible = common.defaultTrueOption(polylineOptions.visible);
polylineOptions.clickable = polylineOptions.clickable === true;
polylineOptions.zIndex = polylineOptions.zIndex || 0;
polylineOptions.geodesic = polylineOptions.geodesic === true;
var opts = JSON.parse(JSON.stringify(polylineOptions));
polylineOptions.points = _orgs;
var polyline = new Polyline(self, polylineOptions, exec);
var polylineId = polyline.getId();
self.OVERLAYS[polylineId] = polyline;
polyline.one(polylineId + '_remove', function() {
polyline.off();
delete self.OVERLAYS[polylineId];
polyline = undefined;
});
self.exec.call(self, function() {
if (polyline) {
polyline._privateInitialize();
delete polyline._privateInitialize;
if (typeof callback === 'function') {
callback.call(self, polyline);
}
}
}, self.errorHandler, self.__pgmId, 'loadPlugin', ['Polyline', opts, polyline.hashCode]);
return polyline;
};
//-------------
// Circle
//-------------
Map.prototype.addCircle = function(circleOptions, callback) {
var self = this;
circleOptions.center = circleOptions.center || {};
circleOptions.center.lat = circleOptions.center.lat || 0.0;
circleOptions.center.lng = circleOptions.center.lng || 0.0;
circleOptions.strokeColor = common.HTMLColor2RGBA(circleOptions.strokeColor || '#FF0000', 0.75);
circleOptions.fillColor = common.HTMLColor2RGBA(circleOptions.fillColor || '#000000', 0.75);
circleOptions.strokeWidth = 'strokeWidth' in circleOptions ? circleOptions.strokeWidth : 10;
circleOptions.visible = common.defaultTrueOption(circleOptions.visible);
circleOptions.zIndex = circleOptions.zIndex || 0;
circleOptions.radius = 'radius' in circleOptions ? circleOptions.radius : 1;
var circle = new Circle(self, circleOptions, exec);
var circleId = circle.getId();
self.OVERLAYS[circleId] = circle;
circle.one(circleId + '_remove', function() {
circle.off();
delete self.OVERLAYS[circleId];
circle = undefined;
});
self.exec.call(self, function() {
circle._privateInitialize();
delete circle._privateInitialize;
if (typeof callback === 'function') {
callback.call(self, circle);
}
}, self.errorHandler, self.__pgmId, 'loadPlugin', ['Circle', circleOptions, circle.hashCode]);
return circle;
};
//-------------
// Marker
//-------------
Map.prototype.addMarker = function(markerOptions, callback) {
var self = this;
markerOptions = common.markerOptionsFilter(markerOptions);
//------------------------------------
// Generate a makrer instance at once.
//------------------------------------
markerOptions.icon = markerOptions.icon || {};
var link;
if (typeof markerOptions.icon === 'string') {
if (markerOptions.icon.indexOf('://') === -1 &&
markerOptions.icon.indexOf('.') === 0) {
link = document.createElement('a');
link.href = markerOptions.icon;
markerOptions.icon = link.protocol+'//'+link.host+link.pathname + link.search;
link = undefined;
}
} else if (typeof markerOptions.icon === 'object' && typeof markerOptions.icon.url === 'string') {
if (markerOptions.icon.url.indexOf('://') === -1 &&
markerOptions.icon.url.indexOf('.') === 0) {
link = document.createElement('a');
link.href = markerOptions.icon.url;
markerOptions.icon.url = link.protocol+'//'+link.host+link.pathname + link.search;
link = undefined;
}
}
var marker = new Marker(self, markerOptions, exec);
var markerId = marker.getId();
self.MARKERS[markerId] = marker;
self.OVERLAYS[markerId] = marker;
marker.one(markerId + '_remove', function() {
delete self.MARKERS[markerId];
delete self.OVERLAYS[markerId];
marker.destroy();
marker = undefined;
});
if (typeof markerOptions.anchor === 'object' &&
'x' in markerOptions.anchor && 'y' in markerOptions.anchor) {
markerOptions.anchor = [markerOptions.anchor.x, markerOptions.anchor.y];
}
self.exec.call(self, function(result) {
if (marker) {
markerOptions.icon.size = markerOptions.icon.size || {};
markerOptions.icon.size.width = markerOptions.icon.size.width || result.width;
markerOptions.icon.size.height = markerOptions.icon.size.height || result.height;
markerOptions.icon.anchor = markerOptions.icon.anchor || [markerOptions.icon.size.width / 2, markerOptions.icon.size.height];
if (!markerOptions.infoWindowAnchor) {
markerOptions.infoWindowAnchor = [markerOptions.icon.size.width / 2, 0];
}
marker._privateInitialize(markerOptions);
delete marker._privateInitialize;
if (typeof callback === 'function') {
callback.call(self, marker);
}
}
}, self.errorHandler, self.__pgmId, 'loadPlugin', ['Marker', markerOptions, marker.hashCode]);
return marker;
};
//------------------
// Marker cluster
//------------------
Map.prototype.addMarkerCluster = function(markerClusterOptions, callback) {
var self = this;
if (typeof markerClusterOptions === 'function') {
callback = markerClusterOptions;
markerClusterOptions = null;
}
markerClusterOptions = markerClusterOptions || {};
var positionList = markerClusterOptions.markers.map(function(marker) {
return marker.position;
});
var markerCluster = new MarkerCluster(self, {
'icons': markerClusterOptions.icons,
//'markerMap': markerMap,
'idxCount': positionList.length + 1,
'maxZoomLevel': Math.min(markerClusterOptions.maxZoomLevel || 15, 18),
'debug': markerClusterOptions.debug === true,
'boundsDraw': common.defaultTrueOption(markerClusterOptions.boundsDraw)
}, exec);
var markerClusterId = markerCluster.getId();
self.OVERLAYS[markerClusterId] = markerCluster;
self.exec.call(self, function(result) {
result.geocellList.forEach(function(geocell, idx) {
var markerOptions = markerClusterOptions.markers[idx];
markerOptions = common.markerOptionsFilter(markerOptions);
markerOptions._cluster = {
isRemoved: false,
isAdded: false,
geocell: geocell
};
markerCluster.addMarker(markerOptions);
//self.MARKERS[marker.getId()] = marker;
//self.OVERLAYS[marker.getId()] = marker;
});
markerCluster.one('remove', function() {
delete self.OVERLAYS[result.__pgmId];
/*
result.geocellList.forEach(function(geocell, idx) {
var markerOptions = markerClusterOptions.markers[idx];
var markerId = result.__pgmId + '-' + (markerOptions.__pgmId || 'marker_' + idx);
var marker = self.MARKERS[markerId];
if (marker) {
marker.off();
}
//delete self.MARKERS[markerId];
delete self.OVERLAYS[markerId];
});
*/
markerCluster.destroy();
});
markerCluster._privateInitialize();
delete markerCluster._privateInitialize;
markerCluster._triggerRedraw.call(markerCluster, {
force: true
});
if (typeof callback === 'function') {
callback.call(self, markerCluster);
}
}, self.errorHandler, self.__pgmId, 'loadPlugin', ['MarkerCluster', {
'positionList': positionList,
'debug': markerClusterOptions.debug === true
}, markerCluster.hashCode]);
return markerCluster;
};
/*****************************************************************************
* Callbacks from the native side
*****************************************************************************/
Map.prototype._onSyncInfoWndPosition = function(eventName, points) {
this.set('infoPosition', points);
};
Map.prototype._onMapEvent = function(eventName) {
if (!this._isReady) {
return;
}
var args = [eventName];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
this.trigger.apply(this, args);
};
Map.prototype._onMarkerEvent = function(eventName, markerId, position) {
var self = this;
var marker = self.MARKERS[markerId] || null;
if (marker) {
marker.set('position', position);
if (eventName === event.INFO_OPEN) {
marker.set('isInfoWindowVisible', true);
}
if (eventName === event.INFO_CLOSE) {
marker.set('isInfoWindowVisible', false);
}
marker.trigger(eventName, position, marker);
}
};
Map.prototype._onClusterEvent = function(eventName, markerClusterId, clusterId, position) {
var self = this;
var markerCluster = self.OVERLAYS[markerClusterId] || null;
if (markerCluster) {
if (/^marker_/i.test(clusterId)) {
// regular marker
var marker = markerCluster.getMarkerById(clusterId);
if (eventName === event.MARKER_CLICK) {
markerCluster.trigger(eventName, position, marker);
} else {
if (eventName === event.INFO_OPEN) {
marker.set('isInfoWindowVisible', true);
}
if (eventName === event.INFO_CLOSE) {
marker.set('isInfoWindowVisible', false);
}
}
marker.trigger(eventName, position, marker);
} else {
// cluster marker
var cluster = markerCluster._getClusterByClusterId(clusterId);
if (cluster) {
markerCluster.trigger(eventName, cluster);
} else {
console.log('-----> This is remained cluster icon : ' + clusterId);
}
}
}
};
Map.prototype._onOverlayEvent = function(eventName, overlayId) {
var self = this;
var overlay = self.OVERLAYS[overlayId] || null;
if (overlay) {
var args = [eventName];
for (var i = 2; i < arguments.length; i++) {
args.push(arguments[i]);
}
args.push(overlay); // for ionic
overlay.trigger.apply(overlay, args);
}
};
Map.prototype.getCameraTarget = function() {
return this.get('camera_target');
};
Map.prototype.getCameraZoom = function() {
return this.get('camera_zoom');
};
Map.prototype.getCameraTilt = function() {
return this.get('camera_tilt');
};
Map.prototype.getCameraBearing = function() {
return this.get('camera_bearing');
};
Map.prototype._onCameraEvent = function(eventName, cameraPosition) {
this.set('camera', cameraPosition);
this.set('camera_target', cameraPosition.target);
this.set('camera_zoom', cameraPosition.zoom);
this.set('camera_bearing', cameraPosition.bearing);
this.set('camera_tilt', cameraPosition.viewAngle || cameraPosition.tilt);
this.set('camera_northeast', cameraPosition.northeast);
this.set('camera_southwest', cameraPosition.southwest);
this.set('camera_nearLeft', cameraPosition.nearLeft);
this.set('camera_nearRight', cameraPosition.nearRight);
this.set('camera_farLeft', cameraPosition.farLeft);
this.set('camera_farRight', cameraPosition.farRight);
if (this._isReady) {
this.trigger(eventName, cameraPosition, this);
}
};
module.exports = Map;