Feature/clustering market (#2050)

* Add support clustering and creating setting schema from google and tencent

* Add settings for leaflet

* Fix name setting

* Fix text setting and change zoom level
This commit is contained in:
Vladyslav 2019-10-17 18:26:10 +03:00 committed by Igor Kulikov
parent 45059ae660
commit 25e36583f8
6 changed files with 387 additions and 198 deletions

357
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -65,6 +65,7 @@
"leaflet": "^1.5.1",
"leaflet-polylinedecorator": "^1.6.0",
"leaflet-providers": "^1.8.0",
"leaflet.markercluster": "^1.4.1",
"material-steppers": "git://github.com/thingsboard/material-steppers.git#master",
"material-ui": "^0.16.1",
"material-ui-number-input": "^5.0.16",

View File

@ -19,7 +19,7 @@ var gmGlobals = {
}
export default class TbGoogleMap {
constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, disableScrollZooming, minZoomLevel, gmApiKey, gmDefaultMapType, defaultCenterPosition) {
constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, disableScrollZooming, minZoomLevel, gmApiKey, gmDefaultMapType, defaultCenterPosition, markerClusteringSetting) {
var tbMap = this;
this.utils = utils;
@ -29,6 +29,7 @@ export default class TbGoogleMap {
this.tooltips = [];
this.defaultMapType = gmDefaultMapType;
this.defaultCenterPosition = defaultCenterPosition;
this.isMarketCluster = markerClusteringSetting.isMarketCluster;
function clearGlobalId() {
if (gmGlobals.loadingGmId && gmGlobals.loadingGmId === tbMap.mapId) {
@ -49,6 +50,10 @@ export default class TbGoogleMap {
zoom: tbMap.defaultZoomLevel || 8,
center: new google.maps.LatLng(tbMap.defaultCenterPosition[0], tbMap.defaultCenterPosition[1]) // eslint-disable-line no-undef
});
if (tbMap.isMarketCluster){
tbMap.markersCluster = new MarkerClusterer(tbMap.map, [], // eslint-disable-line no-undef
angular.merge({imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'}, markerClusteringSetting));
}
if (initCallback) {
initCallback();
}
@ -87,7 +92,10 @@ export default class TbGoogleMap {
this.initMapFunctionName = 'initGoogleMap_' + this.mapId;
window[this.initMapFunctionName] = function() { // eslint-disable-line no-undef, angular/window-service
lazyLoad.load({ type: 'js', path: 'https://unpkg.com/@google/markerwithlabel@1.2.3/src/markerwithlabel.js' }).then( // eslint-disable-line no-undef
lazyLoad.load([ // eslint-disable-line no-undef
{ type: 'js', path: 'https://unpkg.com/@google/markerwithlabel@1.2.3/src/markerwithlabel.js' },
{ type: 'js', path: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/markerclusterer.js' }
]).then(
function success() {
gmGlobals.gmApiKeys[tbMap.apiKey].loaded = true;
initGoogleMap();
@ -105,6 +113,7 @@ export default class TbGoogleMap {
);
};
/* eslint-enable no-undef */
if (this.apiKey && this.apiKey.length > 0) {
if (gmGlobals.gmApiKeys[this.apiKey]) {
@ -143,6 +152,10 @@ export default class TbGoogleMap {
return angular.isDefined(this.map);
}
getContainer() {
return this.isMarketCluster ? this.markersCluster : this.map;
}
/* eslint-disable no-undef */
updateMarkerLabel(marker, settings) {
marker.set('labelContent', '<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>');
@ -240,7 +253,11 @@ export default class TbGoogleMap {
if (settings.showLabel) {
marker.set('labelAnchor', new google.maps.Point(100, iconInfo.size[1] + 20));
}
marker.setMap(gMap.map);
if(gMap.isMarketCluster) {
gMap.getContainer().addMarker(marker);
} else {
marker.setMap(gMap.getContainer());
}
});
if (settings.displayTooltip) {

View File

@ -48,7 +48,7 @@ export default class TbMapWidgetV2 {
};
if (settings.defaultZoomLevel) {
if (settings.defaultZoomLevel > 0 && settings.defaultZoomLevel < 21) {
if (settings.defaultZoomLevel >= 0 && settings.defaultZoomLevel < 21) {
this.defaultZoomLevel = Math.floor(settings.defaultZoomLevel);
}
}
@ -69,6 +69,46 @@ export default class TbMapWidgetV2 {
var minZoomLevel = this.drawRoutes ? 18 : 15;
let markerClusteringSetting = {
isMarketCluster: false
};
if (settings.useClusterMarkers === true){
if (mapProvider === 'google-map' || mapProvider === 'tencent-map') {
markerClusteringSetting = {
isMarketCluster: true,
zoomOnClick: settings.zoomOnClick,
averageCenter: true
};
if(angular.isDefined(settings.maxZoom) && settings.maxZoom >= 0 && settings.maxZoom < 19){
markerClusteringSetting.maxZoom = Math.floor(settings.maxZoom);
}
if(angular.isDefined(settings.gridSize) && settings.gridSize > 0){
markerClusteringSetting.gridSize = Math.floor(settings.gridSize);
}
if(angular.isDefined(settings.minimumClusterSize) && settings.minimumClusterSize > 1){
markerClusteringSetting.minimumClusterSize = Math.ceil(settings.minimumClusterSize);
}
} else if(mapProvider === 'openstreet-map' || mapProvider === 'here') {
markerClusteringSetting = {
isMarketCluster: true,
zoomToBoundsOnClick: settings.zoomOnClick,
showCoverageOnHover: settings.showCoverageOnHover,
removeOutsideVisibleBounds: settings.removeOutsideVisibleBounds,
animate: settings.animate,
chunkedLoading: settings.chunkedLoading
};
if(angular.isDefined(settings.maxClusterRadius) && settings.maxClusterRadius > 0){
markerClusteringSetting.maxClusterRadius = Math.floor(settings.maxClusterRadius);
}
if(angular.isDefined(settings.maxZoom) && settings.maxZoom >= 0 && settings.maxZoom < 19){
markerClusteringSetting.disableClusteringAtZoom = Math.floor(settings.maxZoom);
}
}
}
var initCallback = function () {
tbMap.update();
@ -86,7 +126,7 @@ export default class TbMapWidgetV2 {
let openStreetMapProvider = {};
if (mapProvider === 'google-map') {
this.map = new TbGoogleMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, settings.disableScrollZooming, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType, settings.defaultCenterPosition);
this.map = new TbGoogleMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, settings.disableScrollZooming, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType, settings.defaultCenterPosition, markerClusteringSetting);
} else if (mapProvider === 'openstreet-map') {
if (settings.useCustomProvider && settings.customProviderTileUrl) {
openStreetMapProvider.name = settings.customProviderTileUrl;
@ -94,10 +134,10 @@ export default class TbMapWidgetV2 {
} else {
openStreetMapProvider.name = settings.mapProvider;
}
this.map = new TbOpenStreetMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, settings.disableScrollZooming, minZoomLevel, openStreetMapProvider, null,settings.defaultCenterPosition);
this.map = new TbOpenStreetMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, settings.disableScrollZooming, minZoomLevel, openStreetMapProvider, null,settings.defaultCenterPosition, markerClusteringSetting);
} else if (mapProvider === 'here') {
openStreetMapProvider.name = settings.mapProvider;
this.map = new TbOpenStreetMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, settings.disableScrollZooming, minZoomLevel, openStreetMapProvider, settings.credentials, settings.defaultCenterPosition);
this.map = new TbOpenStreetMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, settings.disableScrollZooming, minZoomLevel, openStreetMapProvider, settings.credentials, settings.defaultCenterPosition, markerClusteringSetting);
} else if (mapProvider === 'image-map') {
this.map = new TbImageMap(this.ctx, $element, this.utils, initCallback,
settings.mapImageUrl,
@ -107,7 +147,7 @@ export default class TbMapWidgetV2 {
settings.imageUrlAttribute,
settings.useDefaultCenterPosition ? settings.defaultCenterPosition: null);
} else if (mapProvider === 'tencent-map') {
this.map = new TbTencentMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, settings.disableScrollZooming, minZoomLevel, settings.tmApiKey, settings.tmDefaultMapType, settings.defaultCenterPosition);
this.map = new TbTencentMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, settings.disableScrollZooming, minZoomLevel, settings.tmApiKey, settings.tmDefaultMapType, settings.defaultCenterPosition, markerClusteringSetting);
}
@ -728,6 +768,24 @@ export default class TbMapWidgetV2 {
"formIndex":schema.groupInfoes.length,
"GroupTitle":"Route Map Settings"
});
} else if (mapProvider !== 'image-map'){
angular.merge(schema.schema.properties, markerClusteringSettingsSchema.schema.properties);
schema.schema.required = schema.schema.required.concat(markerClusteringSettingsSchema.schema.required);
schema.form.push(markerClusteringSettingsSchema.form);
if (mapProvider === 'google-map' || mapProvider === 'tencent-map') {
angular.merge(schema.schema.properties, markerClusteringSettingsSchemaGoogle.schema.properties);
schema.schema.required = schema.schema.required.concat(markerClusteringSettingsSchemaGoogle.schema.required);
schema.form[schema.form.length -1] = schema.form[schema.form.length -1].concat(markerClusteringSettingsSchemaGoogle.form);
}
if (mapProvider === 'openstreet-map' || mapProvider === 'here') {
angular.merge(schema.schema.properties, markerClusteringSettingsSchemaLeaflet.schema.properties);
schema.schema.required = schema.schema.required.concat(markerClusteringSettingsSchemaLeaflet.schema.required);
schema.form[schema.form.length -1] = schema.form[schema.form.length -1].concat(markerClusteringSettingsSchemaLeaflet.form);
}
schema.groupInfoes.push({
"formIndex":schema.groupInfoes.length,
"GroupTitle":"Markers Clustering Settings"
});
}
return schema;
}
@ -975,7 +1033,7 @@ const commonMapSettingsSchema =
"type": "object",
"properties": {
"defaultZoomLevel": {
"title": "Default map zoom level (1 - 20)",
"title": "Default map zoom level (0 - 20)",
"type": "number"
},
"useDefaultCenterPosition": {
@ -1251,6 +1309,103 @@ const routeMapSettingsSchema =
]
};
const markerClusteringSettingsSchema =
{
"schema": {
"title": "Markers Clustering Configuration",
"type": "object",
"properties": {
"useClusterMarkers": {
"title": "Use map markers clustering",
"type": "boolean",
"default": false
},
"zoomOnClick": {
"title": "Zoom when clicking on a cluster",
"type": "boolean",
"default": true
},
"maxZoom": {
"title": "The maximum zoom level when a marker can be part of a cluster (0 - 18)",
"type": "number"
}
},
"required": []
},
"form": [
"useClusterMarkers",
"zoomOnClick",
"maxZoom"
]
};
const markerClusteringSettingsSchemaGoogle =
{
"schema": {
"title": "Marker Clustering Configuration Google",
"type": "object",
"properties": {
"gridSize": {
"title": "Maximum radius that a cluster will cover in pixels",
"type": "number",
"default": 60
},
"minimumClusterSize": {
"title": "The minimum number of markers in a cluster",
"type": "number"
}
},
"required": []
},
"form": [
"gridSize",
"minimumClusterSize"
]
};
const markerClusteringSettingsSchemaLeaflet =
{
"schema": {
"title": "Markers Clustering Configuration Leaflet",
"type": "object",
"properties": {
"showCoverageOnHover": {
"title": "Show the bounds of markers when mouse over a cluster",
"type": "boolean",
"default": true
},
"animate": {
"title": "Show animation on markers when zooming",
"type": "boolean",
"default": true
},
"maxClusterRadius": {
"title": "Maximum radius that a cluster will cover in pixels",
"type": "number",
"default": 80
},
"chunkedLoading": {
"title": "Use chunks for adding markers so that the page does not freeze",
"type": "boolean",
"default": false
},
"removeOutsideVisibleBounds": {
"title": "Use lazy load for adding markers",
"type": "boolean",
"default": true
}
},
"required": []
},
"form": [
"showCoverageOnHover",
"animate",
"maxClusterRadius",
"chunkedLoading",
"removeOutsideVisibleBounds"
]
};
const imageMapSettingsSchema =
{
"schema": {

View File

@ -14,18 +14,22 @@
* limitations under the License.
*/
import 'leaflet/dist/leaflet.css';
import 'leaflet.markercluster/dist/MarkerCluster.css'
import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
import * as L from 'leaflet';
import 'leaflet-providers';
import 'leaflet.markercluster/dist/leaflet.markercluster'
export default class TbOpenStreetMap {
constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, disableScrollZooming, minZoomLevel, mapProvider, credentials, defaultCenterPosition) {
constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, disableScrollZooming, minZoomLevel, mapProvider, credentials, defaultCenterPosition, markerClusteringSetting) {
this.utils = utils;
this.defaultZoomLevel = defaultZoomLevel;
this.dontFitMapBounds = dontFitMapBounds;
this.minZoomLevel = minZoomLevel;
this.tooltips = [];
this.isMarketCluster = markerClusteringSetting.isMarketCluster;
if (!mapProvider) {
mapProvider = {
@ -48,6 +52,11 @@ export default class TbOpenStreetMap {
var tileLayer = mapProvider.isCustom ? L.tileLayer(mapProvider.name) : L.tileLayer.provider(mapProvider.name, credentials);
tileLayer.addTo(this.map);
if (this.isMarketCluster) {
this.markersCluster = L.markerClusterGroup(markerClusteringSetting);
this.map.addLayer(this.markersCluster);
}
if (initCallback) {
setTimeout(initCallback, 0); //eslint-disable-line
}
@ -58,6 +67,10 @@ export default class TbOpenStreetMap {
return angular.isDefined(this.map);
}
getContainer() {
return this.isMarketCluster ? this.markersCluster : this.map;
}
updateMarkerLabel(marker, settings) {
marker.unbindTooltip();
marker.bindTooltip('<div style="color: ' + settings.labelColor + ';"><b>' + settings.labelText + '</b></div>',
@ -147,7 +160,7 @@ export default class TbOpenStreetMap {
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);
marker.addTo(opMap.getContainer());
});
if (settings.displayTooltip) {
@ -162,7 +175,7 @@ export default class TbOpenStreetMap {
}
removeMarker(marker) {
this.map.removeLayer(marker);
this.getContainer().removeLayer(marker);
}
createTooltip(marker, dsIndex, settings, markerArgs) {

View File

@ -19,7 +19,7 @@ var tmGlobals = {
}
export default class TbTencentMap {
constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, disableScrollZooming, minZoomLevel, tmApiKey, tmDefaultMapType, defaultCenterPosition) {
constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, disableScrollZooming, minZoomLevel, tmApiKey, tmDefaultMapType, defaultCenterPosition, markerClusteringSetting) {
var tbMap = this;
this.utils = utils;
this.defaultZoomLevel = defaultZoomLevel;
@ -28,6 +28,7 @@ export default class TbTencentMap {
this.tooltips = [];
this.defaultMapType = tmDefaultMapType;
this.defaultCenterPosition =defaultCenterPosition;
this.isMarketCluster = markerClusteringSetting.isMarketCluster;
function clearGlobalId() {
if (tmGlobals.loadingTmId && tmGlobals.loadingTmId === tbMap.mapId) {
@ -49,6 +50,13 @@ export default class TbTencentMap {
center: new qq.maps.LatLng(tbMap.defaultCenterPosition[0],tbMap.defaultCenterPosition[1]) // eslint-disable-line no-undef
});
if (tbMap.isMarketCluster){
tbMap.markersCluster = new qq.maps.MarkerCluster( // eslint-disable-line no-undef
angular.merge({map:tbMap.map}, markerClusteringSetting)
);
}
if (initCallback) {
initCallback();
}
@ -238,7 +246,11 @@ export default class TbTencentMap {
var tMap = this;
this.createMarkerIcon(marker, settings, (iconInfo) => {
marker.setIcon(iconInfo.icon);
marker.setMap(tMap.map);
if(tMap.isMarketCluster) {
tMap.markersCluster.addMarker(marker);
} else {
marker.setMap(tMap.map);
}
if (settings.showLabel) {
marker.label = new qq.maps.Label({
clickable: false,