committed by
GitHub
8 changed files with 975 additions and 9 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,304 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
import $ from 'jquery'; |
||||
|
|
||||
|
var gmGlobals = { |
||||
|
loadingGmId: null, |
||||
|
gmApiKeys: {} |
||||
|
} |
||||
|
|
||||
|
export default class TbGoogleMap { |
||||
|
constructor(containerElement, defaultZoomLevel, dontFitMapBounds, minZoomLevel, gmApiKey) { |
||||
|
|
||||
|
var tbMap = this; |
||||
|
this.defaultZoomLevel = defaultZoomLevel; |
||||
|
this.dontFitMapBounds = dontFitMapBounds; |
||||
|
this.minZoomLevel = minZoomLevel; |
||||
|
this.tooltips = []; |
||||
|
|
||||
|
function clearGlobalId() { |
||||
|
if (gmGlobals.loadingGmId && gmGlobals.loadingGmId === tbMap.mapId) { |
||||
|
gmGlobals.loadingGmId = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function displayError(message) { |
||||
|
$(containerElement).html( // eslint-disable-line angular/angularelement
|
||||
|
"<div class='error'>"+ message + "</div>" |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
function initGoogleMap() { |
||||
|
|
||||
|
tbMap.map = new google.maps.Map(containerElement, { // eslint-disable-line no-undef
|
||||
|
scrollwheel: false, |
||||
|
zoom: tbMap.defaultZoomLevel || 8 |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
this.mapId = '' + Math.random().toString(36).substr(2, 9); |
||||
|
this.apiKey = gmApiKey || ''; |
||||
|
|
||||
|
window.gm_authFailure = function() { // eslint-disable-line no-undef, angular/window-service
|
||||
|
if (gmGlobals.loadingGmId && gmGlobals.loadingGmId === tbMap.mapId) { |
||||
|
gmGlobals.loadingGmId = null; |
||||
|
gmGlobals.gmApiKeys[tbMap.apiKey].error = 'Unable to authentificate for Google Map API.</br>Please check your API key.'; |
||||
|
displayError(gmGlobals.gmApiKeys[tbMap.apiKey].error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
this.initMapFunctionName = 'initGoogleMap_' + this.mapId; |
||||
|
|
||||
|
window[this.initMapFunctionName] = function() { // eslint-disable-line no-undef, angular/window-service
|
||||
|
lazyLoad.load({ type: 'js', path: 'https://cdn.rawgit.com/googlemaps/v3-utility-library/master/markerwithlabel/src/markerwithlabel.js' }).then( // eslint-disable-line no-undef
|
||||
|
function success() { |
||||
|
gmGlobals.gmApiKeys[tbMap.apiKey].loaded = true; |
||||
|
initGoogleMap(); |
||||
|
for (var p in gmGlobals.gmApiKeys[tbMap.apiKey].pendingInits) { |
||||
|
var pendingInit = gmGlobals.gmApiKeys[tbMap.apiKey].pendingInits[p]; |
||||
|
pendingInit(); |
||||
|
} |
||||
|
gmGlobals.gmApiKeys[tbMap.apiKey].pendingInits = []; |
||||
|
}, |
||||
|
function fail(e) { |
||||
|
clearGlobalId(); |
||||
|
gmGlobals.gmApiKeys[tbMap.apiKey].error = 'Google map api load failed!</br>'+e; |
||||
|
displayError(gmGlobals.gmApiKeys[tbMap.apiKey].error); |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
if (this.apiKey && this.apiKey.length > 0) { |
||||
|
if (gmGlobals.gmApiKeys[this.apiKey]) { |
||||
|
if (gmGlobals.gmApiKeys[this.apiKey].error) { |
||||
|
displayError(gmGlobals.gmApiKeys[this.apiKey].error); |
||||
|
} else if (gmGlobals.gmApiKeys[this.apiKey].loaded) { |
||||
|
initGoogleMap(); |
||||
|
} else { |
||||
|
gmGlobals.gmApiKeys[this.apiKey].pendingInits.push(initGoogleMap); |
||||
|
} |
||||
|
} else { |
||||
|
gmGlobals.gmApiKeys[this.apiKey] = { |
||||
|
loaded: false, |
||||
|
pendingInits: [] |
||||
|
}; |
||||
|
var googleMapScriptRes = 'https://maps.googleapis.com/maps/api/js?key='+this.apiKey+'&callback='+this.initMapFunctionName; |
||||
|
|
||||
|
gmGlobals.loadingGmId = this.mapId; |
||||
|
lazyLoad.load({ type: 'js', path: googleMapScriptRes }).then( // eslint-disable-line no-undef
|
||||
|
function success() { |
||||
|
setTimeout(clearGlobalId, 2000); // eslint-disable-line no-undef, angular/timeout-service
|
||||
|
}, |
||||
|
function fail(e) { |
||||
|
clearGlobalId(); |
||||
|
gmGlobals.gmApiKeys[tbMap.apiKey].error = 'Google map api load failed!</br>'+e; |
||||
|
displayError(gmGlobals.gmApiKeys[tbMap.apiKey].error); |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} else { |
||||
|
displayError('No Google Map Api Key provided!'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
inited() { |
||||
|
return angular.isDefined(this.map); |
||||
|
} |
||||
|
|
||||
|
/* eslint-disable no-undef */ |
||||
|
updateMarkerColor(marker, color) { |
||||
|
var pinColor = color.substr(1); |
||||
|
var pinImage = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor, |
||||
|
new google.maps.Size(21, 34), |
||||
|
new google.maps.Point(0,0), |
||||
|
new google.maps.Point(10, 34)); |
||||
|
marker.setIcon(pinImage); |
||||
|
} |
||||
|
/* eslint-enable no-undef */ |
||||
|
|
||||
|
/* eslint-disable no-undef */ |
||||
|
updateMarkerImage(marker, settings, image, maxSize) { |
||||
|
var testImage = new Image(); |
||||
|
testImage.onload = function() { |
||||
|
var width; |
||||
|
var height; |
||||
|
var aspect = testImage.width / testImage.height; |
||||
|
if (aspect > 1) { |
||||
|
width = maxSize; |
||||
|
height = maxSize / aspect; |
||||
|
} else { |
||||
|
width = maxSize * aspect; |
||||
|
height = maxSize; |
||||
|
} |
||||
|
var pinImage = { |
||||
|
url: image, |
||||
|
scaledSize : new google.maps.Size(width, height) |
||||
|
} |
||||
|
marker.setIcon(pinImage); |
||||
|
if (settings.showLabel) { |
||||
|
marker.set('labelAnchor', new google.maps.Point(50, height + 20)); |
||||
|
} |
||||
|
} |
||||
|
testImage.src = image; |
||||
|
} |
||||
|
/* eslint-enable no-undef */ |
||||
|
|
||||
|
/* eslint-disable no-undef */ |
||||
|
createMarker(location, settings) { |
||||
|
var height = 34; |
||||
|
var pinColor = settings.color.substr(1); |
||||
|
var pinImage = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor, |
||||
|
new google.maps.Size(21, 34), |
||||
|
new google.maps.Point(0,0), |
||||
|
new google.maps.Point(10, 34)); |
||||
|
var pinShadow = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_shadow", |
||||
|
new google.maps.Size(40, 37), |
||||
|
new google.maps.Point(0, 0), |
||||
|
new google.maps.Point(12, 35)); |
||||
|
var marker; |
||||
|
if (settings.showLabel) { |
||||
|
marker = new MarkerWithLabel({ |
||||
|
position: location, |
||||
|
map: this.map, |
||||
|
icon: pinImage, |
||||
|
shadow: pinShadow, |
||||
|
labelContent: '<b>'+settings.label+'</b>', |
||||
|
labelClass: "tb-labels", |
||||
|
labelAnchor: new google.maps.Point(50, height + 20) |
||||
|
}); |
||||
|
} else { |
||||
|
marker = new google.maps.Marker({ |
||||
|
position: location, |
||||
|
map: this.map, |
||||
|
icon: pinImage, |
||||
|
shadow: pinShadow |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (settings.useMarkerImage) { |
||||
|
this.updateMarkerImage(marker, settings, settings.markerImage, settings.markerImageSize || 34); |
||||
|
} |
||||
|
|
||||
|
this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo); |
||||
|
|
||||
|
return marker; |
||||
|
} |
||||
|
/* eslint-enable no-undef */ |
||||
|
|
||||
|
/* eslint-disable no-undef */ |
||||
|
createTooltip(marker, pattern, replaceInfo) { |
||||
|
var popup = new google.maps.InfoWindow({ |
||||
|
content: '' |
||||
|
}); |
||||
|
marker.addListener('click', function() { |
||||
|
popup.open(this.map, marker); |
||||
|
}); |
||||
|
this.tooltips.push( { |
||||
|
popup: popup, |
||||
|
pattern: pattern, |
||||
|
replaceInfo: replaceInfo |
||||
|
}); |
||||
|
} |
||||
|
/* 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 google.maps.Polyline({ |
||||
|
path: locations, |
||||
|
strokeColor: settings.color, |
||||
|
strokeOpacity: settings.strokeOpacity, |
||||
|
strokeWeight: settings.strokeWeight, |
||||
|
map: this.map |
||||
|
}); |
||||
|
|
||||
|
return polyline; |
||||
|
} |
||||
|
/* eslint-enable no-undef */ |
||||
|
|
||||
|
fitBounds(bounds) { |
||||
|
var tbMap = this; |
||||
|
google.maps.event.addListenerOnce(this.map, 'bounds_changed', function() { // eslint-disable-line no-undef
|
||||
|
var newZoomLevel = tbMap.map.getZoom(); |
||||
|
if (tbMap.dontFitMapBounds && tbMap.defaultZoomLevel) { |
||||
|
newZoomLevel = tbMap.defaultZoomLevel; |
||||
|
} |
||||
|
tbMap.map.setZoom(newZoomLevel); |
||||
|
|
||||
|
if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) { |
||||
|
tbMap.map.setZoom(tbMap.minZoomLevel); |
||||
|
} |
||||
|
}); |
||||
|
this.map.fitBounds(bounds); |
||||
|
} |
||||
|
|
||||
|
createLatLng(lat, lng) { |
||||
|
return new google.maps.LatLng(lat, lng); // eslint-disable-line no-undef
|
||||
|
} |
||||
|
|
||||
|
getMarkerPosition(marker) { |
||||
|
return marker.getPosition(); |
||||
|
} |
||||
|
|
||||
|
setMarkerPosition(marker, latLng) { |
||||
|
marker.setPosition(latLng); |
||||
|
} |
||||
|
|
||||
|
getPolylineLatLngs(polyline) { |
||||
|
return polyline.getPath().getArray(); |
||||
|
} |
||||
|
|
||||
|
setPolylineLatLngs(polyline, latLngs) { |
||||
|
polyline.setPath(latLngs); |
||||
|
} |
||||
|
|
||||
|
createBounds() { |
||||
|
return new google.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() { |
||||
|
google.maps.event.trigger(this.map, "resize"); // eslint-disable-line no-undef
|
||||
|
} |
||||
|
|
||||
|
getTooltips() { |
||||
|
return this.tooltips; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,458 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
import tinycolor from 'tinycolor2'; |
||||
|
|
||||
|
import TbGoogleMap from './google-map'; |
||||
|
import TbOpenStreetMap from './openstreet-map'; |
||||
|
|
||||
|
export default class TbMapWidget { |
||||
|
constructor(mapProvider, drawRoutes, containerElement, settings, datasources) { |
||||
|
|
||||
|
var tbMap = this; |
||||
|
|
||||
|
this.drawRoutes = drawRoutes; |
||||
|
this.markers = []; |
||||
|
if (this.drawRoutes) { |
||||
|
this.polylines = []; |
||||
|
} |
||||
|
this.locationsSettings = []; |
||||
|
this.varsRegex = /\$\{([^\}]*)\}/g; |
||||
|
|
||||
|
if (settings.defaultZoomLevel) { |
||||
|
if (settings.defaultZoomLevel > 0 && settings.defaultZoomLevel < 21) { |
||||
|
this.defaultZoomLevel = Math.floor(settings.defaultZoomLevel); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.dontFitMapBounds = settings.fitMapBounds === false; |
||||
|
|
||||
|
function procesTooltipPattern(pattern) { |
||||
|
var match = tbMap.varsRegex.exec(pattern); |
||||
|
var replaceInfo = {}; |
||||
|
replaceInfo.variables = []; |
||||
|
while (match !== null) { |
||||
|
var variableInfo = {}; |
||||
|
variableInfo.dataKeyIndex = -1; |
||||
|
var variable = match[0]; |
||||
|
var label = match[1]; |
||||
|
var valDec = 2; |
||||
|
var splitVals = label.split(':'); |
||||
|
if (splitVals.length > 1) { |
||||
|
label = splitVals[0]; |
||||
|
valDec = parseFloat(splitVals[1]); |
||||
|
} |
||||
|
variableInfo.variable = variable; |
||||
|
variableInfo.valDec = valDec; |
||||
|
|
||||
|
if (label.startsWith('#')) { |
||||
|
var keyIndexStr = label.substring(1); |
||||
|
var n = Math.floor(Number(keyIndexStr)); |
||||
|
if (String(n) === keyIndexStr && n >= 0) { |
||||
|
variableInfo.dataKeyIndex = n; |
||||
|
} |
||||
|
} |
||||
|
if (variableInfo.dataKeyIndex === -1) { |
||||
|
var offset = 0; |
||||
|
for (var i=0;i<datasources.length;i++) { |
||||
|
var datasource = datasources[i]; |
||||
|
for (var k = 0; k < datasource.dataKeys.length; k++) { |
||||
|
var dataKey = datasource.dataKeys[k]; |
||||
|
if (dataKey.label === label) { |
||||
|
variableInfo.dataKeyIndex = offset + k; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
offset += datasource.dataKeys.length; |
||||
|
} |
||||
|
} |
||||
|
replaceInfo.variables.push(variableInfo); |
||||
|
match = tbMap.varsRegex.exec(pattern); |
||||
|
} |
||||
|
return replaceInfo; |
||||
|
} |
||||
|
|
||||
|
var configuredLocationsSettings = drawRoutes ? settings.routesSettings : settings.markersSettings; |
||||
|
if (!configuredLocationsSettings) { |
||||
|
configuredLocationsSettings = []; |
||||
|
} |
||||
|
|
||||
|
for (var i=0;i<configuredLocationsSettings.length;i++) { |
||||
|
this.locationsSettings[i] = { |
||||
|
latKeyName: "lat", |
||||
|
lngKeyName: "lng", |
||||
|
showLabel: true, |
||||
|
label: "", |
||||
|
color: "#FE7569", |
||||
|
useColorFunction: false, |
||||
|
colorFunction: null, |
||||
|
markerImage: null, |
||||
|
markerImageSize: 34, |
||||
|
useMarkerImage: false, |
||||
|
useMarkerImageFunction: false, |
||||
|
markerImageFunction: null, |
||||
|
markerImages: [], |
||||
|
tooltipPattern: "<b>Latitude:</b> ${lat:7}<br/><b>Longitude:</b> ${lng:7}" |
||||
|
}; |
||||
|
|
||||
|
if (drawRoutes) { |
||||
|
this.locationsSettings[i].strokeWeight = 2; |
||||
|
this.locationsSettings[i].strokeOpacity = 1.0; |
||||
|
} |
||||
|
|
||||
|
if (configuredLocationsSettings[i]) { |
||||
|
this.locationsSettings[i].latKeyName = configuredLocationsSettings[i].latKeyName || this.locationsSettings[i].latKeyName; |
||||
|
this.locationsSettings[i].lngKeyName = configuredLocationsSettings[i].lngKeyName || this.locationsSettings[i].lngKeyName; |
||||
|
|
||||
|
this.locationsSettings[i].tooltipPattern = configuredLocationsSettings[i].tooltipPattern || "<b>Latitude:</b> ${"+this.locationsSettings[i].latKeyName+":7}<br/><b>Longitude:</b> ${"+this.locationsSettings[i].lngKeyName+":7}"; |
||||
|
|
||||
|
this.locationsSettings[i].tooltipReplaceInfo = procesTooltipPattern(this.locationsSettings[i].tooltipPattern); |
||||
|
|
||||
|
this.locationsSettings[i].showLabel = configuredLocationsSettings[i].showLabel !== false; |
||||
|
this.locationsSettings[i].label = configuredLocationsSettings[i].label || this.locationsSettings[i].label; |
||||
|
this.locationsSettings[i].color = configuredLocationsSettings[i].color ? tinycolor(configuredLocationsSettings[i].color).toHexString() : this.locationsSettings[i].color; |
||||
|
|
||||
|
this.locationsSettings[i].useColorFunction = configuredLocationsSettings[i].useColorFunction === true; |
||||
|
if (angular.isDefined(configuredLocationsSettings[i].colorFunction) && configuredLocationsSettings[i].colorFunction.length > 0) { |
||||
|
try { |
||||
|
this.locationsSettings[i].colorFunction = new Function('data', configuredLocationsSettings[i].colorFunction); |
||||
|
} catch (e) { |
||||
|
this.locationsSettings[i].colorFunction = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.locationsSettings[i].useMarkerImageFunction = configuredLocationsSettings[i].useMarkerImageFunction === true; |
||||
|
if (angular.isDefined(configuredLocationsSettings[i].markerImageFunction) && configuredLocationsSettings[i].markerImageFunction.length > 0) { |
||||
|
try { |
||||
|
this.locationsSettings[i].markerImageFunction = new Function('data, images', configuredLocationsSettings[i].markerImageFunction); |
||||
|
} catch (e) { |
||||
|
this.locationsSettings[i].markerImageFunction = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.locationsSettings[i].markerImages = configuredLocationsSettings[i].markerImages || []; |
||||
|
|
||||
|
if (!this.locationsSettings[i].useMarkerImageFunction && |
||||
|
angular.isDefined(configuredLocationsSettings[i].markerImage) && |
||||
|
configuredLocationsSettings[i].markerImage.length > 0) { |
||||
|
this.locationsSettings[i].markerImage = configuredLocationsSettings[i].markerImage; |
||||
|
this.locationsSettings[i].useMarkerImage = true; |
||||
|
this.locationsSettings[i].markerImageSize = configuredLocationsSettings[i].markerImageSize || 34; |
||||
|
} |
||||
|
|
||||
|
if (drawRoutes) { |
||||
|
this.locationsSettings[i].strokeWeight = configuredLocationsSettings[i].strokeWeight || this.locationsSettings[i].strokeWeight; |
||||
|
this.locationsSettings[i].strokeOpacity = angular.isDefined(configuredLocationsSettings[i].strokeOpacity) ? configuredLocationsSettings[i].strokeOpacity : this.locationsSettings[i].strokeOpacity; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var minZoomLevel = this.drawRoutes ? 18 : 15; |
||||
|
if (mapProvider === 'google-map') { |
||||
|
this.map = new TbGoogleMap(containerElement, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey); |
||||
|
} else if (mapProvider === 'openstreet-map') { |
||||
|
this.map = new TbOpenStreetMap(containerElement, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
redraw(data, sizeChanged) { |
||||
|
|
||||
|
var tbMap = this; |
||||
|
|
||||
|
function isNumber(n) { |
||||
|
return !isNaN(parseFloat(n)) && isFinite(n); |
||||
|
} |
||||
|
|
||||
|
function padValue(val, dec, int) { |
||||
|
var i = 0; |
||||
|
var s, strVal, n; |
||||
|
|
||||
|
val = parseFloat(val); |
||||
|
n = (val < 0); |
||||
|
val = Math.abs(val); |
||||
|
|
||||
|
if (dec > 0) { |
||||
|
strVal = val.toFixed(dec).toString().split('.'); |
||||
|
s = int - strVal[0].length; |
||||
|
|
||||
|
for (; i < s; ++i) { |
||||
|
strVal[0] = '0' + strVal[0]; |
||||
|
} |
||||
|
|
||||
|
strVal = (n ? '-' : '') + strVal[0] + '.' + strVal[1]; |
||||
|
} |
||||
|
|
||||
|
else { |
||||
|
strVal = Math.round(val).toString(); |
||||
|
s = int - strVal.length; |
||||
|
|
||||
|
for (; i < s; ++i) { |
||||
|
strVal = '0' + strVal; |
||||
|
} |
||||
|
|
||||
|
strVal = (n ? '-' : '') + strVal; |
||||
|
} |
||||
|
|
||||
|
return strVal; |
||||
|
} |
||||
|
|
||||
|
function arraysEqual(a, b) { |
||||
|
if (a === b) return true; |
||||
|
if (a === null || b === null) return false; |
||||
|
if (a.length != b.length) return false; |
||||
|
|
||||
|
for (var i = 0; i < a.length; ++i) { |
||||
|
if (!a[i].equals(b[i])) return false; |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
function calculateLocationColor(settings, dataMap) { |
||||
|
if (settings.useColorFunction && settings.colorFunction) { |
||||
|
var color = '#FE7569'; |
||||
|
try { |
||||
|
color = settings.colorFunction(dataMap); |
||||
|
} catch (e) { |
||||
|
color = '#FE7569'; |
||||
|
} |
||||
|
return tinycolor(color).toHexString(); |
||||
|
} else { |
||||
|
return settings.color; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function updateLocationColor(location, dataMap) { |
||||
|
var color = calculateLocationColor(location.settings, dataMap); |
||||
|
if (!location.settings.calculatedColor || location.settings.calculatedColor !== color) { |
||||
|
if (!location.settings.useMarkerImage && !location.settings.useMarkerImageFunction) { |
||||
|
tbMap.map.updateMarkerColor(location.marker, color); |
||||
|
} |
||||
|
if (location.polyline) { |
||||
|
tbMap.map.updatePolylineColor(location.polyline, location.settings, color); |
||||
|
} |
||||
|
location.settings.calculatedColor = color; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function calculateLocationMarkerImage(settings, dataMap) { |
||||
|
if (settings.useMarkerImageFunction && settings.markerImageFunction) { |
||||
|
var image = null; |
||||
|
try { |
||||
|
image = settings.markerImageFunction(dataMap, settings.markerImages); |
||||
|
} catch (e) { |
||||
|
image = null; |
||||
|
} |
||||
|
return image; |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function updateLocationMarkerImage(location, dataMap) { |
||||
|
var image = calculateLocationMarkerImage(location.settings, dataMap); |
||||
|
if (image != null && (!location.settings.calculatedImage || !angular.equals(location.settings.calculatedImage, image))) { |
||||
|
tbMap.map.updateMarkerImage(location.marker, location.settings, image.url, image.size); |
||||
|
location.settings.calculatedImage = image; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function updateLocationStyle(location, dataMap) { |
||||
|
updateLocationColor(location, dataMap); |
||||
|
updateLocationMarkerImage(location, dataMap); |
||||
|
} |
||||
|
|
||||
|
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]; |
||||
|
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]; |
||||
|
if (!location.marker) { |
||||
|
location.marker = tbMap.map.createMarker(markerLocation, location.settings); |
||||
|
} else { |
||||
|
tbMap.map.setMarkerPosition(location.marker, markerLocation); |
||||
|
} |
||||
|
} |
||||
|
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]; |
||||
|
latLng = tbMap.map.createLatLng(lat, lng); |
||||
|
if (!location.marker) { |
||||
|
location.marker = tbMap.map.createMarker(latLng, location.settings); |
||||
|
tbMap.markers.push(location.marker); |
||||
|
locationChanged = true; |
||||
|
} else { |
||||
|
var prevPosition = tbMap.map.getMarkerPosition(location.marker); |
||||
|
if (!prevPosition.equals(latLng)) { |
||||
|
tbMap.map.setMarkerPosition(location.marker, latLng); |
||||
|
locationChanged = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
updateLocationStyle(location, dataMap); |
||||
|
} |
||||
|
} |
||||
|
return locationChanged; |
||||
|
} |
||||
|
|
||||
|
function toLabelValueMap(data) { |
||||
|
var dataMap = {}; |
||||
|
for (var i = 0; i < data.length; i++) { |
||||
|
var dataKey = data[i].dataKey; |
||||
|
var label = dataKey.label; |
||||
|
var keyData = data[i].data; |
||||
|
var val = null; |
||||
|
if (keyData.length > 0) { |
||||
|
val = keyData[keyData.length-1][1]; |
||||
|
} |
||||
|
dataMap[label] = val; |
||||
|
} |
||||
|
return dataMap; |
||||
|
} |
||||
|
|
||||
|
function loadLocations(data) { |
||||
|
var bounds = tbMap.map.createBounds(); |
||||
|
tbMap.locations = []; |
||||
|
var dataMap = toLabelValueMap(data); |
||||
|
for (var l in tbMap.locationsSettings) { |
||||
|
var locationSettings = tbMap.locationsSettings[l]; |
||||
|
var latIndex = -1; |
||||
|
var lngIndex = -1; |
||||
|
for (var i = 0; i < data.length; i++) { |
||||
|
var dataKey = data[i].dataKey; |
||||
|
if (dataKey.label === locationSettings.latKeyName) { |
||||
|
latIndex = i; |
||||
|
} else if (dataKey.label === locationSettings.lngKeyName) { |
||||
|
lngIndex = i; |
||||
|
} |
||||
|
} |
||||
|
if (latIndex > -1 && lngIndex > -1) { |
||||
|
var location = { |
||||
|
latIndex: latIndex, |
||||
|
lngIndex: lngIndex, |
||||
|
settings: locationSettings |
||||
|
}; |
||||
|
tbMap.locations.push(location); |
||||
|
updateLocation(location, data, dataMap); |
||||
|
if (location.polyline) { |
||||
|
tbMap.map.extendBounds(bounds, location.polyline); |
||||
|
} else if (location.marker) { |
||||
|
bounds.extend(tbMap.map.getMarkerPosition(location.marker)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
tbMap.map.fitBounds(bounds); |
||||
|
} |
||||
|
|
||||
|
function updateLocations(data) { |
||||
|
var locationsChanged = false; |
||||
|
var bounds = tbMap.map.createBounds(); |
||||
|
var dataMap = toLabelValueMap(data); |
||||
|
for (var p in tbMap.locations) { |
||||
|
var location = tbMap.locations[p]; |
||||
|
locationsChanged |= updateLocation(location, data, dataMap); |
||||
|
if (location.polyline) { |
||||
|
tbMap.map.extendBounds(bounds, location.polyline); |
||||
|
} else if (location.marker) { |
||||
|
bounds.extend(tbMap.map.getMarkerPosition(location.marker)); |
||||
|
} |
||||
|
} |
||||
|
if (!tbMap.dontFitMapBounds && locationsChanged) { |
||||
|
tbMap.map.fitBounds(bounds); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (this.map && this.map.inited()) { |
||||
|
if (data) { |
||||
|
if (!this.locations) { |
||||
|
loadLocations(data); |
||||
|
} else { |
||||
|
updateLocations(data); |
||||
|
} |
||||
|
} |
||||
|
if (sizeChanged) { |
||||
|
this.map.invalidateSize(); |
||||
|
if (!this.dontFitMapBounds) { |
||||
|
var bounds = this.map.createBounds(); |
||||
|
for (var m in this.markers) { |
||||
|
bounds.extend(this.map.getMarkerPosition(this.markers[m])); |
||||
|
} |
||||
|
if (this.polylines) { |
||||
|
for (var p in this.polylines) { |
||||
|
this.map.extendBounds(bounds, this.polylines[p]); |
||||
|
} |
||||
|
} |
||||
|
this.map.fitBounds(bounds); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var tooltips = this.map.getTooltips(); |
||||
|
|
||||
|
for (var t in tooltips) { |
||||
|
var tooltip = tooltips[t]; |
||||
|
var text = tooltip.pattern; |
||||
|
var replaceInfo = tooltip.replaceInfo; |
||||
|
for (var v in replaceInfo.variables) { |
||||
|
var variableInfo = replaceInfo.variables[v]; |
||||
|
var txtVal = ''; |
||||
|
if (variableInfo.dataKeyIndex > -1) { |
||||
|
var varData = data[variableInfo.dataKeyIndex].data; |
||||
|
if (varData.length > 0) { |
||||
|
var val = varData[varData.length-1][1]; |
||||
|
if (isNumber(val)) { |
||||
|
txtVal = padValue(val, variableInfo.valDec, 0); |
||||
|
} else { |
||||
|
txtVal = val; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
text = text.split(variableInfo.variable).join(txtVal); |
||||
|
} |
||||
|
tooltip.popup.setContent(text); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,198 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
import 'leaflet/dist/leaflet.css'; |
||||
|
import L from 'leaflet/dist/leaflet'; |
||||
|
|
||||
|
export default class TbOpenStreetMap { |
||||
|
|
||||
|
constructor(containerElement, defaultZoomLevel, dontFitMapBounds, minZoomLevel) { |
||||
|
|
||||
|
this.defaultZoomLevel = defaultZoomLevel; |
||||
|
this.dontFitMapBounds = dontFitMapBounds; |
||||
|
this.minZoomLevel = minZoomLevel; |
||||
|
this.tooltips = []; |
||||
|
|
||||
|
this.map = L.map(containerElement).setView([0, 0], this.defaultZoomLevel || 8); |
||||
|
|
||||
|
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { |
||||
|
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' |
||||
|
}).addTo(this.map); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
inited() { |
||||
|
return angular.isDefined(this.map); |
||||
|
} |
||||
|
|
||||
|
updateMarkerColor(marker, color) { |
||||
|
var pinColor = color.substr(1); |
||||
|
var icon = L.icon({ |
||||
|
iconUrl: 'http://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: 'http://chart.apis.google.com/chart?chst=d_map_pin_shadow', |
||||
|
shadowSize: [40, 37], |
||||
|
shadowAnchor: [12, 35] |
||||
|
}); |
||||
|
marker.setIcon(icon); |
||||
|
} |
||||
|
|
||||
|
updateMarkerImage(marker, settings, image, maxSize) { |
||||
|
var testImage = new Image(); // eslint-disable-line no-undef
|
||||
|
testImage.onload = function() { |
||||
|
var width; |
||||
|
var height; |
||||
|
var aspect = testImage.width / testImage.height; |
||||
|
if (aspect > 1) { |
||||
|
width = maxSize; |
||||
|
height = maxSize / aspect; |
||||
|
} else { |
||||
|
width = maxSize * aspect; |
||||
|
height = maxSize; |
||||
|
} |
||||
|
var icon = L.icon({ |
||||
|
iconUrl: image, |
||||
|
iconSize: [width, height], |
||||
|
iconAnchor: [width/2, height], |
||||
|
popupAnchor: [0, -height] |
||||
|
}); |
||||
|
marker.setIcon(icon); |
||||
|
if (settings.showLabel) { |
||||
|
marker.unbindTooltip(); |
||||
|
marker.bindTooltip('<b>' + settings.label + '</b>', |
||||
|
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: [0, -height + 10] }); |
||||
|
} |
||||
|
} |
||||
|
testImage.src = image; |
||||
|
} |
||||
|
|
||||
|
createMarker(location, settings) { |
||||
|
var height = 34; |
||||
|
var pinColor = settings.color.substr(1); |
||||
|
var icon = L.icon({ |
||||
|
iconUrl: 'http://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: 'http://chart.apis.google.com/chart?chst=d_map_pin_shadow', |
||||
|
shadowSize: [40, 37], |
||||
|
shadowAnchor: [12, 35] |
||||
|
}); |
||||
|
|
||||
|
var marker = L.marker(location, {icon: icon}).addTo(this.map); |
||||
|
|
||||
|
if (settings.showLabel) { |
||||
|
marker.bindTooltip('<b>' + settings.label + '</b>', |
||||
|
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: [0, -height + 10] }); |
||||
|
} |
||||
|
|
||||
|
if (settings.useMarkerImage) { |
||||
|
this.updateMarkerImage(marker, settings, settings.markerImage, settings.markerImageSize || 34); |
||||
|
} |
||||
|
|
||||
|
this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo); |
||||
|
|
||||
|
return marker; |
||||
|
} |
||||
|
|
||||
|
createTooltip(marker, pattern, replaceInfo) { |
||||
|
var popup = L.popup(); |
||||
|
popup.setContent(''); |
||||
|
marker.bindPopup(popup, {autoClose: false, closeOnClick: false}); |
||||
|
this.tooltips.push( { |
||||
|
popup: popup, |
||||
|
pattern: pattern, |
||||
|
replaceInfo: replaceInfo |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
fitBounds(bounds) { |
||||
|
var tbMap = this; |
||||
|
this.map.once('zoomend', function() { |
||||
|
var newZoomLevel = tbMap.map.getZoom(); |
||||
|
if (tbMap.dontFitMapBounds && tbMap.defaultZoomLevel) { |
||||
|
newZoomLevel = tbMap.defaultZoomLevel; |
||||
|
} |
||||
|
tbMap.map.setZoom(newZoomLevel, {animate: false}); |
||||
|
|
||||
|
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); |
||||
|
} |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue