thingsboard-aplcache

Merge conflicts

1/30/2019 3:26:00 PM

Details

diff --git a/ui/src/app/widget/lib/google-map.js b/ui/src/app/widget/lib/google-map.js
index ccf931a..3f7be4f 100644
--- a/ui/src/app/widget/lib/google-map.js
+++ b/ui/src/app/widget/lib/google-map.js
@@ -314,6 +314,48 @@ export default class TbGoogleMap {
         polyline.setMap(null);
     }
 
+
+	createPolygon(latLangs, settings) {
+		let polygon = new google.maps.Polygon({
+			map: this.map,
+			paths: latLangs,
+			strokeColor: settings.polygonStrokeColor,
+			strokeOpacity: settings.polygonStrokeColor,
+			fillColor: settings.polygonColor,
+			fillOpacity: settings.polygonOpacity,
+			strokeWeight: settings.polygonStrokeWeight
+		});
+		return polygon;
+	}
+	/* eslint-disable no-undef */
+
+	removePolygon (polygon) {
+		polygon.setMap(null);
+	}
+
+	/* eslint-disable no-undef,no-unused-vars */
+	updatePolygonColor (polygon, settings, color) {
+		let options = {
+			paths: polygon.getPaths(),
+			map: this.map,
+			strokeColor: color,
+			fillColor: color,
+			strokeWeight: settings.polygonStrokeWeight
+		}
+
+	}
+	/* eslint-disable no-undef ,no-unused-vars*/
+
+
+	getPolygonLatLngs(polygon) {
+		return polygon.getPaths().getArray();
+	}
+
+	setPolygonLatLngs(polygon, latLngs) {
+		polygon.setPaths(latLngs);
+	}
+
+
     /* eslint-disable no-undef */
     fitBounds(bounds) {
         if (this.dontFitMapBounds && this.defaultZoomLevel) {
diff --git a/ui/src/app/widget/lib/image-map.js b/ui/src/app/widget/lib/image-map.js
index 74f5841..3dbf536 100644
--- a/ui/src/app/widget/lib/image-map.js
+++ b/ui/src/app/widget/lib/image-map.js
@@ -369,6 +369,21 @@ export default class TbImageMap {
     removePolyline(/*polyline*/) {
     }
 
+	createPolygon(/*latLangs, settings*/) {
+	}
+
+	removePolygon(/*latLangs, settings*/) {
+	}
+
+	updatePolygonColor(/*latLangs, settings*/) {
+	}
+
+	getPolygonLatLngs(/*latLangs, settings*/) {
+	}
+
+	setPolygonLatLngs(/*latLangs, settings*/) {
+	}
+
     fitBounds() {
     }
 
diff --git a/ui/src/app/widget/lib/map-widget2.js b/ui/src/app/widget/lib/map-widget2.js
index d969c48..fed9cb5 100644
--- a/ui/src/app/widget/lib/map-widget2.js
+++ b/ui/src/app/widget/lib/map-widget2.js
@@ -23,1074 +23,1207 @@ import TbTencentMap from './tencent-map';
 import {processPattern, arraysEqual, toLabelValueMap, fillPattern, fillPatternWithActions} from './widget-utils';
 
 export default class TbMapWidgetV2 {
-    constructor(mapProvider, drawRoutes, ctx, useDynamicLocations, $element) {
-        var tbMap = this;
-        this.ctx = ctx;
-        this.mapProvider = mapProvider;
-        if (!$element) {
-            $element = ctx.$container;
-        }
-        this.utils = ctx.$scope.$injector.get('utils');
-        this.drawRoutes = drawRoutes;
-        this.markers = [];
-        if (this.drawRoutes) {
-            this.polylines = [];
-        }
-
-        this.locationSettings = {};
-
-        var settings = ctx.settings;
-
-        this.callbacks = {};
-        this.callbacks.onLocationClick = function(){};
-
-        if (settings.defaultZoomLevel) {
-            if (settings.defaultZoomLevel > 0 && settings.defaultZoomLevel < 21) {
-                this.defaultZoomLevel = Math.floor(settings.defaultZoomLevel);
-            }
-        }
-
-        this.dontFitMapBounds = settings.fitMapBounds === false;
-
-        if (!useDynamicLocations) {
-            this.subscription = this.ctx.defaultSubscription;
-        }
-
-        this.configureLocationsSettings();
-
-        var minZoomLevel = this.drawRoutes ? 18 : 15;
-
-
-
-
-        var initCallback = function() {
-            tbMap.update();
-            tbMap.resize();
-        };
-
-        this.ctx.$scope.onTooltipAction = function(event, actionName, dsIndex) {
-            tbMap.onTooltipAction(event, actionName, dsIndex);
-        };
-        this.tooltipActionsMap = {};
-        var descriptors = this.ctx.actionsApi.getActionDescriptors('tooltipAction');
-        descriptors.forEach(function (descriptor) {
-            tbMap.tooltipActionsMap[descriptor.name] = descriptor;
-        });
-
-        if (mapProvider === 'google-map') {
-            this.map = new TbGoogleMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType);
-        } else if (mapProvider === 'openstreet-map') {
-            this.map = new TbOpenStreetMap($element, this.utils,  initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.mapProvider);
-        } else if (mapProvider === 'image-map') {
-            this.map = new TbImageMap(this.ctx, $element, this.utils, initCallback,
-                settings.mapImageUrl,
-                settings.posFunction,
-                settings.imageEntityAlias,
-                settings.imageUrlAttribute);
-        } else if (mapProvider === 'tencent-map') {
-            this.map = new TbTencentMap($element,this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.tmApiKey, settings.tmDefaultMapType);
-        }
-
-
-        tbMap.initBounds = true;
-    }
-
-    setCallbacks(callbacks) {
-        Object.assign(this.callbacks, callbacks);
-    }
-
-    clearLocations() {
-        if (this.locations) {
-            var tbMap = this;
-            this.locations.forEach(function(location) {
-                if (location.marker) {
-                    tbMap.map.removeMarker(location.marker);
-                }
-                if (location.polyline) {
-                    tbMap.map.removePolyline(location.polyline);
-                }
-            });
-            this.locations = null;
-            this.markers = [];
-            if (this.drawRoutes) {
-                this.polylines = [];
-            }
-        }
-    }
-
-    setSubscription(subscription) {
-        this.subscription = subscription;
-        this.clearLocations();
-    }
-
-    configureLocationsSettings() {
-
-        if (this.mapProvider  == 'image-map') {
-            this.locationSettings.latKeyName = this.ctx.settings.xPosKeyName || 'xPos';
-            this.locationSettings.lngKeyName = this.ctx.settings.yPosKeyName || 'yPos';
-            this.locationSettings.markerOffsetX = angular.isDefined(this.ctx.settings.markerOffsetX) ? this.ctx.settings.markerOffsetX : 0.5;
-            this.locationSettings.markerOffsetY = angular.isDefined(this.ctx.settings.markerOffsetY) ? this.ctx.settings.markerOffsetY : 1;
-        } else {
-            this.locationSettings.latKeyName = this.ctx.settings.latKeyName || 'latitude';
-            this.locationSettings.lngKeyName = this.ctx.settings.lngKeyName || 'longitude';
-        }
-
-        this.locationSettings.tooltipPattern = this.ctx.settings.tooltipPattern || "<b>${entityName}</b><br/><br/><b>Latitude:</b> ${"+this.locationSettings.latKeyName+":7}<br/><b>Longitude:</b> ${"+this.locationSettings.lngKeyName+":7}";
-
-        this.locationSettings.showLabel = this.ctx.settings.showLabel !== false;
-        this.locationSettings.displayTooltip = this.ctx.settings.showTooltip !== false;
-        this.locationSettings.autocloseTooltip = this.ctx.settings.autocloseTooltip !== false;
-        this.locationSettings.labelColor = this.ctx.widgetConfig.color || '#000000',
-        this.locationSettings.label = this.ctx.settings.label || "${entityName}";
-        this.locationSettings.color = this.ctx.settings.color ? tinycolor(this.ctx.settings.color).toHexString() : "#FE7569";
-
-        this.locationSettings.useLabelFunction = this.ctx.settings.useLabelFunction === true;
-        if (angular.isDefined(this.ctx.settings.labelFunction) && this.ctx.settings.labelFunction.length > 0) {
-            try {
-                this.locationSettings.labelFunction = new Function('data, dsData, dsIndex', this.ctx.settings.labelFunction);
-            } catch (e) {
-                this.locationSettings.labelFunction = null;
-            }
-        }
-
-        this.locationSettings.useTooltipFunction = this.ctx.settings.useTooltipFunction === true;
-        if (angular.isDefined(this.ctx.settings.tooltipFunction) && this.ctx.settings.tooltipFunction.length > 0) {
-            try {
-                this.locationSettings.tooltipFunction = new Function('data, dsData, dsIndex', this.ctx.settings.tooltipFunction);
-            } catch (e) {
-                this.locationSettings.tooltipFunction = null;
-            }
-        }
-
-        this.locationSettings.useColorFunction = this.ctx.settings.useColorFunction === true;
-        if (angular.isDefined(this.ctx.settings.colorFunction) && this.ctx.settings.colorFunction.length > 0) {
-            try {
-                this.locationSettings.colorFunction = new Function('data, dsData, dsIndex', this.ctx.settings.colorFunction);
-            } catch (e) {
-                this.locationSettings.colorFunction = null;
-            }
-        }
-
-        this.locationSettings.useMarkerImageFunction = this.ctx.settings.useMarkerImageFunction === true;
-        if (angular.isDefined(this.ctx.settings.markerImageFunction) && this.ctx.settings.markerImageFunction.length > 0) {
-            try {
-                this.locationSettings.markerImageFunction = new Function('data, images, dsData, dsIndex', this.ctx.settings.markerImageFunction);
-            } catch (e) {
-                this.locationSettings.markerImageFunction = null;
-            }
-        }
-
-        this.locationSettings.markerImages = this.ctx.settings.markerImages || [];
-
-        if (!this.locationSettings.useMarkerImageFunction &&
-            angular.isDefined(this.ctx.settings.markerImage) &&
-            this.ctx.settings.markerImage.length > 0) {
-            this.locationSettings.useMarkerImage = true;
-            var url = this.ctx.settings.markerImage;
-            var size = this.ctx.settings.markerImageSize || 34;
-            this.locationSettings.currentImage = {
-                url: url,
-                size: size
-            };
-        }
-
-        if (this.drawRoutes) {
-            this.locationSettings.strokeWeight = this.ctx.settings.strokeWeight || 2;
-            this.locationSettings.strokeOpacity = this.ctx.settings.strokeOpacity || 1.0;
-        }
-    }
-
-    onTooltipAction(event, actionName, dsIndex) {
-        var descriptor = this.tooltipActionsMap[actionName];
-        if (descriptor) {
-            var datasource = this.subscription.datasources[dsIndex];
-            var entityId = {};
-            entityId.id = datasource.entityId;
-            entityId.entityType = datasource.entityType;
-            var entityName = datasource.entityName;
-            this.ctx.actionsApi.handleWidgetAction(event, descriptor, entityId, entityName);
-        }
-    }
-
-    update() {
-
-        var tbMap = this;
-
-        function updateLocationLabel(location, dataMap) {
-            if (location.settings.showLabel) {
-                if (location.settings.useLabelFunction && location.settings.labelFunction) {
-                    try {
-                        location.settings.label = location.settings.labelFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
-                    } catch (e) {
-                        location.settings.label = null;
-                    }
-                    if (location.settings.label) {
-                        var datasources = tbMap.subscription.datasources;
-                        location.settings.label = tbMap.utils.createLabelFromDatasource(datasources[location.dsIndex], location.settings.label);
-                        location.settings.labelReplaceInfo = processPattern(location.settings.label, datasources, location.dsIndex);
-                        location.settings.labelText = location.settings.label;
-                    }
-                }
-                if (location.settings.labelReplaceInfo.variables.length) {
-                    location.settings.labelText = fillPattern(location.settings.label,
-                        location.settings.labelReplaceInfo, tbMap.subscription.data);
-                }
-                tbMap.map.updateMarkerLabel(location.marker, location.settings);
-            }
-        }
-
-
-        function calculateLocationColor(location, dataMap) {
-            if (location.settings.useColorFunction && location.settings.colorFunction) {
-                var color;
-                try {
-                    color = location.settings.colorFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
-                } catch (e) {/**/}
-                if (!color) {
-                    color = '#FE7569';
-                }
-                return tinycolor(color).toHexString();
-            } else {
-                return location.settings.color;
-            }
-        }
-
-        function updateLocationColor(location, color, image) {
-            if (!location.settings.calculatedColor || location.settings.calculatedColor !== color) {
-                if (!location.settings.useMarkerImage && !image) {
-                    tbMap.map.updateMarkerColor(location.marker, color);
-                }
-                if (location.polyline) {
-                    tbMap.map.updatePolylineColor(location.polyline, location.settings, color);
-                }
-                location.settings.calculatedColor = color;
-            }
-        }
-
-        function calculateLocationMarkerImage(location, dataMap) {
-            if (location.settings.useMarkerImageFunction && location.settings.markerImageFunction) {
-                var image = null;
-                try {
-                    image = location.settings.markerImageFunction(dataMap.dataMap, location.settings.markerImages, dataMap.dsDataMap, location.dsIndex);
-                } catch (e) {
-                    image = null;
-                }
-                return image;
-            } else {
-                return null;
-            }
-        }
-
-        function updateLocationMarkerIcon(location, image) {
-            if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
-                location.settings.currentImage = image;
-                tbMap.map.updateMarkerIcon(location.marker, location.settings);
-            }
-        }
-
-        function updateLocationStyle(location, dataMap) {
-            updateLocationLabel(location, dataMap);
-            var color = calculateLocationColor(location, dataMap);
-            var image = calculateLocationMarkerImage(location, dataMap);
-            updateLocationColor(location, color, image);
-            updateLocationMarkerIcon(location, image);
-        }
-
-        function createOrUpdateLocationMarker(location, markerLocation, dataMap) {
-            var changed = false;
-            if (!location.marker) {
-                var image = calculateLocationMarkerImage(location, dataMap);
-                if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
-                    location.settings.currentImage = image;
-                }
-                location.marker = tbMap.map.createMarker(markerLocation, location.dsIndex, location.settings,
-                    function (event) {
-                        tbMap.callbacks.onLocationClick(location);
-                        locationRowClick(event, location);
-                    }, [location.dsIndex]);
-                tbMap.markers.push(location.marker);
-                changed = true;
-            } else {
-                var prevPosition = tbMap.map.getMarkerPosition(location.marker);
-                if (!prevPosition.equals(markerLocation)) {
-                    tbMap.map.setMarkerPosition(location.marker, markerLocation);
-                    changed = true;
-                }
-            }
-            return changed;
-        }
-
-        function locationRowClick($event, location) {
-            var descriptors = tbMap.ctx.actionsApi.getActionDescriptors('markerClick');
-            if (descriptors.length) {
-                var datasource = tbMap.subscription.datasources[location.dsIndex];
-                var entityId = {};
-                entityId.id = datasource.entityId;
-                entityId.entityType = datasource.entityType;
-                var entityName = datasource.entityName;
-                tbMap.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName);
-            }
-        }
-
-        function updateLocation(location, data, dataMap) {
-            var locationChanged = false;
-            if (location.latIndex > -1 && location.lngIndex > -1) {
-                var latData = data[location.latIndex].data;
-                var lngData = data[location.lngIndex].data;
-                var lat, lng, latLng;
-                if (latData.length > 0 && lngData.length > 0) {
-                    if (tbMap.drawRoutes) {
-                        // Create or update route
-                        var latLngs = [];
-                        for (var i = 0; i < latData.length; i++) {
-                            lat = latData[i][1];
-                            lng = lngData[i][1];
-                            if (angular.isDefined(lat) && lat != null && angular.isDefined(lng) && lng != null) {
-                                latLng = tbMap.map.createLatLng(lat, lng);
-                                if (i == 0 || !latLngs[latLngs.length - 1].equals(latLng)) {
-                                    latLngs.push(latLng);
-                                }
-                            }
-                        }
-                        if (latLngs.length > 0) {
-                            var markerLocation = latLngs[latLngs.length - 1];
-                            createOrUpdateLocationMarker(location, markerLocation, dataMap);
-                        }
-                        if (!location.polyline) {
-                            location.polyline = tbMap.map.createPolyline(latLngs, location.settings);
-                            tbMap.polylines.push(location.polyline);
-                            locationChanged = true;
-                        } else {
-                            var prevPath = tbMap.map.getPolylineLatLngs(location.polyline);
-                            if (!prevPath || !arraysEqual(prevPath, latLngs)) {
-                                tbMap.map.setPolylineLatLngs(location.polyline, latLngs);
-                                locationChanged = true;
-                            }
-                        }
-                    } else {
-                        // Create or update marker
-                        lat = latData[latData.length - 1][1];
-                        lng = lngData[lngData.length - 1][1];
-                        if (angular.isDefined(lat) && lat != null && angular.isDefined(lng) && lng != null) {
-                            latLng = tbMap.map.createLatLng(lat, lng);
-                            if (createOrUpdateLocationMarker(location, latLng, dataMap)) {
-                                locationChanged = true;
-                            }
-                        }
-                    }
-                    if (location.marker) {
-                        updateLocationStyle(location, dataMap);
-                    }
-                }
-            }
-            return locationChanged;
-        }
-
-        function loadLocations(data, datasources) {
-            var bounds = tbMap.map.createBounds();
-            tbMap.locations = [];
-            var dataMap = toLabelValueMap(data, datasources);
-            var currentDatasource = null;
-            var currentDatasourceIndex = -1;
-            var latIndex = -1;
-            var lngIndex = -1;
-
-            for (var i=0;i<data.length;i++) {
-                var dataKeyData = data[i];
-                var dataKey = dataKeyData.dataKey;
-                if (dataKeyData.datasource != currentDatasource) {
-                    currentDatasource = dataKeyData.datasource;
-                    currentDatasourceIndex++;
-                    latIndex = -1;
-                    lngIndex = -1;
-                }
-                var nameToCheck;
-                if (dataKey.locationAttrName) {
-                    nameToCheck = dataKey.locationAttrName;
-                } else {
-                    nameToCheck = dataKey.label;
-                }
-                if (nameToCheck === tbMap.locationSettings.latKeyName) {
-                    latIndex = i;
-                } else if (nameToCheck === tbMap.locationSettings.lngKeyName) {
-                    lngIndex = i;
-                }
-                if (latIndex > -1 && lngIndex > -1) {
-                    var location = {
-                        latIndex: latIndex,
-                        lngIndex: lngIndex,
-                        dsIndex: currentDatasourceIndex,
-                        settings: angular.copy(tbMap.locationSettings)
-                    };
-                    if (location.settings.showLabel) {
-                        location.settings.label = tbMap.utils.createLabelFromDatasource(currentDatasource, location.settings.label);
-                        location.settings.labelReplaceInfo = processPattern(location.settings.label, datasources, currentDatasourceIndex);
-                        location.settings.labelText = location.settings.label;
-                    }
-                    if (location.settings.displayTooltip) {
-                        location.settings.tooltipPattern = tbMap.utils.createLabelFromDatasource(currentDatasource, location.settings.tooltipPattern);
-                        location.settings.tooltipReplaceInfo = processPattern(location.settings.tooltipPattern, datasources, currentDatasourceIndex);
-                    }
-
-                    tbMap.locations.push(location);
-                    updateLocation(location, data, dataMap);
-                    if (location.polyline) {
-                        tbMap.map.extendBounds(bounds, location.polyline);
-                    } else if (location.marker) {
-                        tbMap.map.extendBoundsWithMarker(bounds, location.marker);
-                    }
-                    latIndex = -1;
-                    lngIndex = -1;
-                }
-
-            }
-            tbMap.map.fitBounds(bounds);
-        }
-
-        function updateLocations(data, datasources) {
-
-            var locationsChanged = false;
-            var bounds = tbMap.map.createBounds();
-            var dataMap = toLabelValueMap(data, datasources);
-            for (var p = 0; p < tbMap.locations.length; p++) {
-                var location = tbMap.locations[p];
-                locationsChanged |= updateLocation(location, data, dataMap);
-                if (location.polyline) {
-                    tbMap.map.extendBounds(bounds, location.polyline);
-                } else if (location.marker) {
-                    tbMap.map.extendBoundsWithMarker(bounds, location.marker);
-                }
-            }
-             if (locationsChanged && tbMap.initBounds) {
-                tbMap.initBounds = !datasources.every(
-                    function (ds) {
-                        return ds.dataReceived === true;
-                    });
-                tbMap.map.fitBounds(bounds);
-             }
-        }
-
-        function createTooltipContent(tooltip, data, datasources) {
-            var content;
-            var settings = tooltip.locationSettings;
-            if (settings.useTooltipFunction && settings.tooltipFunction) {
-                var dataMap = toLabelValueMap(data, datasources);
-                try {
-                    settings.tooltipPattern = settings.tooltipFunction(dataMap.dataMap, dataMap.dsDataMap, tooltip.dsIndex);
-                } catch (e) {
-                    settings.tooltipPattern = null;
-                }
-                if (settings.tooltipPattern) {
-                    settings.tooltipPattern = tbMap.utils.createLabelFromDatasource(datasources[tooltip.dsIndex], settings.tooltipPattern);
-                    settings.tooltipReplaceInfo = processPattern(settings.tooltipPattern, datasources, tooltip.dsIndex);
-                }
-            }
-            content = fillPattern(settings.tooltipPattern, settings.tooltipReplaceInfo, data);
-            return fillPatternWithActions(content, 'onTooltipAction', tooltip.markerArgs);
-        }
-        if (this.map && this.map.inited() && this.subscription) {
-            if (this.subscription.data) {
-                if (!this.locations) {
-                    loadLocations(this.subscription.data, this.subscription.datasources);
-                } else {
-                    updateLocations(this.subscription.data, this.subscription.datasources);
-                }
-                var tooltips = this.map.getTooltips();
-                for (var t = 0; t < tooltips.length; t++) {
-                    var tooltip = tooltips[t];
-                    var text = createTooltipContent(tooltip, this.subscription.data, this.subscription.datasources);
-                    tooltip.popup.setContent(text);
-                }
-            }
-        }
-    }
-
-    resize() {
-        if (this.map && this.map.inited()) {
-            this.map.invalidateSize();
-            if (this.locations && this.locations.length > 0) {
-                var bounds = this.map.createBounds();
-                for (var m = 0; m < this.markers.length; m++) {
-                    this.map.extendBoundsWithMarker(bounds, this.markers[m]);
-                }
-                if (this.polylines) {
-                    for (var p = 0; p < this.polylines.length; p++) {
-                        this.map.extendBounds(bounds, this.polylines[p]);
-                    }
-                }
-                this.map.fitBounds(bounds);
-            }
-        }
-    }
-
-    static settingsSchema(mapProvider, drawRoutes) {
-        var schema;
-        if (mapProvider === 'google-map') {
-            schema = angular.copy(googleMapSettingsSchema);
-        } else if (mapProvider === 'openstreet-map') {
-            schema = angular.copy(openstreetMapSettingsSchema);
-        } else if (mapProvider === 'image-map') {
-            return imageMapSettingsSchema;
-        } else if (mapProvider === 'tencent-map') {
-            schema = angular.copy(tencentMapSettingsSchema);
-        }
-        angular.merge(schema.schema.properties, commonMapSettingsSchema.schema.properties);
-        schema.schema.required = schema.schema.required.concat(commonMapSettingsSchema.schema.required);
-        schema.form = schema.form.concat(commonMapSettingsSchema.form);
-        if (drawRoutes) {
-            angular.merge(schema.schema.properties, routeMapSettingsSchema.schema.properties);
-            schema.schema.required = schema.schema.required.concat(routeMapSettingsSchema.schema.required);
-            schema.form = schema.form.concat(routeMapSettingsSchema.form);
-        }
-        return schema;
-    }
-
-    static dataKeySettingsSchema(/*mapProvider*/) {
-        return {};
-    }
-
-    static actionSources() {
-        return {
-            'markerClick': {
-                name: 'widget-action.marker-click',
-                multiple: false
-            },
-            'tooltipAction': {
-                name: 'widget-action.tooltip-tag-action',
-                multiple: true
-            }
-        };
-    }
+
+	constructor(mapProvider, drawRoutes, ctx, useDynamicLocations, $element) {
+		var tbMap = this;
+		this.ctx = ctx;
+		this.mapProvider = mapProvider;
+		if (!$element) {
+			$element = ctx.$container;
+		}
+		this.utils = ctx.$scope.$injector.get('utils');
+		this.drawRoutes = drawRoutes;
+		this.markers = [];
+		this.polygons = [];
+		if (this.drawRoutes) {
+			this.polylines = [];
+		}
+
+		this.locationSettings = {};
+
+		var settings = ctx.settings;
+
+		this.callbacks = {};
+		this.callbacks.onLocationClick = function () {
+		};
+
+		if (settings.defaultZoomLevel) {
+			if (settings.defaultZoomLevel > 0 && settings.defaultZoomLevel < 21) {
+				this.defaultZoomLevel = Math.floor(settings.defaultZoomLevel);
+			}
+		}
+
+		this.dontFitMapBounds = settings.fitMapBounds === false;
+
+		if (!useDynamicLocations) {
+			this.subscription = this.ctx.defaultSubscription;
+		}
+
+		this.configureLocationsSettings();
+
+		var minZoomLevel = this.drawRoutes ? 18 : 15;
+
+
+		var initCallback = function () {
+			tbMap.update();
+			tbMap.resize();
+		};
+
+		this.ctx.$scope.onTooltipAction = function (event, actionName, dsIndex) {
+			tbMap.onTooltipAction(event, actionName, dsIndex);
+		};
+		this.tooltipActionsMap = {};
+		var descriptors = this.ctx.actionsApi.getActionDescriptors('tooltipAction');
+		descriptors.forEach(function (descriptor) {
+			tbMap.tooltipActionsMap[descriptor.name] = descriptor;
+		});
+
+		if (mapProvider === 'google-map') {
+			this.map = new TbGoogleMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType);
+		} else if (mapProvider === 'openstreet-map') {
+			this.map = new TbOpenStreetMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.mapProvider);
+		} else if (mapProvider === 'image-map') {
+			this.map = new TbImageMap(this.ctx, $element, this.utils, initCallback,
+				settings.mapImageUrl,
+				settings.posFunction,
+				settings.imageEntityAlias,
+				settings.imageUrlAttribute);
+		} else if (mapProvider === 'tencent-map') {
+			this.map = new TbTencentMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.tmApiKey, settings.tmDefaultMapType);
+		}
+
+
+		tbMap.initBounds = true;
+	}
+
+	setCallbacks(callbacks) {
+		Object.assign(this.callbacks, callbacks);
+	}
+
+	clearLocations() {
+		if (this.locations) {
+			var tbMap = this;
+			this.locations.forEach(function (location) {
+				if (location.marker) {
+					tbMap.map.removeMarker(location.marker);
+				}
+				if (location.polyline) {
+					tbMap.map.removePolyline(location.polyline);
+				}
+				if (location.polygon) {
+					tbMap.map.removePolygon(location.polygon);
+				}
+			});
+			this.locations = null;
+			this.markers = [];
+			this.polygons = [];
+			if (this.drawRoutes) {
+				this.polylines = [];
+			}
+		}
+	}
+
+	setSubscription(subscription) {
+		this.subscription = subscription;
+		this.clearLocations();
+	}
+
+	configureLocationsSettings() {
+
+		if (this.mapProvider == 'image-map') {
+			this.locationSettings.latKeyName = this.ctx.settings.xPosKeyName || 'xPos';
+			this.locationSettings.lngKeyName = this.ctx.settings.yPosKeyName || 'yPos';
+			this.locationSettings.markerOffsetX = angular.isDefined(this.ctx.settings.markerOffsetX) ? this.ctx.settings.markerOffsetX : 0.5;
+			this.locationSettings.markerOffsetY = angular.isDefined(this.ctx.settings.markerOffsetY) ? this.ctx.settings.markerOffsetY : 1;
+		} else {
+			this.locationSettings.latKeyName = this.ctx.settings.latKeyName || 'latitude';
+			this.locationSettings.lngKeyName = this.ctx.settings.lngKeyName || 'longitude';
+			this.locationSettings.polygonKeyName = this.ctx.settings.polygonKeyName || 'coordinates';
+		}
+
+		this.locationSettings.tooltipPattern = this.ctx.settings.tooltipPattern || "<b>${entityName}</b><br/><br/><b>Latitude:</b> ${" + this.locationSettings.latKeyName + ":7}<br/><b>Longitude:</b> ${" + this.locationSettings.lngKeyName + ":7}";
+
+		this.locationSettings.showLabel = this.ctx.settings.showLabel !== false;
+		this.locationSettings.displayTooltip = this.ctx.settings.showTooltip !== false;
+		this.locationSettings.autocloseTooltip = this.ctx.settings.autocloseTooltip !== false;
+		this.locationSettings.showPolygon = this.ctx.settings.showPolygon !== false;
+		this.locationSettings.labelColor = this.ctx.widgetConfig.color || '#000000';
+		this.locationSettings.label = this.ctx.settings.label || "${entityName}";
+		this.locationSettings.color = this.ctx.settings.color ? tinycolor(this.ctx.settings.color).toHexString() : "#FE7569";
+		this.locationSettings.polygonColor = this.ctx.settings.polygonColor ? tinycolor(this.ctx.settings.polygonColor).toHexString() : "#0000ff";
+		this.locationSettings.polygonStrokeColor = this.ctx.settings.polygonStrokeColor ? tinycolor(this.ctx.settings.polygonStrokeColor).toHexString() : "#fe0001";
+		this.locationSettings.polygonOpacity = angular.isDefined(this.ctx.settings.polygonOpacity) ? this.ctx.settings.polygonOpacity : 0.5;
+		this.locationSettings.polygonStrokeOpacity = angular.isDefined(this.ctx.settings.polygonStrokeOpacity) ? this.ctx.settings.polygonStrokeOpacity : 1;
+		this.locationSettings.polygonStrokeWeight = angular.isDefined(this.ctx.settings.polygonStrokeWeight) ? this.ctx.settings.polygonStrokeWeight : 1;
+
+		this.locationSettings.useLabelFunction = this.ctx.settings.useLabelFunction === true;
+		if (angular.isDefined(this.ctx.settings.labelFunction) && this.ctx.settings.labelFunction.length > 0) {
+			try {
+				this.locationSettings.labelFunction = new Function('data, dsData, dsIndex', this.ctx.settings.labelFunction);
+			} catch (e) {
+				this.locationSettings.labelFunction = null;
+			}
+		}
+
+		this.locationSettings.useTooltipFunction = this.ctx.settings.useTooltipFunction === true;
+		if (angular.isDefined(this.ctx.settings.tooltipFunction) && this.ctx.settings.tooltipFunction.length > 0) {
+			try {
+				this.locationSettings.tooltipFunction = new Function('data, dsData, dsIndex', this.ctx.settings.tooltipFunction);
+			} catch (e) {
+				this.locationSettings.tooltipFunction = null;
+			}
+		}
+
+		this.locationSettings.useColorFunction = this.ctx.settings.useColorFunction === true;
+		if (angular.isDefined(this.ctx.settings.colorFunction) && this.ctx.settings.colorFunction.length > 0) {
+			try {
+				this.locationSettings.colorFunction = new Function('data, dsData, dsIndex', this.ctx.settings.colorFunction);
+			} catch (e) {
+				this.locationSettings.colorFunction = null;
+			}
+		}
+		this.locationSettings.usePolygonColorFunction = this.ctx.settings.usePolygonColorFunction === true;
+		if (angular.isDefined(this.ctx.settings.polygonColorFunction) && this.ctx.settings.polygonColorFunction.length > 0) {
+			try {
+				this.locationSettings.polygonColorFunction = new Function('data, dsData, dsIndex', this.ctx.settings.polygonColorFunction);
+			} catch (e) {
+				this.locationSettings.polygonColorFunction = null;
+			}
+		}
+
+		this.locationSettings.useMarkerImageFunction = this.ctx.settings.useMarkerImageFunction === true;
+		if (angular.isDefined(this.ctx.settings.markerImageFunction) && this.ctx.settings.markerImageFunction.length > 0) {
+			try {
+				this.locationSettings.markerImageFunction = new Function('data, images, dsData, dsIndex', this.ctx.settings.markerImageFunction);
+			} catch (e) {
+				this.locationSettings.markerImageFunction = null;
+			}
+		}
+
+		this.locationSettings.markerImages = this.ctx.settings.markerImages || [];
+
+		if (!this.locationSettings.useMarkerImageFunction &&
+			angular.isDefined(this.ctx.settings.markerImage) &&
+			this.ctx.settings.markerImage.length > 0) {
+			this.locationSettings.useMarkerImage = true;
+			var url = this.ctx.settings.markerImage;
+			var size = this.ctx.settings.markerImageSize || 34;
+			this.locationSettings.currentImage = {
+				url: url,
+				size: size
+			};
+		}
+
+		if (this.drawRoutes) {
+			this.locationSettings.strokeWeight = this.ctx.settings.strokeWeight || 2;
+			this.locationSettings.strokeOpacity = this.ctx.settings.strokeOpacity || 1.0;
+		}
+	}
+
+	onTooltipAction(event, actionName, dsIndex) {
+		var descriptor = this.tooltipActionsMap[actionName];
+		if (descriptor) {
+			var datasource = this.subscription.datasources[dsIndex];
+			var entityId = {};
+			entityId.id = datasource.entityId;
+			entityId.entityType = datasource.entityType;
+			var entityName = datasource.entityName;
+			this.ctx.actionsApi.handleWidgetAction(event, descriptor, entityId, entityName);
+		}
+	}
+
+	update() {
+
+		var tbMap = this;
+
+
+		function updateLocationLabel(location, dataMap) {
+			if (location.settings.showLabel) {
+				if (location.settings.useLabelFunction && location.settings.labelFunction) {
+					try {
+						location.settings.label = location.settings.labelFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
+					} catch (e) {
+						location.settings.label = null;
+					}
+					if (location.settings.label) {
+						var datasources = tbMap.subscription.datasources;
+						location.settings.label = tbMap.utils.createLabelFromDatasource(datasources[location.dsIndex], location.settings.label);
+						location.settings.labelReplaceInfo = processPattern(location.settings.label, datasources, location.dsIndex);
+						location.settings.labelText = location.settings.label;
+					}
+				}
+				if (location.settings.labelReplaceInfo.variables.length) {
+					location.settings.labelText = fillPattern(location.settings.label,
+						location.settings.labelReplaceInfo, tbMap.subscription.data);
+				}
+				tbMap.map.updateMarkerLabel(location.marker, location.settings);
+			}
+		}
+
+
+		function calculateLocationColor(location, dataMap) {
+			if (location.settings.useColorFunction && location.settings.colorFunction) {
+				var color;
+				try {
+					color = location.settings.colorFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
+				} catch (e) {/**/
+				}
+				if (!color) {
+					color = '#FE7569';
+				}
+				return tinycolor(color).toHexString();
+			} else {
+				return location.settings.color;
+			}
+		}
+
+		function calculateLocationPolygonColor(location, dataMap) {
+			if (location.settings.usePolygonColorFunction && location.settings.polygonColorFunction) {
+				var color;
+				try {
+					color = location.settings.polygonColorFunction(dataMap.dataMap, dataMap.dsDataMap, location.dsIndex);
+				} catch (e) {/**/
+				}
+				if (!color) {
+					color = '#007800';
+				}
+				return tinycolor(color).toHexString();
+			} else {
+				return location.settings.polygonColor;
+			}
+		}
+
+		function updateLocationColor(location, color, image) {
+			if (!location.settings.calculatedColor || location.settings.calculatedColor !== color) {
+				if (!location.settings.useMarkerImage && !image) {
+					tbMap.map.updateMarkerColor(location.marker, color);
+				}
+				if (location.polyline) {
+					tbMap.map.updatePolylineColor(location.polyline, location.settings, color);
+				}
+				location.settings.calculatedColor = color;
+			}
+		}
+
+		function updateLocationPolygonColor(location, color) {
+			if (!location.settings.calculatedPolygonColor || location.settings.calculatedPolygonColor !== color) {
+				location.settings.calculatedPolygonColor = color;
+				if (location.polygon) {
+					tbMap.map.updatePolygonColor(location.polygon, location.settings, color);
+				}
+
+			}
+		}
+
+		function calculateLocationMarkerImage(location, dataMap) {
+			if (location.settings.useMarkerImageFunction && location.settings.markerImageFunction) {
+				var image = null;
+				try {
+					image = location.settings.markerImageFunction(dataMap.dataMap, location.settings.markerImages, dataMap.dsDataMap, location.dsIndex);
+				} catch (e) {
+					image = null;
+				}
+				return image;
+			} else {
+				return null;
+			}
+		}
+
+		function updateLocationMarkerIcon(location, image) {
+			if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
+				location.settings.currentImage = image;
+				tbMap.map.updateMarkerIcon(location.marker, location.settings);
+			}
+		}
+
+		function updateLocationStyle(location, dataMap) {
+			updateLocationLabel(location, dataMap);
+			var color = calculateLocationColor(location, dataMap);
+			var polygonColor = calculateLocationPolygonColor(location, dataMap);
+			var image = calculateLocationMarkerImage(location, dataMap);
+			updateLocationColor(location, color, image);
+			if (location.settings.usePolygonColorFunction) updateLocationPolygonColor(location, polygonColor);
+			updateLocationMarkerIcon(location, image);
+		}
+
+		function createOrUpdateLocationMarker(location, markerLocation, dataMap) {
+			var changed = false;
+			if (!location.marker) {
+				var image = calculateLocationMarkerImage(location, dataMap);
+				if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
+					location.settings.currentImage = image;
+				}
+				location.marker = tbMap.map.createMarker(markerLocation, location.dsIndex, location.settings,
+					function (event) {
+						tbMap.callbacks.onLocationClick(location);
+						locationRowClick(event, location);
+					}, [location.dsIndex]);
+				tbMap.markers.push(location.marker);
+				changed = true;
+			} else {
+				var prevPosition = tbMap.map.getMarkerPosition(location.marker);
+				if (!prevPosition.equals(markerLocation)) {
+					tbMap.map.setMarkerPosition(location.marker, markerLocation);
+					changed = true;
+				}
+			}
+			return changed;
+		}
+
+		function locationRowClick($event, location) {
+			var descriptors = tbMap.ctx.actionsApi.getActionDescriptors('markerClick');
+			if (descriptors.length) {
+				var datasource = tbMap.subscription.datasources[location.dsIndex];
+				var entityId = {};
+				entityId.id = datasource.entityId;
+				entityId.entityType = datasource.entityType;
+				var entityName = datasource.entityName;
+				tbMap.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName);
+			}
+		}
+
+		function updateLocation(location, data, dataMap) {
+			var locationChanged = false;
+			if (location.latIndex > -1 && location.lngIndex > -1) {
+				var latData = data[location.latIndex].data;
+				var lngData = data[location.lngIndex].data;
+				var lat, lng, latLng;
+				if (latData.length > 0 && lngData.length > 0) {
+					if (tbMap.drawRoutes) {
+						// Create or update route
+						var latLngs = [];
+						for (var i = 0; i < latData.length; i++) {
+							lat = latData[i][1];
+							lng = lngData[i][1];
+							if (angular.isDefined(lat) && lat != null && angular.isDefined(lng) && lng != null) {
+								latLng = tbMap.map.createLatLng(lat, lng);
+								if (i == 0 || !latLngs[latLngs.length - 1].equals(latLng)) {
+									latLngs.push(latLng);
+								}
+							}
+						}
+						if (latLngs.length > 0) {
+							var markerLocation = latLngs[latLngs.length - 1];
+							createOrUpdateLocationMarker(location, markerLocation, dataMap);
+						}
+						if (!location.polyline) {
+							location.polyline = tbMap.map.createPolyline(latLngs, location.settings);
+							tbMap.polylines.push(location.polyline);
+							locationChanged = true;
+						} else {
+							var prevPath = tbMap.map.getPolylineLatLngs(location.polyline);
+							if (!prevPath || !arraysEqual(prevPath, latLngs)) {
+								tbMap.map.setPolylineLatLngs(location.polyline, latLngs);
+								locationChanged = true;
+							}
+						}
+					} else {
+						// Create or update marker
+						lat = latData[latData.length - 1][1];
+						lng = lngData[lngData.length - 1][1];
+						if (angular.isDefined(lat) && lat != null && angular.isDefined(lng) && lng != null) {
+							latLng = tbMap.map.createLatLng(lat, lng);
+							if (createOrUpdateLocationMarker(location, latLng, dataMap)) {
+								locationChanged = true;
+							}
+						}
+
+
+						if (location.settings.showPolygon && dataMap.dsDataMap[location.dsIndex][location.settings.polygonKeyName] !== null) {
+							let polygonLatLngsRaw = angular.fromJson(dataMap.dsDataMap[location.dsIndex][location.settings.polygonKeyName]);
+							let polygonLatLngs = !polygonLatLngsRaw || mapPolygonArray(polygonLatLngsRaw);
+							if (!location.polygon && polygonLatLngs.length > 0) {
+								location.polygon = tbMap.map.createPolygon(polygonLatLngs, location.settings);
+								tbMap.polygons.push(location.polygon);
+							} else if (polygonLatLngs.length > 0) {
+								let prevPolygonArr = tbMap.map.getPolygonLatLngs(location.polygon);
+								if (!prevPolygonArr || !arraysEqual(prevPolygonArr, polygonLatLngs)) {
+									tbMap.map.setPolygonLatLngs(location.polygon, polygonLatLngs);
+								}
+							}
+						}
+					}
+					if (location.marker) {
+						updateLocationStyle(location, dataMap);
+					}
+				}
+			}
+			return locationChanged;
+		}
+
+		function loadLocations(data, datasources) {
+			var bounds = tbMap.map.createBounds();
+			tbMap.locations = [];
+			var dataMap = toLabelValueMap(data, datasources);
+			var currentDatasource = null;
+			var currentDatasourceIndex = -1;
+			var latIndex = -1;
+			var lngIndex = -1;
+
+			for (var i = 0; i < data.length; i++) {
+				var dataKeyData = data[i];
+				var dataKey = dataKeyData.dataKey;
+				if (dataKeyData.datasource != currentDatasource) {
+					currentDatasource = dataKeyData.datasource;
+					currentDatasourceIndex++;
+					latIndex = -1;
+					lngIndex = -1;
+				}
+				var nameToCheck;
+				if (dataKey.locationAttrName) {
+					nameToCheck = dataKey.locationAttrName;
+				} else {
+					nameToCheck = dataKey.label;
+				}
+				if (nameToCheck === tbMap.locationSettings.latKeyName) {
+					latIndex = i;
+				} else if (nameToCheck === tbMap.locationSettings.lngKeyName) {
+					lngIndex = i;
+				}
+
+				if (latIndex > -1 && lngIndex > -1) {
+					var location = {
+						latIndex: latIndex,
+						lngIndex: lngIndex,
+						dsIndex: currentDatasourceIndex,
+						settings: angular.copy(tbMap.locationSettings)
+					};
+					if (location.settings.showLabel) {
+						location.settings.label = tbMap.utils.createLabelFromDatasource(currentDatasource, location.settings.label);
+						location.settings.labelReplaceInfo = processPattern(location.settings.label, datasources, currentDatasourceIndex);
+						location.settings.labelText = location.settings.label;
+					}
+					if (location.settings.displayTooltip) {
+						location.settings.tooltipPattern = tbMap.utils.createLabelFromDatasource(currentDatasource, location.settings.tooltipPattern);
+						location.settings.tooltipReplaceInfo = processPattern(location.settings.tooltipPattern, datasources, currentDatasourceIndex);
+					}
+					tbMap.locations.push(location);
+					updateLocation(location, data, dataMap);
+					if (location.polyline) {
+						tbMap.map.extendBounds(bounds, location.polyline);
+					} else if (location.marker) {
+						tbMap.map.extendBoundsWithMarker(bounds, location.marker);
+					}
+					latIndex = -1;
+					lngIndex = -1;
+				}
+
+			}
+			tbMap.map.fitBounds(bounds);
+		}
+
+		function mapPolygonArray (rawArray) {
+			let latLngArray = rawArray.map(function (el) {
+				if (el.length === 2) {
+					return tbMap.map.createLatLng(el[0], el[1]);
+				} else if (el.length > 2) {
+					return mapPolygonArray(el);
+				} else {
+					return tbMap.map.createLatLng(false);
+				}
+			});
+			return latLngArray;
+		}
+
+		function updateLocations(data, datasources) {
+			var locationsChanged = false;
+			var bounds = tbMap.map.createBounds();
+			var dataMap = toLabelValueMap(data, datasources);
+			for (var p = 0; p < tbMap.locations.length; p++) {
+				var location = tbMap.locations[p];
+				locationsChanged |= updateLocation(location, data, dataMap);
+				if (location.polyline) {
+					tbMap.map.extendBounds(bounds, location.polyline);
+				} else if (location.marker) {
+					tbMap.map.extendBoundsWithMarker(bounds, location.marker);
+				}
+			}
+			if (locationsChanged && tbMap.initBounds) {
+				tbMap.initBounds = !datasources.every(
+					function (ds) {
+						return ds.dataReceived === true;
+					});
+				tbMap.map.fitBounds(bounds);
+			}
+		}
+
+		function createTooltipContent(tooltip, data, datasources) {
+			var content;
+			var settings = tooltip.locationSettings;
+			if (settings.useTooltipFunction && settings.tooltipFunction) {
+				var dataMap = toLabelValueMap(data, datasources);
+				try {
+					settings.tooltipPattern = settings.tooltipFunction(dataMap.dataMap, dataMap.dsDataMap, tooltip.dsIndex);
+				} catch (e) {
+					settings.tooltipPattern = null;
+				}
+				if (settings.tooltipPattern) {
+					settings.tooltipPattern = tbMap.utils.createLabelFromDatasource(datasources[tooltip.dsIndex], settings.tooltipPattern);
+					settings.tooltipReplaceInfo = processPattern(settings.tooltipPattern, datasources, tooltip.dsIndex);
+				}
+			}
+			content = fillPattern(settings.tooltipPattern, settings.tooltipReplaceInfo, data);
+			return fillPatternWithActions(content, 'onTooltipAction', tooltip.markerArgs);
+		}
+
+		if (this.map && this.map.inited() && this.subscription) {
+			if (this.subscription.data) {
+				if (!this.locations) {
+					loadLocations(this.subscription.data, this.subscription.datasources);
+				} else {
+					updateLocations(this.subscription.data, this.subscription.datasources);
+				}
+				var tooltips = this.map.getTooltips();
+				for (var t = 0; t < tooltips.length; t++) {
+					var tooltip = tooltips[t];
+					var text = createTooltipContent(tooltip, this.subscription.data, this.subscription.datasources);
+					tooltip.popup.setContent(text);
+				}
+			}
+		}
+
+	}
+
+	resize() {
+		if (this.map && this.map.inited()) {
+			this.map.invalidateSize();
+			if (this.locations && this.locations.length > 0) {
+				var bounds = this.map.createBounds();
+				for (var m = 0; m < this.markers.length; m++) {
+					this.map.extendBoundsWithMarker(bounds, this.markers[m]);
+				}
+				if (this.polylines) {
+					for (var p = 0; p < this.polylines.length; p++) {
+						this.map.extendBounds(bounds, this.polylines[p]);
+					}
+				}
+				this.map.fitBounds(bounds);
+			}
+		}
+	}
+
+	static settingsSchema(mapProvider, drawRoutes) {
+		var schema;
+		if (mapProvider === 'google-map') {
+			schema = angular.copy(googleMapSettingsSchema);
+		} else if (mapProvider === 'openstreet-map') {
+			schema = angular.copy(openstreetMapSettingsSchema);
+		} else if (mapProvider === 'image-map') {
+			return imageMapSettingsSchema;
+		} else if (mapProvider === 'tencent-map') {
+			schema = angular.copy(tencentMapSettingsSchema);
+		}
+		angular.merge(schema.schema.properties, commonMapSettingsSchema.schema.properties);
+		schema.schema.required = schema.schema.required.concat(commonMapSettingsSchema.schema.required);
+		schema.form = schema.form.concat(commonMapSettingsSchema.form);
+		if (drawRoutes) {
+			angular.merge(schema.schema.properties, routeMapSettingsSchema.schema.properties);
+			schema.schema.required = schema.schema.required.concat(routeMapSettingsSchema.schema.required);
+			schema.form = schema.form.concat(routeMapSettingsSchema.form);
+		}
+		return schema;
+	}
+
+	static dataKeySettingsSchema(/*mapProvider*/) {
+		return {};
+	}
+
+	static actionSources() {
+		return {
+			'markerClick': {
+				name: 'widget-action.marker-click',
+				multiple: false
+			},
+			'tooltipAction': {
+				name: 'widget-action.tooltip-tag-action',
+				multiple: true
+			}
+		};
+	}
 
 }
 
 const googleMapSettingsSchema =
-    {
-        "schema":{
-            "title":"Google Map Configuration",
-            "type":"object",
-            "properties":{
-                "gmApiKey":{
-                    "title":"Google Maps API Key",
-                    "type":"string"
-                },
-                "gmDefaultMapType":{
-                    "title":"Default map type",
-                    "type":"string",
-                    "default":"roadmap"
-                }
-            },
-            "required":[
-                "gmApiKey"
-            ]
-        },
-        "form":[
-            "gmApiKey",
-            {
-                "key":"gmDefaultMapType",
-                "type":"rc-select",
-                "multiple":false,
-                "items":[
-                    {
-                        "value":"roadmap",
-                        "label":"Roadmap"
-                    },
-                    {
-                        "value":"satellite",
-                        "label":"Satellite"
-                    },
-                    {
-                        "value":"hybrid",
-                        "label":"Hybrid"
-                    },
-                    {
-                        "value":"terrain",
-                        "label":"Terrain"
-                    }
-                ]
-            }
-        ]
-    };
-    
+	{
+		"schema": {
+			"title": "Google Map Configuration",
+			"type": "object",
+			"properties": {
+				"gmApiKey": {
+					"title": "Google Maps API Key",
+					"type": "string"
+				},
+				"gmDefaultMapType": {
+					"title": "Default map type",
+					"type": "string",
+					"default": "roadmap"
+				}
+			},
+			"required": [
+				"gmApiKey"
+			]
+		},
+		"form": [
+			"gmApiKey",
+			{
+				"key": "gmDefaultMapType",
+				"type": "rc-select",
+				"multiple": false,
+				"items": [
+					{
+						"value": "roadmap",
+						"label": "Roadmap"
+					},
+					{
+						"value": "satellite",
+						"label": "Satellite"
+					},
+					{
+						"value": "hybrid",
+						"label": "Hybrid"
+					},
+					{
+						"value": "terrain",
+						"label": "Terrain"
+					}
+				]
+			}
+		]
+	};
+
 const tencentMapSettingsSchema =
-    {
-        "schema":{
-            "title":"Tencent Map Configuration",
-            "type":"object",
-            "properties":{
-                "tmApiKey":{
-                    "title":"Tencent Maps API Key",
-                    "type":"string"
-                },
-                "tmDefaultMapType":{
-                    "title":"Default map type",
-                    "type":"string",
-                    "default":"roadmap"
-                }
-            },
-            "required":[
-                "tmApiKey"
-            ]
-        },
-        "form":[
-            "tmApiKey",
-            {
-                "key":"tmDefaultMapType",
-                "type":"rc-select",
-                "multiple":false,
-                "items":[
-                    {
-                        "value":"roadmap",
-                        "label":"Roadmap"
-                    },
-                    {
-                        "value":"satellite",
-                        "label":"Satellite"
-                    },
-                    {
-                        "value":"hybrid",
-                        "label":"Hybrid"
-                    },
-                ]
-            }
-        ]
-    };
-    
+	{
+		"schema": {
+			"title": "Tencent Map Configuration",
+			"type": "object",
+			"properties": {
+				"tmApiKey": {
+					"title": "Tencent Maps API Key",
+					"type": "string"
+				},
+				"tmDefaultMapType": {
+					"title": "Default map type",
+					"type": "string",
+					"default": "roadmap"
+				}
+			},
+			"required": [
+				"tmApiKey"
+			]
+		},
+		"form": [
+			"tmApiKey",
+			{
+				"key": "tmDefaultMapType",
+				"type": "rc-select",
+				"multiple": false,
+				"items": [
+					{
+						"value": "roadmap",
+						"label": "Roadmap"
+					},
+					{
+						"value": "satellite",
+						"label": "Satellite"
+					},
+					{
+						"value": "hybrid",
+						"label": "Hybrid"
+					},
+				]
+			}
+		]
+	};
+
 const openstreetMapSettingsSchema =
-    {
-        "schema":{
-            "title":"Openstreet Map Configuration",
-            "type":"object",
-            "properties":{
-                "mapProvider":{
-                    "title":"Map provider",
-                    "type":"string",
-                    "default":"OpenStreetMap.Mapnik"
-                }
-            },
-            "required":[
-            ]
-        },
-        "form":[
-            {
-                "key":"mapProvider",
-                "type":"rc-select",
-                "multiple":false,
-                "items":[
-                    {
-                        "value":"OpenStreetMap.Mapnik",
-                        "label":"OpenStreetMap.Mapnik (Default)"
-                    },
-                    {
-                        "value":"OpenStreetMap.BlackAndWhite",
-                        "label":"OpenStreetMap.BlackAndWhite"
-                    },
-                    {
-                        "value":"OpenStreetMap.HOT",
-                        "label":"OpenStreetMap.HOT"
-                    },
-                    {
-                        "value":"Esri.WorldStreetMap",
-                        "label":"Esri.WorldStreetMap"
-                    },
-                    {
-                        "value":"Esri.WorldTopoMap",
-                        "label":"Esri.WorldTopoMap"
-                    },
-                    {
-                        "value":"CartoDB.Positron",
-                        "label":"CartoDB.Positron"
-                    },
-                    {
-                        "value":"CartoDB.DarkMatter",
-                        "label":"CartoDB.DarkMatter"
-                    }
-                ]
-            }
-        ]
-    };
+	{
+		"schema": {
+			"title": "Openstreet Map Configuration",
+			"type": "object",
+			"properties": {
+				"mapProvider": {
+					"title": "Map provider",
+					"type": "string",
+					"default": "OpenStreetMap.Mapnik"
+				}
+			},
+			"required": []
+		},
+		"form": [
+			{
+				"key": "mapProvider",
+				"type": "rc-select",
+				"multiple": false,
+				"items": [
+					{
+						"value": "OpenStreetMap.Mapnik",
+						"label": "OpenStreetMap.Mapnik (Default)"
+					},
+					{
+						"value": "OpenStreetMap.BlackAndWhite",
+						"label": "OpenStreetMap.BlackAndWhite"
+					},
+					{
+						"value": "OpenStreetMap.HOT",
+						"label": "OpenStreetMap.HOT"
+					},
+					{
+						"value": "Esri.WorldStreetMap",
+						"label": "Esri.WorldStreetMap"
+					},
+					{
+						"value": "Esri.WorldTopoMap",
+						"label": "Esri.WorldTopoMap"
+					},
+					{
+						"value": "CartoDB.Positron",
+						"label": "CartoDB.Positron"
+					},
+					{
+						"value": "CartoDB.DarkMatter",
+						"label": "CartoDB.DarkMatter"
+					}
+				]
+			}
+		]
+	};
 
 const commonMapSettingsSchema =
-    {
-        "schema":{
-            "title":"Map Configuration",
-            "type":"object",
-            "properties":{
-                "defaultZoomLevel":{
-                    "title":"Default map zoom level (1 - 20)",
-                    "type":"number"
-                },
-                "fitMapBounds":{
-                    "title":"Fit map bounds to cover all markers",
-                    "type":"boolean",
-                    "default":true
-                },
-                "latKeyName":{
-                    "title":"Latitude key name",
-                    "type":"string",
-                    "default":"latitude"
-                },
-                "lngKeyName":{
-                    "title":"Longitude key name",
-                    "type":"string",
-                    "default":"longitude"
-                },
-                "showLabel":{
-                    "title":"Show label",
-                    "type":"boolean",
-                    "default":true
-                },
-                "label":{
-                    "title":"Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )",
-                    "type":"string",
-                    "default":"${entityName}"
-                },
-                "useLabelFunction": {
-                    "title":"Use label function",
-                    "type":"boolean",
-                    "default":false
-                },
-                "labelFunction":{
-                    "title":"Label function: f(data, dsData, dsIndex)",
-                    "type":"string"
-                },
-                "showTooltip": {
-                    "title": "Show tooltip",
-                    "type":"boolean",
-                    "default":true
-                },
-                "autocloseTooltip": {
-                    "title": "Auto-close tooltips",
-                    "type":"boolean",
-                    "default":true
-                },
-                "tooltipPattern":{
-                    "title":"Tooltip (for ex. 'Text ${keyName} units.' or <link-act name='my-action'>Link text</link-act>')",
-                    "type":"string",
-                    "default":"<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}"
-                },
-                "useTooltipFunction": {
-                    "title":"Use tooltip function",
-                    "type":"boolean",
-                    "default":false
-                },
-                "tooltipFunction":{
-                    "title":"Tooltip function: f(data, dsData, dsIndex)",
-                    "type":"string"
-                },
-                "color":{
-                    "title":"Color",
-                    "type":"string"
-                },
-                "useColorFunction":{
-                    "title":"Use color function",
-                    "type":"boolean",
-                    "default":false
-                },
-                "colorFunction":{
-                    "title":"Color function: f(data, dsData, dsIndex)",
-                    "type":"string"
-                },
-                "markerImage":{
-                    "title":"Custom marker image",
-                    "type":"string"
-                },
-                "markerImageSize":{
-                    "title":"Custom marker image size (px)",
-                    "type":"number",
-                    "default":34
-                },
-                "useMarkerImageFunction":{
-                    "title":"Use marker image function",
-                    "type":"boolean",
-                    "default":false
-                },
-                "markerImageFunction":{
-                    "title":"Marker image function: f(data, images, dsData, dsIndex)",
-                    "type":"string"
-                },
-                "markerImages":{
-                    "title":"Marker images",
-                    "type":"array",
-                    "items":{
-                        "title":"Marker image",
-                        "type":"string"
-                    }
-                }
-            },
-            "required":[]
-        },
-        "form":[
-            "defaultZoomLevel",
-            "fitMapBounds",
-            "latKeyName",
-            "lngKeyName",
-            "showLabel",
-            "label",
-            "useLabelFunction",
-            {
-                "key":"labelFunction",
-                "type":"javascript"
-            },
-            "showTooltip",
-            "autocloseTooltip",
-            {
-                "key": "tooltipPattern",
-                "type": "textarea"
-            },
-            "useTooltipFunction",
-            {
-                "key":"tooltipFunction",
-                "type":"javascript"
-            },
-            {
-                "key":"color",
-                "type":"color"
-            },
-            "useColorFunction",
-            {
-                "key":"colorFunction",
-                "type":"javascript"
-            },
-            {
-                "key":"markerImage",
-                "type":"image"
-            },
-            "markerImageSize",
-            "useMarkerImageFunction",
-            {
-                "key":"markerImageFunction",
-                "type":"javascript"
-            },
-            {
-                "key":"markerImages",
-                "items":[
-                    {
-                        "key":"markerImages[]",
-                        "type":"image"
-                    }
-                ]
-            }
-        ]
-};
+	{
+		"schema": {
+			"title": "Map Configuration",
+			"type": "object",
+			"properties": {
+				"defaultZoomLevel": {
+					"title": "Default map zoom level (1 - 20)",
+					"type": "number"
+				},
+				"fitMapBounds": {
+					"title": "Fit map bounds to cover all markers",
+					"type": "boolean",
+					"default": true
+				},
+				"latKeyName": {
+					"title": "Latitude key name",
+					"type": "string",
+					"default": "latitude"
+				},
+				"lngKeyName": {
+					"title": "Longitude key name",
+					"type": "string",
+					"default": "longitude"
+				},
+				"showLabel": {
+					"title": "Show label",
+					"type": "boolean",
+					"default": true
+				},
+				"label": {
+					"title": "Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )",
+					"type": "string",
+					"default": "${entityName}"
+				},
+				"useLabelFunction": {
+					"title": "Use label function",
+					"type": "boolean",
+					"default": false
+				},
+				"labelFunction": {
+					"title": "Label function: f(data, dsData, dsIndex)",
+					"type": "string"
+				},
+				"showTooltip": {
+					"title": "Show tooltip",
+					"type": "boolean",
+					"default": true
+				},
+				"autocloseTooltip": {
+					"title": "Auto-close tooltips",
+					"type": "boolean",
+					"default": true
+				},
+				"tooltipPattern": {
+					"title": "Tooltip (for ex. 'Text ${keyName} units.' or <link-act name='my-action'>Link text</link-act>')",
+					"type": "string",
+					"default": "<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}"
+				},
+				"useTooltipFunction": {
+					"title": "Use tooltip function",
+					"type": "boolean",
+					"default": false
+				},
+				"tooltipFunction": {
+					"title": "Tooltip function: f(data, dsData, dsIndex)",
+					"type": "string"
+				},
+				"color": {
+					"title": "Color",
+					"type": "string"
+				},
+				"useColorFunction": {
+					"title": "Use color function",
+					"type": "boolean",
+					"default": false
+				},
+				"colorFunction": {
+					"title": "Color function: f(data, dsData, dsIndex)",
+					"type": "string"
+				},
+				"showPolygon": {
+					"title": "Show polygon",
+					"type": "boolean",
+					"default": false
+				},
+				"polygonKeyName": {
+					"title": "Polygon key name",
+					"type": "string",
+					"default": "coordinates"
+				},
+				"polygonColor": {
+					"title": "Polygon color",
+					"type": "string"
+				},
+				"polygonOpacity": {
+					"title": "Polygon opacity",
+					"type": "number",
+					"default": 0.5
+				},
+				"polygonStrokeColor": {
+					"title": "Stroke color",
+					"type": "string"
+				},
+				"polygonStrokeOpacity": {
+					"title": "Stroke opacity",
+					"type": "number",
+					"default": 1
+				},
+				"polygonStrokeWeight": {
+					"title": "Stroke weight",
+					"type": "number",
+					"default": 1
+				},
+				"usePolygonColorFunction": {
+					"title": "Use polygon color function",
+					"type": "boolean",
+					"default": false
+				},
+				"polygonColorFunction": {
+					"title": "Polygon Color function: f(data, dsData, dsIndex)",
+					"type": "string"
+				},
+				"markerImage": {
+					"title": "Custom marker image",
+					"type": "string"
+				},
+				"markerImageSize": {
+					"title": "Custom marker image size (px)",
+					"type": "number",
+					"default": 34
+				},
+				"useMarkerImageFunction": {
+					"title": "Use marker image function",
+					"type": "boolean",
+					"default": false
+				},
+				"markerImageFunction": {
+					"title": "Marker image function: f(data, images, dsData, dsIndex)",
+					"type": "string"
+				},
+				"markerImages": {
+					"title": "Marker images",
+					"type": "array",
+					"items": {
+						"title": "Marker image",
+						"type": "string"
+					}
+				}
+			},
+			"required": []
+		},
+		"form": [
+			"defaultZoomLevel",
+			"fitMapBounds",
+			"latKeyName",
+			"lngKeyName",
+			"showLabel",
+			"label",
+			"useLabelFunction",
+			{
+				"key": "labelFunction",
+				"type": "javascript"
+			},
+			"showTooltip",
+			"autocloseTooltip",
+			{
+				"key": "tooltipPattern",
+				"type": "textarea"
+			},
+			"useTooltipFunction",
+			{
+				"key": "tooltipFunction",
+				"type": "javascript"
+			},
+			{
+				"key": "color",
+				"type": "color"
+			},
+			"useColorFunction",
+			{
+				"key": "colorFunction",
+				"type": "javascript"
+			}, "showPolygon", "polygonKeyName",
+			{
+				"key": "polygonColor",
+				"type": "color"
+			},
+			"polygonOpacity",
+			{
+				"key": "polygonStrokeColor",
+				"type": "color"
+			},
+			"polygonStrokeOpacity","polygonStrokeWeight","usePolygonColorFunction",
+			{
+				"key": "polygonColorFunction",
+				"type": "javascript"
+			},
+			{
+				"key": "markerImage",
+				"type": "image"
+			},
+			"markerImageSize",
+			"useMarkerImageFunction",
+			{
+				"key": "markerImageFunction",
+				"type": "javascript"
+			},
+			{
+				"key": "markerImages",
+				"items": [
+					{
+						"key": "markerImages[]",
+						"type": "image"
+					}
+				]
+			}
+		]
+	};
 
 const routeMapSettingsSchema =
-    {
-        "schema":{
-            "title":"Route Map Configuration",
-            "type":"object",
-            "properties":{
-                "strokeWeight": {
-                    "title": "Stroke weight",
-                    "type": "number",
-                    "default": 2
-                },
-                "strokeOpacity": {
-                    "title": "Stroke opacity",
-                    "type": "number",
-                    "default": 1.0
-                }
-            },
-            "required":[
-            ]
-        },
-        "form":[
-            "strokeWeight",
-            "strokeOpacity"
-        ]
-    };
+	{
+		"schema": {
+			"title": "Route Map Configuration",
+			"type": "object",
+			"properties": {
+				"strokeWeight": {
+					"title": "Stroke weight",
+					"type": "number",
+					"default": 2
+				},
+				"strokeOpacity": {
+					"title": "Stroke opacity",
+					"type": "number",
+					"default": 1.0
+				}
+			},
+			"required": []
+		},
+		"form": [
+			"strokeWeight",
+			"strokeOpacity"
+		]
+	};
 
 const imageMapSettingsSchema =
-{
-    "schema":{
-        "title":"Image Map Configuration",
-        "type":"object",
-        "properties":{
-            "mapImageUrl": {
-                "title": "Image map background",
-                "type": "string",
-                "default": "data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg=="
-            },
-            "imageEntityAlias": {
-                "title": "Image URL source entity alias",
-                "type": "string",
-                "default": ""
-            },
-            "imageUrlAttribute": {
-                "title": "Image URL source entity attribute",
-                "type": "string",
-                "default": ""
-            },
-            "xPosKeyName":{
-                "title":"X position key name",
-                "type":"string",
-                "default":"xPos"
-            },
-            "yPosKeyName":{
-                "title":"Y position key name",
-                "type":"string",
-                "default":"yPos"
-            },
-            "showLabel":{
-                "title":"Show label",
-                "type":"boolean",
-                "default":true
-            },
-            "label":{
-                "title":"Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )",
-                "type":"string",
-                "default":"${entityName}"
-            },
-            "useLabelFunction": {
-                "title":"Use label function",
-                "type":"boolean",
-                "default":false
-            },
-            "labelFunction":{
-                "title":"Label function: f(data, dsData, dsIndex)",
-                "type":"string"
-            },
-            "showTooltip": {
-                "title": "Show tooltip",
-                "type":"boolean",
-                "default":true
-            },
-            "autocloseTooltip": {
-                "title": "Auto-close tooltips",
-                "type":"boolean",
-                "default":true
-            },
-            "tooltipPattern":{
-                "title":"Tooltip (for ex. 'Text ${keyName} units.' or <link-act name='my-action'>Link text</link-act>')",
-                "type":"string",
-                "default":"<b>${entityName}</b><br/><br/><b>X Pos:</b> ${xPos:2}<br/><b>Y Pos:</b> ${yPos:2}"
-            },
-            "useTooltipFunction": {
-                "title":"Use tooltip function",
-                "type":"boolean",
-                "default":false
-            },
-            "tooltipFunction":{
-                "title":"Tooltip function: f(data, dsData, dsIndex)",
-                "type":"string"
-            },
-            "color":{
-                "title":"Color",
-                "type":"string"
-            },
-            "posFunction":{
-                "title":"Position conversion function: f(origXPos, origYPos), should return x,y coordinates as double from 0 to 1 each",
-                "type":"string",
-                "default": "return {x: origXPos, y: origYPos};"
-            },
-            "markerOffsetX": {
-                "title": "Marker X offset relative to position",
-                "type": "number",
-                "default": 0.5
-            },
-            "markerOffsetY": {
-                "title": "Marker Y offset relative to position",
-                "type": "number",
-                "default": 1
-            },
-            "useColorFunction":{
-                "title":"Use color function",
-                "type":"boolean",
-                "default":false
-            },
-            "colorFunction":{
-                "title":"Color function: f(data, dsData, dsIndex)",
-                "type":"string"
-            },
-            "markerImage":{
-                "title":"Custom marker image",
-                "type":"string"
-            },
-            "markerImageSize":{
-                "title":"Custom marker image size (px)",
-                "type":"number",
-                "default":34
-            },
-            "useMarkerImageFunction":{
-                "title":"Use marker image function",
-                "type":"boolean",
-                "default":false
-            },
-            "markerImageFunction":{
-                "title":"Marker image function: f(data, images, dsData, dsIndex)",
-                "type":"string"
-            },
-            "markerImages":{
-                "title":"Marker images",
-                "type":"array",
-                "items":{
-                    "title":"Marker image",
-                    "type":"string"
-                }
-            }
-        },
-        "required":[]
-    },
-    "form":[
-        {
-            "key": "mapImageUrl",
-            "type": "image"
-        },
-        "imageEntityAlias",
-        "imageUrlAttribute",
-        "xPosKeyName",
-        "yPosKeyName",
-        "showLabel",
-        "label",
-        "useLabelFunction",
-        {
-            "key":"labelFunction",
-            "type":"javascript"
-        },
-        "showTooltip",
-        "autocloseTooltip",
-        {
-            "key": "tooltipPattern",
-            "type": "textarea"
-        },
-        "useTooltipFunction",
-        {
-            "key":"tooltipFunction",
-            "type":"javascript"
-        },
-        {
-            "key":"color",
-            "type":"color"
-        },
-        {
-            "key":"posFunction",
-            "type":"javascript"
-        },
-        "markerOffsetX",
-        "markerOffsetY",
-        "useColorFunction",
-        {
-            "key":"colorFunction",
-            "type":"javascript"
-        },
-        {
-            "key":"markerImage",
-            "type":"image"
-        },
-        "markerImageSize",
-        "useMarkerImageFunction",
-        {
-            "key":"markerImageFunction",
-            "type":"javascript"
-        },
-        {
-            "key":"markerImages",
-            "items":[
-                {
-                    "key":"markerImages[]",
-                    "type":"image"
-                }
-            ]
-        }
-    ]
-};
\ No newline at end of file
+	{
+		"schema": {
+			"title": "Image Map Configuration",
+			"type": "object",
+			"properties": {
+				"mapImageUrl": {
+					"title": "Image map background",
+					"type": "string",
+					"default": "data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg=="
+				},
+				"imageEntityAlias": {
+					"title": "Image URL source entity alias",
+					"type": "string",
+					"default": ""
+				},
+				"imageUrlAttribute": {
+					"title": "Image URL source entity attribute",
+					"type": "string",
+					"default": ""
+				},
+				"xPosKeyName": {
+					"title": "X position key name",
+					"type": "string",
+					"default": "xPos"
+				},
+				"yPosKeyName": {
+					"title": "Y position key name",
+					"type": "string",
+					"default": "yPos"
+				},
+				"showLabel": {
+					"title": "Show label",
+					"type": "boolean",
+					"default": true
+				},
+				"label": {
+					"title": "Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )",
+					"type": "string",
+					"default": "${entityName}"
+				},
+				"useLabelFunction": {
+					"title": "Use label function",
+					"type": "boolean",
+					"default": false
+				},
+				"labelFunction": {
+					"title": "Label function: f(data, dsData, dsIndex)",
+					"type": "string"
+				},
+				"showTooltip": {
+					"title": "Show tooltip",
+					"type": "boolean",
+					"default": true
+				},
+				"autocloseTooltip": {
+					"title": "Auto-close tooltips",
+					"type": "boolean",
+					"default": true
+				},
+				"tooltipPattern": {
+					"title": "Tooltip (for ex. 'Text ${keyName} units.' or <link-act name='my-action'>Link text</link-act>')",
+					"type": "string",
+					"default": "<b>${entityName}</b><br/><br/><b>X Pos:</b> ${xPos:2}<br/><b>Y Pos:</b> ${yPos:2}"
+				},
+				"useTooltipFunction": {
+					"title": "Use tooltip function",
+					"type": "boolean",
+					"default": false
+				},
+				"tooltipFunction": {
+					"title": "Tooltip function: f(data, dsData, dsIndex)",
+					"type": "string"
+				},
+				"color": {
+					"title": "Color",
+					"type": "string"
+				},
+				"posFunction": {
+					"title": "Position conversion function: f(origXPos, origYPos), should return x,y coordinates as double from 0 to 1 each",
+					"type": "string",
+					"default": "return {x: origXPos, y: origYPos};"
+				},
+				"markerOffsetX": {
+					"title": "Marker X offset relative to position",
+					"type": "number",
+					"default": 0.5
+				},
+				"markerOffsetY": {
+					"title": "Marker Y offset relative to position",
+					"type": "number",
+					"default": 1
+				},
+				"useColorFunction": {
+					"title": "Use color function",
+					"type": "boolean",
+					"default": false
+				},
+				"colorFunction": {
+					"title": "Color function: f(data, dsData, dsIndex)",
+					"type": "string"
+				},
+				"markerImage": {
+					"title": "Custom marker image",
+					"type": "string"
+				},
+				"markerImageSize": {
+					"title": "Custom marker image size (px)",
+					"type": "number",
+					"default": 34
+				},
+				"useMarkerImageFunction": {
+					"title": "Use marker image function",
+					"type": "boolean",
+					"default": false
+				},
+				"markerImageFunction": {
+					"title": "Marker image function: f(data, images, dsData, dsIndex)",
+					"type": "string"
+				},
+				"markerImages": {
+					"title": "Marker images",
+					"type": "array",
+					"items": {
+						"title": "Marker image",
+						"type": "string"
+					}
+				}
+			},
+			"required": []
+		},
+		"form": [
+			{
+				"key": "mapImageUrl",
+				"type": "image"
+			},
+			"imageEntityAlias",
+			"imageUrlAttribute",
+			"xPosKeyName",
+			"yPosKeyName",
+			"showLabel",
+			"label",
+			"useLabelFunction",
+			{
+				"key": "labelFunction",
+				"type": "javascript"
+			},
+			"showTooltip",
+			"autocloseTooltip",
+			{
+				"key": "tooltipPattern",
+				"type": "textarea"
+			},
+			"useTooltipFunction",
+			{
+				"key": "tooltipFunction",
+				"type": "javascript"
+			},
+			{
+				"key": "color",
+				"type": "color"
+			},
+			{
+				"key": "posFunction",
+				"type": "javascript"
+			},
+			"markerOffsetX",
+			"markerOffsetY",
+			"useColorFunction",
+			{
+				"key": "colorFunction",
+				"type": "javascript"
+			},
+			{
+				"key": "markerImage",
+				"type": "image"
+			},
+			"markerImageSize",
+			"useMarkerImageFunction",
+			{
+				"key": "markerImageFunction",
+				"type": "javascript"
+			},
+			{
+				"key": "markerImages",
+				"items": [
+					{
+						"key": "markerImages[]",
+						"type": "image"
+					}
+				]
+			}
+		]
+	};
\ No newline at end of file
diff --git a/ui/src/app/widget/lib/openstreet-map.js b/ui/src/app/widget/lib/openstreet-map.js
index 2f9c1bc..25e454f 100644
--- a/ui/src/app/widget/lib/openstreet-map.js
+++ b/ui/src/app/widget/lib/openstreet-map.js
@@ -19,234 +19,270 @@ import 'leaflet-providers';
 
 export default class TbOpenStreetMap {
 
-    constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, mapProvider) {
-
-        this.utils = utils;
-        this.defaultZoomLevel = defaultZoomLevel;
-        this.dontFitMapBounds = dontFitMapBounds;
-        this.minZoomLevel = minZoomLevel;
-        this.tooltips = [];
-
-        if (!mapProvider) {
-            mapProvider = "OpenStreetMap.Mapnik";
-        }
-
-        this.map = L.map($containerElement[0]).setView([0, 0], this.defaultZoomLevel || 8);
-
-        var tileLayer = L.tileLayer.provider(mapProvider);
-
-        tileLayer.addTo(this.map);
-
-        if (initCallback) {
-            setTimeout(initCallback, 0); //eslint-disable-line
-        }
-
-    }
-
-    inited() {
-        return angular.isDefined(this.map);
-    }
-
-    updateMarkerLabel(marker, settings) {
-        marker.unbindTooltip();
-        marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
-            { className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
-    }
-
-    updateMarkerColor(marker, color) {
-        this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
-            marker.setIcon(iconInfo.icon);
-        });
-    }
-
-    updateMarkerIcon(marker, settings) {
-        this.createMarkerIcon(marker, settings, (iconInfo) => {
-            marker.setIcon(iconInfo.icon);
-            if (settings.showLabel) {
-                marker.unbindTooltip();
-                marker.tooltipOffset = [0, -iconInfo.size[1] + 10];
-                marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
-                    { className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
-            }
-        });
-    }
-
-    createMarkerIcon(marker, settings, onMarkerIconReady) {
-        var currentImage = settings.currentImage;
-        var opMap = this;
-        if (currentImage && currentImage.url) {
-            this.utils.loadImageAspect(currentImage.url).then(
-                (aspect) => {
-                    if (aspect) {
-                        var width;
-                        var height;
-                        if (aspect > 1) {
-                            width = currentImage.size;
-                            height = currentImage.size / aspect;
-                        } else {
-                            width = currentImage.size * aspect;
-                            height = currentImage.size;
-                        }
-                        var icon = L.icon({
-                            iconUrl: currentImage.url,
-                            iconSize: [width, height],
-                            iconAnchor: [width/2, height],
-                            popupAnchor: [0, -height]
-                        });
-                        var iconInfo = {
-                            size: [width, height],
-                            icon: icon
-                        };
-                        onMarkerIconReady(iconInfo);
-                    } else {
-                        opMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
-                    }
-                }
-            );
-        } else {
-            this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
-        }
-    }
-
-    createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
-        var pinColor = color.substr(1);
-        var icon = L.icon({
-            iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
-            iconSize: [21, 34],
-            iconAnchor: [10, 34],
-            popupAnchor: [0, -34],
-            shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
-            shadowSize: [40, 37],
-            shadowAnchor: [12, 35]
-        });
-        var iconInfo = {
-            size: [21, 34],
-            icon: icon
-        };
-        onMarkerIconReady(iconInfo);
-    }
-
-    createMarker(location, dsIndex, settings, onClickListener, markerArgs) {
-        var marker = L.marker(location, {});
-        var opMap = this;
-        this.createMarkerIcon(marker, settings, (iconInfo) => {
-            marker.setIcon(iconInfo.icon);
-            if (settings.showLabel) {
-                marker.tooltipOffset = [0, -iconInfo.size[1] + 10];
-                marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
-                    { className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
-            }
-            marker.addTo(opMap.map);
-        });
-
-        if (settings.displayTooltip) {
-            this.createTooltip(marker, dsIndex, settings, markerArgs);
-        }
-
-        if (onClickListener) {
-            marker.on('click', onClickListener);
-        }
-
-        return marker;
-    }
-
-    removeMarker(marker) {
-        this.map.removeLayer(marker);
-    }
-
-    createTooltip(marker, dsIndex, settings, markerArgs) {
-        var popup = L.popup();
-        popup.setContent('');
-        marker.bindPopup(popup, {autoClose: settings.autocloseTooltip, closeOnClick: false});
-        this.tooltips.push( {
-            markerArgs: markerArgs,
-            popup: popup,
-            locationSettings: settings,
-            dsIndex: dsIndex
-        });
-    }
-
-    updatePolylineColor(polyline, settings, color) {
-        var style = {
-            color: color,
-            opacity: settings.strokeOpacity,
-            weight: settings.strokeWeight
-        };
-        polyline.setStyle(style);
-    }
-
-    createPolyline(locations, settings) {
-        var polyline = L.polyline(locations,
-            {
-                color: settings.color,
-                opacity: settings.strokeOpacity,
-                weight: settings.strokeWeight
-            }
-        ).addTo(this.map);
-        return polyline;
-    }
-
-    removePolyline(polyline) {
-        this.map.removeLayer(polyline);
-    }
-
-    fitBounds(bounds) {
-        if (bounds.isValid()) {
-            if (this.dontFitMapBounds && this.defaultZoomLevel) {
-                this.map.setZoom(this.defaultZoomLevel, {animate: false});
-                this.map.panTo(bounds.getCenter(), {animate: false});
-            } else {
-                var tbMap = this;
-                this.map.once('zoomend', function() {
-                    if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) {
-                        tbMap.map.setZoom(tbMap.minZoomLevel, {animate: false});
-                    }
-                });
-                this.map.fitBounds(bounds, {padding: [50, 50], animate: false});
-            }
-        }
-    }
-
-    createLatLng(lat, lng) {
-        return L.latLng(lat, lng);
-    }
-
-    extendBoundsWithMarker(bounds, marker) {
-        bounds.extend(marker.getLatLng());
-    }
-
-    getMarkerPosition(marker) {
-        return marker.getLatLng();
-    }
-
-    setMarkerPosition(marker, latLng) {
-        marker.setLatLng(latLng);
-    }
-
-    getPolylineLatLngs(polyline) {
-        return polyline.getLatLngs();
-    }
-
-    setPolylineLatLngs(polyline, latLngs) {
-        polyline.setLatLngs(latLngs);
-    }
-
-    createBounds() {
-        return L.latLngBounds();
-    }
-
-    extendBounds(bounds, polyline) {
-        if (polyline && polyline.getLatLngs()) {
-            bounds.extend(polyline.getBounds());
-        }
-    }
-
-    invalidateSize() {
-        this.map.invalidateSize(true);
-    }
-
-    getTooltips() {
-        return this.tooltips;
-    }
+	constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, mapProvider) {
+
+		this.utils = utils;
+		this.defaultZoomLevel = defaultZoomLevel;
+		this.dontFitMapBounds = dontFitMapBounds;
+		this.minZoomLevel = minZoomLevel;
+		this.tooltips = [];
+
+		if (!mapProvider) {
+			mapProvider = "OpenStreetMap.Mapnik";
+		}
+
+		this.map = L.map($containerElement[0]).setView([0, 0], this.defaultZoomLevel || 8);
+
+		var tileLayer = L.tileLayer.provider(mapProvider);
+
+		tileLayer.addTo(this.map);
+
+		if (initCallback) {
+			setTimeout(initCallback, 0); //eslint-disable-line
+		}
+
+	}
+
+	inited() {
+		return angular.isDefined(this.map);
+	}
+
+	updateMarkerLabel(marker, settings) {
+		marker.unbindTooltip();
+		marker.bindTooltip('<div style="color: ' + settings.labelColor + ';"><b>' + settings.labelText + '</b></div>',
+			{className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset});
+	}
+
+	updateMarkerColor(marker, color) {
+		this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
+			marker.setIcon(iconInfo.icon);
+		});
+	}
+
+	updateMarkerIcon(marker, settings) {
+		this.createMarkerIcon(marker, settings, (iconInfo) => {
+			marker.setIcon(iconInfo.icon);
+			if (settings.showLabel) {
+				marker.unbindTooltip();
+				marker.tooltipOffset = [0, -iconInfo.size[1] + 10];
+				marker.bindTooltip('<div style="color: ' + settings.labelColor + ';"><b>' + settings.labelText + '</b></div>',
+					{className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset});
+			}
+		});
+	}
+
+	createMarkerIcon(marker, settings, onMarkerIconReady) {
+		var currentImage = settings.currentImage;
+		var opMap = this;
+		if (currentImage && currentImage.url) {
+			this.utils.loadImageAspect(currentImage.url).then(
+				(aspect) => {
+					if (aspect) {
+						var width;
+						var height;
+						if (aspect > 1) {
+							width = currentImage.size;
+							height = currentImage.size / aspect;
+						} else {
+							width = currentImage.size * aspect;
+							height = currentImage.size;
+						}
+						var icon = L.icon({
+							iconUrl: currentImage.url,
+							iconSize: [width, height],
+							iconAnchor: [width / 2, height],
+							popupAnchor: [0, -height]
+						});
+						var iconInfo = {
+							size: [width, height],
+							icon: icon
+						};
+						onMarkerIconReady(iconInfo);
+					} else {
+						opMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
+					}
+				}
+			);
+		} else {
+			this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
+		}
+	}
+
+	createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
+		var pinColor = color.substr(1);
+		var icon = L.icon({
+			iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
+			iconSize: [21, 34],
+			iconAnchor: [10, 34],
+			popupAnchor: [0, -34],
+			shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
+			shadowSize: [40, 37],
+			shadowAnchor: [12, 35]
+		});
+		var iconInfo = {
+			size: [21, 34],
+			icon: icon
+		};
+		onMarkerIconReady(iconInfo);
+	}
+
+	createMarker(location, dsIndex, settings, onClickListener, markerArgs) {
+		var marker = L.marker(location, {});
+		var opMap = this;
+		this.createMarkerIcon(marker, settings, (iconInfo) => {
+			marker.setIcon(iconInfo.icon);
+			if (settings.showLabel) {
+				marker.tooltipOffset = [0, -iconInfo.size[1] + 10];
+				marker.bindTooltip('<div style="color: ' + settings.labelColor + ';"><b>' + settings.labelText + '</b></div>',
+					{className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset});
+			}
+			marker.addTo(opMap.map);
+		});
+
+		if (settings.displayTooltip) {
+			this.createTooltip(marker, dsIndex, settings, markerArgs);
+		}
+
+		if (onClickListener) {
+			marker.on('click', onClickListener);
+		}
+
+		return marker;
+	}
+
+	removeMarker(marker) {
+		this.map.removeLayer(marker);
+	}
+
+	createTooltip(marker, dsIndex, settings, markerArgs) {
+		var popup = L.popup();
+		popup.setContent('');
+		marker.bindPopup(popup, {autoClose: settings.autocloseTooltip, closeOnClick: false});
+		this.tooltips.push({
+			markerArgs: markerArgs,
+			popup: popup,
+			locationSettings: settings,
+			dsIndex: dsIndex
+		});
+	}
+
+	updatePolylineColor(polyline, settings, color) {
+		var style = {
+			color: color,
+			opacity: settings.strokeOpacity,
+			weight: settings.strokeWeight
+		};
+		polyline.setStyle(style);
+	}
+
+	createPolyline(locations, settings) {
+		var polyline = L.polyline(locations,
+			{
+				color: settings.color,
+				opacity: settings.strokeOpacity,
+				weight: settings.strokeWeight
+			}
+		).addTo(this.map);
+		return polyline;
+	}
+
+	removePolyline(polyline) {
+		this.map.removeLayer(polyline);
+	}
+
+	createPolygon(latLangs, settings) {
+		let polygon = L.polygon(latLangs, {
+			fill: true,
+			fillColor: settings.polygonColor,
+			color: settings.polygonStrokeColor,
+			weight: settings.polygonStrokeWeight,
+			fillOpacity: settings.polygonOpacity,
+			opacity: settings.polygonStrokeOpacity
+		}).addTo(this.map);
+		return polygon;
+	}
+
+	removePolygon(polygon) {
+		this.map.removeLayer(polygon);
+	}
+
+	updatePolygonColor(polygon, settings, color) {
+		let style = {
+			fill: true,
+			fillColor: color,
+			color: color,
+			weight: settings.polygonStrokeWeight,
+			fillOpacity: settings.polygonOpacity,
+			opacity: settings.polygonStrokeOpacity
+		};
+		polygon.setStyle(style);
+	}
+
+	getPolygonLatLngs(polygon) {
+		return polygon.getLatLngs();
+	}
+
+	setPolygonLatLngs(polygon, latLngs) {
+		polygon.setLatLngs(latLngs);
+	}
+
+	fitBounds(bounds) {
+		if (bounds.isValid()) {
+			if (this.dontFitMapBounds && this.defaultZoomLevel) {
+				this.map.setZoom(this.defaultZoomLevel, {animate: false});
+				this.map.panTo(bounds.getCenter(), {animate: false});
+			} else {
+				var tbMap = this;
+				this.map.once('zoomend', function () {
+					if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) {
+						tbMap.map.setZoom(tbMap.minZoomLevel, {animate: false});
+					}
+				});
+				this.map.fitBounds(bounds, {padding: [50, 50], animate: false});
+			}
+		}
+	}
+
+	createLatLng(lat, lng) {
+		return L.latLng(lat, lng);
+	}
+
+	extendBoundsWithMarker(bounds, marker) {
+		bounds.extend(marker.getLatLng());
+	}
+
+	getMarkerPosition(marker) {
+		return marker.getLatLng();
+	}
+
+	setMarkerPosition(marker, latLng) {
+		marker.setLatLng(latLng);
+	}
+
+	getPolylineLatLngs(polyline) {
+		return polyline.getLatLngs();
+	}
+
+	setPolylineLatLngs(polyline, latLngs) {
+		polyline.setLatLngs(latLngs);
+	}
+
+	createBounds() {
+		return L.latLngBounds();
+	}
+
+	extendBounds(bounds, polyline) {
+		if (polyline && polyline.getLatLngs()) {
+			bounds.extend(polyline.getBounds());
+		}
+	}
+
+	invalidateSize() {
+		this.map.invalidateSize(true);
+	}
+
+	getTooltips() {
+		return this.tooltips;
+	}
 
 }
diff --git a/ui/src/app/widget/lib/tencent-map.js b/ui/src/app/widget/lib/tencent-map.js
index 94fc43a..17a6121 100644
--- a/ui/src/app/widget/lib/tencent-map.js
+++ b/ui/src/app/widget/lib/tencent-map.js
@@ -14,377 +14,425 @@
  * limitations under the License.
  */
 var tmGlobals = {
-    loadingTmId: null,
-    tmApiKeys: {}
+	loadingTmId: null,
+	tmApiKeys: {}
 }
 
 export default class TbTencentMap {
-    constructor($containerElement,utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, tmApiKey, tmDefaultMapType) {
-        var tbMap = this;
-        this.utils = utils;
-        this.defaultZoomLevel = defaultZoomLevel;
-        this.dontFitMapBounds = dontFitMapBounds;
-        this.minZoomLevel = minZoomLevel;
-        this.tooltips = [];
-        this.defaultMapType = tmDefaultMapType;
-
-        function clearGlobalId() {
-            if (tmGlobals.loadingTmId && tmGlobals.loadingTmId === tbMap.mapId) {
-                tmGlobals.loadingTmId = null;
-            }
-        }
-
-        function displayError(message) {
-            $containerElement.html( // eslint-disable-line angular/angularelement
-                "<div class='error'>"+ message + "</div>"
-            );
-        }
-
-        function initTencentMap() {
-            tbMap.map = new qq.maps.Map($containerElement[0], { // eslint-disable-line no-undef
-                scrollwheel: true,
-                mapTypeId: getTencentMapTypeId(tbMap.defaultMapType),
-                zoom: tbMap.defaultZoomLevel || 8
-            });
-
-            if (initCallback) {
-                initCallback();
-            }
-        }
-
-        /* eslint-disable no-undef */
-
-        function getTencentMapTypeId(mapType) {
-            var mapTypeId =qq.maps.MapTypeId.ROADMAP;
-            if (mapType) {
-                if (mapType === 'hybrid') {
-                   mapTypeId = qq.maps.MapTypeId.HYBRID;
-                } else if (mapType === 'satellite') {
-                   mapTypeId = qq.maps.MapTypeId.SATELLITE;
-                } else if (mapType === 'terrain') {
-                   mapTypeId = qq.maps.MapTypeId.ROADMAP;
-                }
-            }
-            return mapTypeId;
-        }
-
-        /* eslint-enable no-undef */
-
-        this.mapId = '' + Math.random().toString(36).substr(2, 9);
-        this.apiKey = tmApiKey || '84d6d83e0e51e481e50454ccbe8986b';
-
-        window.tm_authFailure = function() { // eslint-disable-line no-undef, angular/window-service
-            if (tmGlobals.loadingTmId && tmGlobals.loadingTmId === tbMap.mapId) {
-                tmGlobals.loadingTmId = null;
-                tmGlobals.tmApiKeys[tbMap.apiKey].error = 'Unable to authentificate for tencent Map API.</br>Please check your API key.';
-                displayError(tmGlobals.tmApiKeys[tbMap.apiKey].error);
-            }
-        };
-
-        this.initMapFunctionName = 'initTencentMap_' + this.mapId;
-
-        window[this.initMapFunctionName] = function() { // eslint-disable-line no-undef, angular/window-service
-            tmGlobals.tmApiKeys[tbMap.apiKey].loaded = true;
-            initTencentMap();
-            for (var p = 0; p < tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits.length; p++) {
-                var pendingInit = tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits[p];
-                pendingInit();
-            }
-            tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits = [];
-        };
-        if (this.apiKey && this.apiKey.length > 0) {
-            if (tmGlobals.tmApiKeys[this.apiKey]) {
-                if (tmGlobals.tmApiKeys[this.apiKey].error) {
-                    displayError(tmGlobals.tmApiKeys[this.apiKey].error);
-                } else if (tmGlobals.tmApiKeys[this.apiKey].loaded) {
-                    initTencentMap();
-                } else {
-                    tmGlobals.tmApiKeys[this.apiKey].pendingInits.push(initTencentMap);
-                }
-            } else {
-                tmGlobals.tmApiKeys[this.apiKey] = {
-                    loaded: false,
-                    pendingInits: []
-                };
-                var tencentMapScriptRes = 'https://map.qq.com/api/js?v=2.exp&key='+this.apiKey+'&callback='+this.initMapFunctionName;
-
-                tmGlobals.loadingTmId = this.mapId;
-                lazyLoad.load({ type: 'js', path: tencentMapScriptRes }).then( // eslint-disable-line no-undef
-                    function success() {
-                        setTimeout(clearGlobalId, 2000); // eslint-disable-line no-undef, angular/timeout-service
-                    },
-                    function fail(e) {
-                        clearGlobalId();
-                        tmGlobals.tmApiKeys[tbMap.apiKey].error = 'tencent map api load failed!</br>'+e;
-                        displayError(tmGlobals.tmApiKeys[tbMap.apiKey].error);
-                    }
-                );
-            }
-        } else {
-            displayError('No tencent Map Api Key provided!');
-        }
-    }
-
-    inited() {
-        return angular.isDefined(this.map);
-    }
-
-    createMarkerLabelStyle(settings) {
-        return {
-            width: "200px",
-            textAlign: "center",
-            color: settings.labelColor,
-            background: "none",
-            border: "none",
-            fontSize: "12px",
-            fontFamily: "\"Helvetica Neue\", Arial, Helvetica, sans-serif",
-            fontWeight: "bold"
-        };
-    }
-
-    /* eslint-disable no-undef,no-unused-vars*/
-    updateMarkerLabel(marker, settings) {
-        if (marker.label) {
-            marker.label.setContent(settings.labelText);
-            marker.label.setStyle(this.createMarkerLabelStyle(settings));
-        }
-    }
-    /* eslint-enable no-undef,no-unused-vars */
-
-    /* eslint-disable no-undef,no-unused-vars */
-    updateMarkerColor(marker, color) {
-        this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
-            marker.setIcon(iconInfo.icon);
-        });
-    }
-    /* eslint-enable no-undef,,no-unused-vars */
-
-    /* eslint-disable no-undef */
-    updateMarkerIcon(marker, settings) {
-        this.createMarkerIcon(marker, settings, (iconInfo) => {
-            marker.setIcon(iconInfo.icon);
-            if (marker.label) {
-                marker.label.setOffset(new qq.maps.Size(-100, -iconInfo.size[1]-20));
-            }
-        });
-    }
-    /* eslint-disable no-undef */
-
-    /* eslint-disable no-undef */
-    createMarkerIcon(marker, settings, onMarkerIconReady) {
-        var currentImage = settings.currentImage;
-        var tMap = this;
-        if (currentImage && currentImage.url) {
-            this.utils.loadImageAspect(currentImage.url).then(
-                (aspect) => {
-                    if (aspect) {
-                        var width;
-                        var height;
-                        if (aspect > 1) {
-                            width = currentImage.size;
-                            height = currentImage.size / aspect;
-                        } else {
-                            width = currentImage.size * aspect;
-                            height = currentImage.size;
-                        }
-                        var icon = new qq.maps.MarkerImage(currentImage.url,
-                            new qq.maps.Size(width, height),
-                            new qq.maps.Point(0,0),
-                            new qq.maps.Point(width/2, height),
-                            new qq.maps.Size(width, height));
-                        var iconInfo = {
-                            size: [width, height],
-                            icon: icon
-                        };
-                        onMarkerIconReady(iconInfo);
-                    } else {
-                        tMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
-                    }
-                }
-            );
-        } else {
-            this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
-        }
-    }
-    /* eslint-enable no-undef */
-
-    /* eslint-disable no-undef */
-    createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
-        var pinColor = color.substr(1);
-        var icon = new qq.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=%E2%80%A2|" + pinColor,
-            new qq.maps.Size(40, 37),
-            new qq.maps.Point(0,0),
-            new qq.maps.Point(10, 37));
-        var iconInfo = {
-            size: [40, 37],
-            icon: icon
-        };
-        onMarkerIconReady(iconInfo);
-    }
-    /* eslint-enable no-undef */
-
-    /* eslint-disable no-undef */
-    createMarker(location, dsIndex, settings, onClickListener, markerArgs) {
-        var marker = new qq.maps.Marker({
-            position: location
-        });
-        var tMap = this;
-        this.createMarkerIcon(marker, settings, (iconInfo) => {
-            marker.setIcon(iconInfo.icon);
-            marker.setMap(tMap.map);
-            if (settings.showLabel) {
-                marker.label = new qq.maps.Label({
-                    clickable: false,
-                    content: settings.labelText,
-                    offset: new qq.maps.Size(-100, -iconInfo.size[1]-20),
-                    style: tMap.createMarkerLabelStyle(settings),
-                    visible: true,
-                    position: location,
-                    map: tMap.map,
-                    zIndex: 1000
-                });
-            }
-        });
-
-        if (settings.displayTooltip) {
-            this.createTooltip(marker, dsIndex, settings, markerArgs);
-        }
-
-        if (onClickListener) {
-            qq.maps.event.addListener(marker, 'click', onClickListener);
-        }
-
-        return marker;
-    }
-
-    /* eslint-disable no-undef */
-    removeMarker(marker) {
-        marker.setMap(null);
-        if (marker.label) {
-            marker.label.setMap(null);
-        }
-    }
-
-    /* eslint-enable no-undef */
-
-    /* eslint-disable no-undef */
-    createTooltip(marker, dsIndex, settings, markerArgs) {
-        var popup = new qq.maps.InfoWindow({
-            map :this.map
-        });
-        var map = this;
-        qq.maps.event.addListener(marker, 'click', function() {
-            if (settings.autocloseTooltip) {
-                map.tooltips.forEach((tooltip) => {
-                    tooltip.popup.close();
-                });
-            }
-            popup.open();
-            popup.setPosition(marker);
-        });
-        this.tooltips.push( {
-            markerArgs: markerArgs,
-            popup: popup,
-            locationSettings: settings,
-            dsIndex: dsIndex
-        });
-    }
-    /* eslint-enable no-undef */
-
-    /* eslint-disable no-undef */
-    updatePolylineColor(polyline, settings, color) {
-        var options = {
-            path: polyline.getPath(),
-            strokeColor: color,
-            strokeOpacity: settings.strokeOpacity,
-            strokeWeight: settings.strokeWeight,
-            map: this.map
-        };
-        polyline.setOptions(options);
-    }
-    /* eslint-enable no-undef */
-
-    /* eslint-disable no-undef */
-    createPolyline(locations, settings) {
-        var polyline = new qq.maps.Polyline({
-            path: locations,
-            strokeColor: settings.color,
-            strokeOpacity: settings.strokeOpacity,
-            strokeWeight: settings.strokeWeight,
-            map: this.map
-        });
-
-        return polyline;
-    }
-    /* eslint-enable no-undef */
-
-    removePolyline(polyline) {
-        polyline.setMap(null);
-    }
-
-    /* eslint-disable no-undef ,no-unused-vars*/
-    fitBounds(bounds) {
-        if (this.dontFitMapBounds && this.defaultZoomLevel) {
-            this.map.setZoom(this.defaultZoomLevel);
-            this.map.setCenter(bounds.getCenter());
-        } else {
-            var tbMap = this;
-            qq.maps.event.addListenerOnce(this.map, 'bounds_changed', function() { // eslint-disable-line no-undef
-                if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) {
-                    tbMap.map.setZoom(tbMap.minZoomLevel);
-                }
-            });
-            this.map.fitBounds(bounds);
-        }
-    }
-    /* eslint-enable no-undef,no-unused-vars */
-
-    createLatLng(lat, lng) {
-        return new qq.maps.LatLng(lat, lng); // eslint-disable-line no-undef
-    }
-
-    extendBoundsWithMarker(bounds, marker) {
-        bounds.extend(marker.getPosition());
-    }
-
-    getMarkerPosition(marker) {
-        return marker.getPosition();
-    }
-
-    setMarkerPosition(marker, latLng) {
-        marker.setPosition(latLng);
-        if (marker.label) {
-            marker.label.setPosition(latLng);
-        }
-    }
-
-    getPolylineLatLngs(polyline) {
-        return polyline.getPath().getArray();
-    }
-
-    setPolylineLatLngs(polyline, latLngs) {
-        polyline.setPath(latLngs);
-    }
-
-    createBounds() {
-        return new qq.maps.LatLngBounds(); // eslint-disable-line no-undef
-    }
-
-    extendBounds(bounds, polyline) {
-        if (polyline && polyline.getPath()) {
-            var locations = polyline.getPath();
-            for (var i = 0; i < locations.getLength(); i++) {
-                bounds.extend(locations.getAt(i));
-            }
-        }
-    }
-
-    invalidateSize() {
-        qq.maps.event.trigger(this.map, "resize"); // eslint-disable-line no-undef
-    }
-
-    getTooltips() {
-        return this.tooltips;
-    }
+	constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, tmApiKey, tmDefaultMapType) {
+		var tbMap = this;
+		this.utils = utils;
+		this.defaultZoomLevel = defaultZoomLevel;
+		this.dontFitMapBounds = dontFitMapBounds;
+		this.minZoomLevel = minZoomLevel;
+		this.tooltips = [];
+		this.defaultMapType = tmDefaultMapType;
+
+		function clearGlobalId() {
+			if (tmGlobals.loadingTmId && tmGlobals.loadingTmId === tbMap.mapId) {
+				tmGlobals.loadingTmId = null;
+			}
+		}
+
+		function displayError(message) {
+			$containerElement.html( // eslint-disable-line angular/angularelement
+				"<div class='error'>" + message + "</div>"
+			);
+		}
+
+		function initTencentMap() {
+			tbMap.map = new qq.maps.Map($containerElement[0], { // eslint-disable-line no-undef
+				scrollwheel: true,
+				mapTypeId: getTencentMapTypeId(tbMap.defaultMapType),
+				zoom: tbMap.defaultZoomLevel || 8
+			});
+
+			if (initCallback) {
+				initCallback();
+			}
+		}
+
+		/* eslint-disable no-undef */
+
+		function getTencentMapTypeId(mapType) {
+			var mapTypeId = qq.maps.MapTypeId.ROADMAP;
+			if (mapType) {
+				if (mapType === 'hybrid') {
+					mapTypeId = qq.maps.MapTypeId.HYBRID;
+				} else if (mapType === 'satellite') {
+					mapTypeId = qq.maps.MapTypeId.SATELLITE;
+				} else if (mapType === 'terrain') {
+					mapTypeId = qq.maps.MapTypeId.ROADMAP;
+				}
+			}
+			return mapTypeId;
+		}
+
+		/* eslint-enable no-undef */
+
+		this.mapId = '' + Math.random().toString(36).substr(2, 9);
+		this.apiKey = tmApiKey || '84d6d83e0e51e481e50454ccbe8986b';
+
+		window.tm_authFailure = function () { // eslint-disable-line no-undef, angular/window-service
+			if (tmGlobals.loadingTmId && tmGlobals.loadingTmId === tbMap.mapId) {
+				tmGlobals.loadingTmId = null;
+				tmGlobals.tmApiKeys[tbMap.apiKey].error = 'Unable to authentificate for tencent Map API.</br>Please check your API key.';
+				displayError(tmGlobals.tmApiKeys[tbMap.apiKey].error);
+			}
+		};
+
+		this.initMapFunctionName = 'initTencentMap_' + this.mapId;
+
+		window[this.initMapFunctionName] = function () { // eslint-disable-line no-undef, angular/window-service
+			tmGlobals.tmApiKeys[tbMap.apiKey].loaded = true;
+			initTencentMap();
+			for (var p = 0; p < tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits.length; p++) {
+				var pendingInit = tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits[p];
+				pendingInit();
+			}
+			tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits = [];
+		};
+		if (this.apiKey && this.apiKey.length > 0) {
+			if (tmGlobals.tmApiKeys[this.apiKey]) {
+				if (tmGlobals.tmApiKeys[this.apiKey].error) {
+					displayError(tmGlobals.tmApiKeys[this.apiKey].error);
+				} else if (tmGlobals.tmApiKeys[this.apiKey].loaded) {
+					initTencentMap();
+				} else {
+					tmGlobals.tmApiKeys[this.apiKey].pendingInits.push(initTencentMap);
+				}
+			} else {
+				tmGlobals.tmApiKeys[this.apiKey] = {
+					loaded: false,
+					pendingInits: []
+				};
+				var tencentMapScriptRes = 'https://map.qq.com/api/js?v=2.exp&key=' + this.apiKey + '&callback=' + this.initMapFunctionName;
+
+				tmGlobals.loadingTmId = this.mapId;
+				lazyLoad.load({type: 'js', path: tencentMapScriptRes}).then( // eslint-disable-line no-undef
+					function success() {
+						setTimeout(clearGlobalId, 2000); // eslint-disable-line no-undef, angular/timeout-service
+					},
+					function fail(e) {
+						clearGlobalId();
+						tmGlobals.tmApiKeys[tbMap.apiKey].error = 'tencent map api load failed!</br>' + e;
+						displayError(tmGlobals.tmApiKeys[tbMap.apiKey].error);
+					}
+				);
+			}
+		} else {
+			displayError('No tencent Map Api Key provided!');
+		}
+	}
+
+	inited() {
+		return angular.isDefined(this.map);
+	}
+
+	createMarkerLabelStyle(settings) {
+		return {
+			width: "200px",
+			textAlign: "center",
+			color: settings.labelColor,
+			background: "none",
+			border: "none",
+			fontSize: "12px",
+			fontFamily: "\"Helvetica Neue\", Arial, Helvetica, sans-serif",
+			fontWeight: "bold"
+		};
+	}
+
+	/* eslint-disable no-undef,no-unused-vars*/
+	updateMarkerLabel(marker, settings) {
+		if (marker.label) {
+			marker.label.setContent(settings.labelText);
+			marker.label.setStyle(this.createMarkerLabelStyle(settings));
+		}
+	}
+
+	/* eslint-enable no-undef,no-unused-vars */
+
+	/* eslint-disable no-undef,no-unused-vars */
+	updateMarkerColor(marker, color) {
+		this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
+			marker.setIcon(iconInfo.icon);
+		});
+	}
+
+	/* eslint-enable no-undef,,no-unused-vars */
+
+	/* eslint-disable no-undef */
+	updateMarkerIcon(marker, settings) {
+		this.createMarkerIcon(marker, settings, (iconInfo) => {
+			marker.setIcon(iconInfo.icon);
+			if (marker.label) {
+				marker.label.setOffset(new qq.maps.Size(-100, -iconInfo.size[1] - 20));
+			}
+		});
+	}
+
+	/* eslint-disable no-undef */
+
+	/* eslint-disable no-undef */
+	createMarkerIcon(marker, settings, onMarkerIconReady) {
+		var currentImage = settings.currentImage;
+		var tMap = this;
+		if (currentImage && currentImage.url) {
+			this.utils.loadImageAspect(currentImage.url).then(
+				(aspect) => {
+					if (aspect) {
+						var width;
+						var height;
+						if (aspect > 1) {
+							width = currentImage.size;
+							height = currentImage.size / aspect;
+						} else {
+							width = currentImage.size * aspect;
+							height = currentImage.size;
+						}
+						var icon = new qq.maps.MarkerImage(currentImage.url,
+							new qq.maps.Size(width, height),
+							new qq.maps.Point(0, 0),
+							new qq.maps.Point(width / 2, height),
+							new qq.maps.Size(width, height));
+						var iconInfo = {
+							size: [width, height],
+							icon: icon
+						};
+						onMarkerIconReady(iconInfo);
+					} else {
+						tMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
+					}
+				}
+			);
+		} else {
+			this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
+		}
+	}
+
+	/* eslint-enable no-undef */
+
+	/* eslint-disable no-undef */
+	createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
+		var pinColor = color.substr(1);
+		var icon = new qq.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=%E2%80%A2|" + pinColor,
+			new qq.maps.Size(40, 37),
+			new qq.maps.Point(0, 0),
+			new qq.maps.Point(10, 37));
+		var iconInfo = {
+			size: [40, 37],
+			icon: icon
+		};
+		onMarkerIconReady(iconInfo);
+	}
+
+	/* eslint-enable no-undef */
+
+	/* eslint-disable no-undef */
+	createMarker(location, dsIndex, settings, onClickListener, markerArgs) {
+		var marker = new qq.maps.Marker({
+			position: location
+		});
+		var tMap = this;
+		this.createMarkerIcon(marker, settings, (iconInfo) => {
+			marker.setIcon(iconInfo.icon);
+			marker.setMap(tMap.map);
+			if (settings.showLabel) {
+				marker.label = new qq.maps.Label({
+					clickable: false,
+					content: settings.labelText,
+					offset: new qq.maps.Size(-100, -iconInfo.size[1] - 20),
+					style: tMap.createMarkerLabelStyle(settings),
+					visible: true,
+					position: location,
+					map: tMap.map,
+					zIndex: 1000
+				});
+			}
+		});
+
+		if (settings.displayTooltip) {
+			this.createTooltip(marker, dsIndex, settings, markerArgs);
+		}
+
+		if (onClickListener) {
+			qq.maps.event.addListener(marker, 'click', onClickListener);
+		}
+
+		return marker;
+	}
+
+	/* eslint-disable no-undef */
+	removeMarker(marker) {
+		marker.setMap(null);
+		if (marker.label) {
+			marker.label.setMap(null);
+		}
+	}
+
+	/* eslint-enable no-undef */
+
+	/* eslint-disable no-undef */
+	createTooltip(marker, dsIndex, settings, markerArgs) {
+		var popup = new qq.maps.InfoWindow({
+			map: this.map
+		});
+		var map = this;
+		qq.maps.event.addListener(marker, 'click', function () {
+			if (settings.autocloseTooltip) {
+				map.tooltips.forEach((tooltip) => {
+					tooltip.popup.close();
+				});
+			}
+			popup.open();
+			popup.setPosition(marker);
+		});
+		this.tooltips.push({
+			markerArgs: markerArgs,
+			popup: popup,
+			locationSettings: settings,
+			dsIndex: dsIndex
+		});
+	}
+
+	/* eslint-enable no-undef */
+
+	/* eslint-disable no-undef */
+	updatePolylineColor(polyline, settings, color) {
+		var options = {
+			path: polyline.getPath(),
+			strokeColor: color,
+			strokeOpacity: settings.strokeOpacity,
+			strokeWeight: settings.strokeWeight,
+			map: this.map
+		};
+		polyline.setOptions(options);
+	}
+
+	/* eslint-enable no-undef */
+
+	/* eslint-disable no-undef */
+	createPolyline(locations, settings) {
+		var polyline = new qq.maps.Polyline({
+			path: locations,
+			strokeColor: settings.color,
+			strokeOpacity: settings.strokeOpacity,
+			strokeWeight: settings.strokeWeight,
+			map: this.map
+		});
+
+		return polyline;
+	}
+
+	/* eslint-enable no-undef */
+
+	removePolyline(polyline) {
+		polyline.setMap(null);
+	}
+
+	/* eslint-disable no-undef */
+	createPolygon(latLangs, settings) {
+		let polygon = new qq.maps.Polygon({
+			map: this.map,
+			path: latLangs,
+			strokeColor: settings.polygonStrokeColor,
+			fillColor: settings.polygonColor,
+			strokeWeight: settings.polygonStrokeWeight
+		});
+		return polygon;
+	}
+	/* eslint-disable no-undef */
+
+	removePolygon (polygon) {
+		polygon.setMap(null);
+	}
+
+	/* eslint-disable no-undef,no-unused-vars */
+	updatePolygonColor (polygon, settings, color) {
+		let options = {
+			path: polygon.getPath(),
+			map: this.map,
+			strokeColor: color,
+			fillColor: color,
+			strokeWeight: settings.polygonStrokeWeight
+		}
+
+	}
+	/* eslint-disable no-undef ,no-unused-vars*/
+
+
+	getPolygonLatLngs(polygon) {
+		return polygon.getPath().getArray();
+	}
+
+	setPolygonLatLngs(polygon, latLngs) {
+		polygon.setPath(latLngs);
+	}
+
+	/* eslint-disable no-undef ,no-unused-vars*/
+	fitBounds(bounds) {
+		if (this.dontFitMapBounds && this.defaultZoomLevel) {
+			this.map.setZoom(this.defaultZoomLevel);
+			this.map.setCenter(bounds.getCenter());
+		} else {
+			var tbMap = this;
+			qq.maps.event.addListenerOnce(this.map, 'bounds_changed', function () { // eslint-disable-line no-undef
+				if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) {
+					tbMap.map.setZoom(tbMap.minZoomLevel);
+				}
+			});
+			this.map.fitBounds(bounds);
+		}
+	}
+
+	/* eslint-enable no-undef,no-unused-vars */
+
+	createLatLng(lat, lng) {
+		return new qq.maps.LatLng(lat, lng); // eslint-disable-line no-undef
+	}
+
+	extendBoundsWithMarker(bounds, marker) {
+		bounds.extend(marker.getPosition());
+	}
+
+	getMarkerPosition(marker) {
+		return marker.getPosition();
+	}
+
+	setMarkerPosition(marker, latLng) {
+		marker.setPosition(latLng);
+		if (marker.label) {
+			marker.label.setPosition(latLng);
+		}
+	}
+
+	getPolylineLatLngs(polyline) {
+		return polyline.getPath().getArray();
+	}
+
+	setPolylineLatLngs(polyline, latLngs) {
+		polyline.setPath(latLngs);
+	}
+
+	createBounds() {
+		return new qq.maps.LatLngBounds(); // eslint-disable-line no-undef
+	}
+
+	extendBounds(bounds, polyline) {
+		if (polyline && polyline.getPath()) {
+			var locations = polyline.getPath();
+			for (var i = 0; i < locations.getLength(); i++) {
+				bounds.extend(locations.getAt(i));
+			}
+		}
+	}
+
+	invalidateSize() {
+		qq.maps.event.trigger(this.map, "resize"); // eslint-disable-line no-undef
+	}
+
+	getTooltips() {
+		return this.tooltips;
+	}
 
 }