Merge branch 'master' into develop/3.2
This commit is contained in:
commit
3ffda44816
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.thingsboard.server.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@ -59,7 +60,11 @@ public class AdminController extends BaseController {
|
||||
public AdminSettings getAdminSettings(@PathVariable("key") String key) throws ThingsboardException {
|
||||
try {
|
||||
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
|
||||
return checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key));
|
||||
AdminSettings adminSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key));
|
||||
if (adminSettings.getKey().equals("mail")) {
|
||||
((ObjectNode) adminSettings.getJsonValue()).put("password", "");
|
||||
}
|
||||
return adminSettings;
|
||||
} catch (Exception e) {
|
||||
throw handleException(e);
|
||||
}
|
||||
@ -74,6 +79,7 @@ public class AdminController extends BaseController {
|
||||
adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings));
|
||||
if (adminSettings.getKey().equals("mail")) {
|
||||
mailService.updateMailConfiguration();
|
||||
((ObjectNode) adminSettings.getJsonValue()).put("password", "");
|
||||
}
|
||||
return adminSettings;
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -391,8 +391,12 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
|
||||
}
|
||||
|
||||
private Set<EntityId> getPropagationEntityIds(Alarm alarm) {
|
||||
List<EntityRelation> relations = relationService.findByTo(alarm.getTenantId(), alarm.getId(), RelationTypeGroup.ALARM);
|
||||
return relations.stream().map(EntityRelation::getFrom).collect(Collectors.toSet());
|
||||
if (alarm.isPropagate()) {
|
||||
List<EntityRelation> relations = relationService.findByTo(alarm.getTenantId(), alarm.getId(), RelationTypeGroup.ALARM);
|
||||
return relations.stream().map(EntityRelation::getFrom).collect(Collectors.toSet());
|
||||
} else {
|
||||
return Collections.singleton(alarm.getOriginator());
|
||||
}
|
||||
}
|
||||
|
||||
private void createAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId) {
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.thingsboard.server.dao.settings;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -51,6 +52,13 @@ public class AdminSettingsServiceImpl implements AdminSettingsService {
|
||||
public AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings) {
|
||||
log.trace("Executing saveAdminSettings [{}]", adminSettings);
|
||||
adminSettingsValidator.validate(adminSettings, data -> tenantId);
|
||||
if (adminSettings.getKey().equals("mail") && "".equals(adminSettings.getJsonValue().get("password").asText())) {
|
||||
AdminSettings mailSettings = findAdminSettingsByKey(tenantId, "mail");
|
||||
if (mailSettings != null) {
|
||||
((ObjectNode) adminSettings.getJsonValue()).put("password", mailSettings.getJsonValue().get("password").asText());
|
||||
}
|
||||
}
|
||||
|
||||
return adminSettingsDao.save(tenantId, adminSettings);
|
||||
}
|
||||
|
||||
|
||||
@ -35,13 +35,11 @@
|
||||
"@ngrx/effects": "^10.0.0",
|
||||
"@ngrx/store": "^10.0.0",
|
||||
"@ngrx/store-devtools": "^10.0.0",
|
||||
"ngx-sharebuttons": "^8.0.1",
|
||||
"@ngx-translate/core": "^13.0.0",
|
||||
"@ngx-translate/http-loader": "^6.0.0",
|
||||
"ace-builds": "^1.4.12",
|
||||
"angular-gridster2": "^10.1.3",
|
||||
"angular2-hotkeys": "^2.2.0",
|
||||
"base64-js": "^1.3.1",
|
||||
"canvas-gauges": "^2.1.7",
|
||||
"compass-sass-mixins": "^0.12.7",
|
||||
"core-js": "^3.6.5",
|
||||
@ -70,6 +68,7 @@
|
||||
"ngx-daterangepicker-material": "^3.0.4",
|
||||
"ngx-flowchart": "git://github.com/thingsboard/ngx-flowchart.git#master",
|
||||
"ngx-hm-carousel": "^2.0.0-rc.1",
|
||||
"ngx-sharebuttons": "^8.0.1",
|
||||
"ngx-translate-messageformat-compiler": "^4.8.0",
|
||||
"objectpath": "^2.0.0",
|
||||
"prettier": "^2.0.5",
|
||||
|
||||
@ -26,7 +26,6 @@ import {
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { forkJoin, Observable, ReplaySubject, throwError } from 'rxjs';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { objToBase64 } from '@core/utils';
|
||||
|
||||
declare const SystemJS;
|
||||
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
import _ from 'lodash';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { finalize, share } from 'rxjs/operators';
|
||||
import base64js from 'base64-js';
|
||||
import { Datasource } from '@app/shared/models/widget.models';
|
||||
|
||||
const varsRegex = /\${([^}]*)}/g;
|
||||
@ -123,7 +122,8 @@ export function isEmpty(obj: any): boolean {
|
||||
}
|
||||
|
||||
export function formatValue(value: any, dec?: number, units?: string, showZeroDecimals?: boolean): string | undefined {
|
||||
if (isDefinedAndNotNull(value) && isNumeric(value) && (isDefinedAndNotNull(dec) || isDefinedAndNotNull(units) || Number(value).toString() === value)) {
|
||||
if (isDefinedAndNotNull(value) && isNumeric(value) &&
|
||||
(isDefinedAndNotNull(dec) || isDefinedAndNotNull(units) || Number(value).toString() === value)) {
|
||||
let formatted: string | number = Number(value);
|
||||
if (isDefinedAndNotNull(dec)) {
|
||||
formatted = formatted.toFixed(dec);
|
||||
@ -164,30 +164,23 @@ export function deleteNullProperties(obj: any) {
|
||||
|
||||
export function objToBase64(obj: any): string {
|
||||
const json = JSON.stringify(obj);
|
||||
const encoded = utf8Encode(json);
|
||||
return base64js.fromByteArray(encoded);
|
||||
return btoa(encodeURIComponent(json).replace(/%([0-9A-F]{2})/g,
|
||||
function toSolidBytes(match, p1) {
|
||||
return String.fromCharCode(Number('0x' + p1));
|
||||
}));
|
||||
}
|
||||
|
||||
export function objToBase64URI(obj: any): string {
|
||||
return encodeURIComponent(objToBase64(obj));
|
||||
}
|
||||
|
||||
export function base64toObj(b64Encoded: string): any {
|
||||
const encoded: Uint8Array | number[] = base64js.toByteArray(b64Encoded);
|
||||
const json = utf8Decode(encoded);
|
||||
const json = decodeURIComponent(atob(b64Encoded).split('').map((c) => {
|
||||
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||
}).join(''));
|
||||
return JSON.parse(json);
|
||||
}
|
||||
|
||||
function utf8Encode(str: string): Uint8Array | number[] {
|
||||
let result: Uint8Array | number[];
|
||||
if (isUndefined(Uint8Array)) {
|
||||
result = utf8ToBytes(str);
|
||||
} else {
|
||||
result = new Uint8Array(utf8ToBytes(str));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function utf8Decode(bytes: Uint8Array | number[]): string {
|
||||
return utf8Slice(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
const scrollRegex = /(auto|scroll)/;
|
||||
|
||||
function parentNodes(node: Node, nodes: Node[]): Node[] {
|
||||
@ -275,129 +268,6 @@ function easeInOut(
|
||||
);
|
||||
}
|
||||
|
||||
function utf8Slice(buf: Uint8Array | number[], start: number, end: number): string {
|
||||
let res = '';
|
||||
let tmp = '';
|
||||
end = Math.min(buf.length, end || Infinity);
|
||||
start = start || 0;
|
||||
|
||||
for (let i = start; i < end; i++) {
|
||||
if (buf[i] <= 0x7F) {
|
||||
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]);
|
||||
tmp = '';
|
||||
} else {
|
||||
tmp += '%' + buf[i].toString(16);
|
||||
}
|
||||
}
|
||||
return res + decodeUtf8Char(tmp);
|
||||
}
|
||||
|
||||
function decodeUtf8Char(str: string): string {
|
||||
try {
|
||||
return decodeURIComponent(str);
|
||||
} catch (err) {
|
||||
return String.fromCharCode(0xFFFD); // UTF 8 invalid char
|
||||
}
|
||||
}
|
||||
|
||||
function utf8ToBytes(input: string, units?: number): number[] {
|
||||
units = units || Infinity;
|
||||
let codePoint: number;
|
||||
const length = input.length;
|
||||
let leadSurrogate: number = null;
|
||||
const bytes: number[] = [];
|
||||
let i = 0;
|
||||
|
||||
for (; i < length; i++) {
|
||||
codePoint = input.charCodeAt(i);
|
||||
|
||||
// is surrogate component
|
||||
if (codePoint > 0xD7FF && codePoint < 0xE000) {
|
||||
// last char was a lead
|
||||
if (leadSurrogate) {
|
||||
// 2 leads in a row
|
||||
if (codePoint < 0xDC00) {
|
||||
units -= 3;
|
||||
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
|
||||
leadSurrogate = codePoint;
|
||||
continue;
|
||||
} else {
|
||||
// valid surrogate pair
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000;
|
||||
leadSurrogate = null;
|
||||
}
|
||||
} else {
|
||||
// no lead yet
|
||||
|
||||
if (codePoint > 0xDBFF) {
|
||||
// unexpected trail
|
||||
units -= 3;
|
||||
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
|
||||
continue;
|
||||
} else if (i + 1 === length) {
|
||||
// unpaired lead
|
||||
units -= 3;
|
||||
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
|
||||
continue;
|
||||
} else {
|
||||
// valid lead
|
||||
leadSurrogate = codePoint;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (leadSurrogate) {
|
||||
// valid bmp char, but last char was a lead
|
||||
units -= 3;
|
||||
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
|
||||
leadSurrogate = null;
|
||||
}
|
||||
|
||||
// encode utf8
|
||||
if (codePoint < 0x80) {
|
||||
units -= 1;
|
||||
if (units < 0) { break; }
|
||||
bytes.push(codePoint);
|
||||
} else if (codePoint < 0x800) {
|
||||
units -= 2;
|
||||
if (units < 0) { break; }
|
||||
bytes.push(
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
codePoint >> 0x6 | 0xC0,
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
codePoint & 0x3F | 0x80
|
||||
);
|
||||
} else if (codePoint < 0x10000) {
|
||||
units -= 3;
|
||||
if (units < 0) { break; }
|
||||
bytes.push(
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
codePoint >> 0xC | 0xE0,
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
codePoint >> 0x6 & 0x3F | 0x80,
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
codePoint & 0x3F | 0x80
|
||||
);
|
||||
} else if (codePoint < 0x200000) {
|
||||
units -= 4;
|
||||
if (units < 0) { break; }
|
||||
bytes.push(
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
codePoint >> 0x12 | 0xF0,
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
codePoint >> 0xC & 0x3F | 0x80,
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
codePoint >> 0x6 & 0x3F | 0x80,
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
codePoint & 0x3F | 0x80
|
||||
);
|
||||
} else {
|
||||
throw new Error('Invalid code point');
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export function deepClone<T>(target: T, ignoreFields?: string[]): T {
|
||||
if (target === null) {
|
||||
return target;
|
||||
|
||||
@ -24,7 +24,7 @@ import { Router } from '@angular/router';
|
||||
import { DialogComponent } from '@app/shared/components/dialog.component';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.models';
|
||||
import { objToBase64 } from '@core/utils';
|
||||
import { objToBase64URI } from '@core/utils';
|
||||
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
|
||||
import { EntityId } from '@app/shared/models/id/entity-id';
|
||||
import { Widget } from '@app/shared/models/widget.models';
|
||||
@ -205,7 +205,7 @@ export class AddWidgetToDashboardDialogComponent extends
|
||||
id: targetState,
|
||||
params: {}
|
||||
};
|
||||
const state = objToBase64([ stateObject ]);
|
||||
const state = objToBase64URI([ stateObject ]);
|
||||
url = `/dashboards/${theDashboard.id.id}?state=${state}`;
|
||||
} else {
|
||||
url = `/dashboards/${theDashboard.id.id}`;
|
||||
|
||||
@ -30,6 +30,7 @@ import 'leaflet.markercluster/dist/leaflet.markercluster';
|
||||
import {
|
||||
defaultSettings,
|
||||
FormattedData,
|
||||
MapProviders,
|
||||
MapSettings,
|
||||
MarkerSettings,
|
||||
PolygonSettings,
|
||||
@ -74,6 +75,8 @@ export default abstract class LeafletMap {
|
||||
drawRoutes: boolean;
|
||||
showPolygon: boolean;
|
||||
updatePending = false;
|
||||
addMarkers: L.Marker[] = [];
|
||||
addPolygons: L.Polygon[] = [];
|
||||
|
||||
protected constructor(public ctx: WidgetContext,
|
||||
public $container: HTMLElement,
|
||||
@ -133,6 +136,7 @@ export default abstract class LeafletMap {
|
||||
shadowSize: [41, 41]
|
||||
});
|
||||
const newMarker = L.marker(mousePositionOnMap, { icon }).addTo(this.map);
|
||||
this.addMarkers.push(newMarker);
|
||||
const datasourcesList = document.createElement('div');
|
||||
const customLatLng = this.convertToCustomFormat(mousePositionOnMap);
|
||||
const header = document.createElement('p');
|
||||
@ -147,10 +151,14 @@ export default abstract class LeafletMap {
|
||||
const updatedEnttity = { ...ds, ...customLatLng };
|
||||
this.saveMarkerLocation(updatedEnttity).subscribe(() => {
|
||||
this.map.removeLayer(newMarker);
|
||||
const markerIndex = this.addMarkers.indexOf(newMarker);
|
||||
if (markerIndex > -1) {
|
||||
this.addMarkers.splice(markerIndex, 1);
|
||||
}
|
||||
this.deleteMarker(ds.entityName);
|
||||
this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options);
|
||||
});
|
||||
}
|
||||
};
|
||||
datasourcesList.append(dsItem);
|
||||
});
|
||||
datasourcesList.append(document.createElement('br'));
|
||||
@ -158,14 +166,18 @@ export default abstract class LeafletMap {
|
||||
deleteBtn.appendChild(document.createTextNode('Discard changes'));
|
||||
deleteBtn.onclick = () => {
|
||||
this.map.removeLayer(newMarker);
|
||||
}
|
||||
const markerIndex = this.addMarkers.indexOf(newMarker);
|
||||
if (markerIndex > -1) {
|
||||
this.addMarkers.splice(markerIndex, 1);
|
||||
}
|
||||
};
|
||||
datasourcesList.append(deleteBtn);
|
||||
const popup = L.popup();
|
||||
popup.setContent(datasourcesList);
|
||||
newMarker.bindPopup(popup).openPopup();
|
||||
}
|
||||
addMarker.setPosition('topright')
|
||||
}
|
||||
addMarker.setPosition('topright');
|
||||
};
|
||||
L.Control.AddMarker = L.Control.extend({
|
||||
onAdd() {
|
||||
const img = L.DomUtil.create('img') as any;
|
||||
@ -177,7 +189,7 @@ export default abstract class LeafletMap {
|
||||
img.draggable = true;
|
||||
const draggableImg = new L.Draggable(img);
|
||||
draggableImg.enable();
|
||||
draggableImg.on('dragend', dragListener)
|
||||
draggableImg.on('dragend', dragListener);
|
||||
return img;
|
||||
},
|
||||
onRemove() {
|
||||
@ -186,7 +198,7 @@ export default abstract class LeafletMap {
|
||||
} as any);
|
||||
L.control.addMarker = (opts) => {
|
||||
return new L.Control.AddMarker(opts);
|
||||
}
|
||||
};
|
||||
addMarker = L.control.addMarker({ position: 'topright' }).addTo(this.map);
|
||||
}
|
||||
}
|
||||
@ -196,14 +208,16 @@ export default abstract class LeafletMap {
|
||||
let mousePositionOnMap: L.LatLng[];
|
||||
let addPolygon: L.Control;
|
||||
this.map.on('mousemove', (e: L.LeafletMouseEvent) => {
|
||||
const polygonOffset = this.options.provider === MapProviders.image ? 10 : 0.01;
|
||||
const latlng1 = e.latlng;
|
||||
const latlng2 = L.latLng(e.latlng.lat, e.latlng.lng + 10);
|
||||
const latlng3 = L.latLng(e.latlng.lat-10, e.latlng.lng);
|
||||
mousePositionOnMap = [latlng1,latlng2, latlng3 ];
|
||||
const latlng2 = L.latLng(e.latlng.lat, e.latlng.lng + polygonOffset);
|
||||
const latlng3 = L.latLng(e.latlng.lat - polygonOffset, e.latlng.lng);
|
||||
mousePositionOnMap = [latlng1, latlng2, latlng3];
|
||||
});
|
||||
const dragListener = (e: L.DragEndEvent) => {
|
||||
if (e.type === 'dragend' && mousePositionOnMap) {
|
||||
const newPolygon = L.polygon(mousePositionOnMap).addTo(this.map);
|
||||
this.addPolygons.push(newPolygon);
|
||||
const datasourcesList = document.createElement('div');
|
||||
const customLatLng = {[this.options.polygonKeyName]: this.convertToPolygonFormat(mousePositionOnMap)};
|
||||
const header = document.createElement('p');
|
||||
@ -218,9 +232,13 @@ export default abstract class LeafletMap {
|
||||
const updatedEnttity = { ...ds, ...customLatLng };
|
||||
this.savePolygonLocation(updatedEnttity).subscribe(() => {
|
||||
this.map.removeLayer(newPolygon);
|
||||
const polygonIndex = this.addPolygons.indexOf(newPolygon);
|
||||
if (polygonIndex > -1) {
|
||||
this.addPolygons.splice(polygonIndex, 1);
|
||||
}
|
||||
this.deletePolygon(ds.entityName);
|
||||
});
|
||||
}
|
||||
};
|
||||
datasourcesList.append(dsItem);
|
||||
});
|
||||
datasourcesList.append(document.createElement('br'));
|
||||
@ -228,14 +246,18 @@ export default abstract class LeafletMap {
|
||||
deleteBtn.appendChild(document.createTextNode('Discard changes'));
|
||||
deleteBtn.onclick = () => {
|
||||
this.map.removeLayer(newPolygon);
|
||||
}
|
||||
const polygonIndex = this.addPolygons.indexOf(newPolygon);
|
||||
if (polygonIndex > -1) {
|
||||
this.addPolygons.splice(polygonIndex, 1);
|
||||
}
|
||||
};
|
||||
datasourcesList.append(deleteBtn);
|
||||
const popup = L.popup();
|
||||
popup.setContent(datasourcesList);
|
||||
newPolygon.bindPopup(popup).openPopup();
|
||||
}
|
||||
addPolygon.setPosition('topright')
|
||||
}
|
||||
addPolygon.setPosition('topright');
|
||||
};
|
||||
L.Control.AddPolygon = L.Control.extend({
|
||||
onAdd() {
|
||||
const img = L.DomUtil.create('img') as any;
|
||||
@ -247,7 +269,7 @@ export default abstract class LeafletMap {
|
||||
img.draggable = true;
|
||||
const draggableImg = new L.Draggable(img);
|
||||
draggableImg.enable();
|
||||
draggableImg.on('dragend', dragListener)
|
||||
draggableImg.on('dragend', dragListener);
|
||||
return img;
|
||||
},
|
||||
onRemove() {
|
||||
@ -256,7 +278,7 @@ export default abstract class LeafletMap {
|
||||
} as any);
|
||||
L.control.addPolygon = (opts) => {
|
||||
return new L.Control.AddPolygon(opts);
|
||||
}
|
||||
};
|
||||
addPolygon = L.control.addPolygon({ position: 'topright' }).addTo(this.map);
|
||||
}
|
||||
}
|
||||
@ -280,10 +302,11 @@ export default abstract class LeafletMap {
|
||||
public setMap(map: L.Map) {
|
||||
this.map = map;
|
||||
if (this.options.useDefaultCenterPosition) {
|
||||
this.map.panTo(this.options.defaultCenterPosition);
|
||||
this.bounds = map.getBounds();
|
||||
this.map.panTo(this.options.defaultCenterPosition);
|
||||
this.bounds = map.getBounds();
|
||||
} else {
|
||||
this.bounds = new L.LatLngBounds(null, null);
|
||||
}
|
||||
else this.bounds = new L.LatLngBounds(null, null);
|
||||
if (this.options.draggableMarker) {
|
||||
this.addMarkerControl();
|
||||
}
|
||||
@ -299,11 +322,11 @@ export default abstract class LeafletMap {
|
||||
}
|
||||
}
|
||||
|
||||
public saveMarkerLocation(_e: FormattedData, lat?: number, lng?: number): Observable<any> {
|
||||
public saveMarkerLocation(datasource: FormattedData, lat?: number, lng?: number): Observable<any> {
|
||||
return of(null);
|
||||
}
|
||||
|
||||
public savePolygonLocation(_e: FormattedData, coordinates?: Array<[number, number]>): Observable<any> {
|
||||
public savePolygonLocation(datasource: FormattedData, coordinates?: Array<[number, number]>): Observable<any> {
|
||||
return of(null);
|
||||
}
|
||||
|
||||
@ -364,7 +387,9 @@ export default abstract class LeafletMap {
|
||||
}
|
||||
|
||||
convertPosition(expression: object): L.LatLng {
|
||||
if (!expression) return null;
|
||||
if (!expression) {
|
||||
return null;
|
||||
}
|
||||
const lat = expression[this.options.latKeyName];
|
||||
const lng = expression[this.options.lngKeyName];
|
||||
if (!isDefinedAndNotNull(lat) || isString(lat) || isNaN(lat) || !isDefinedAndNotNull(lng) || isString(lng) || isNaN(lng)) {
|
||||
@ -382,35 +407,35 @@ export default abstract class LeafletMap {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}).filter(el => !!el)
|
||||
}).filter(el => !!el);
|
||||
}
|
||||
|
||||
convertToCustomFormat(position: L.LatLng): object {
|
||||
return {
|
||||
[this.options.latKeyName]: position.lat % 90,
|
||||
[this.options.lngKeyName]: position.lng % 180
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
convertToPolygonFormat(points: Array<any>): Array<any> {
|
||||
if (points.length) {
|
||||
return points.map(point=> {
|
||||
if (point.length) {
|
||||
return this.convertToPolygonFormat(point);
|
||||
} else {
|
||||
return [point.lat, point.lng];
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return []
|
||||
convertToPolygonFormat(points: Array<any>): Array<any> {
|
||||
if (points.length) {
|
||||
return points.map(point => {
|
||||
if (point.length) {
|
||||
return this.convertToPolygonFormat(point);
|
||||
} else {
|
||||
return [point.lat, point.lng];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
convertPolygonToCustomFormat(expression: any[][]): object {
|
||||
return {
|
||||
[this.options.polygonKeyName] : this.convertToPolygonFormat(expression)
|
||||
convertPolygonToCustomFormat(expression: any[][]): object {
|
||||
return {
|
||||
[this.options.polygonKeyName] : this.convertToPolygonFormat(expression)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
updateData(drawRoutes: boolean, showPolygon: boolean) {
|
||||
this.drawRoutes = drawRoutes;
|
||||
@ -509,7 +534,7 @@ export default abstract class LeafletMap {
|
||||
this.markersCluster.addLayers(createdMarkers.map(marker => marker.leafletMarker));
|
||||
}
|
||||
if (updatedMarkers.length) {
|
||||
this.markersCluster.refreshClusters(updatedMarkers.map(marker => marker.leafletMarker))
|
||||
this.markersCluster.refreshClusters(updatedMarkers.map(marker => marker.leafletMarker));
|
||||
}
|
||||
if (deletedMarkers.length) {
|
||||
this.markersCluster.removeLayers(deletedMarkers.map(marker => marker.leafletMarker));
|
||||
@ -518,7 +543,9 @@ export default abstract class LeafletMap {
|
||||
}
|
||||
|
||||
dragMarker = (e, data = {} as FormattedData) => {
|
||||
if (e.type !== 'dragend') return;
|
||||
if (e.type !== 'dragend') {
|
||||
return;
|
||||
}
|
||||
this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) }).subscribe();
|
||||
}
|
||||
|
||||
@ -527,7 +554,7 @@ export default abstract class LeafletMap {
|
||||
const newMarker = new Marker(this, this.convertPosition(data), settings, data, dataSources, this.dragMarker);
|
||||
if (callback) {
|
||||
newMarker.leafletMarker.on('click', () => {
|
||||
callback(data, true)
|
||||
callback(data, true);
|
||||
});
|
||||
}
|
||||
if (this.bounds && updateBounds && !(this.options as MarkerSettings).useClusterMarkers) {
|
||||
@ -565,11 +592,10 @@ export default abstract class LeafletMap {
|
||||
}
|
||||
|
||||
deletePolygon(key: string) {
|
||||
let polygon = this.polygons.get(key)?.leafletPoly;
|
||||
const polygon = this.polygons.get(key)?.leafletPoly;
|
||||
if (polygon) {
|
||||
this.map.removeLayer(polygon);
|
||||
this.polygons.delete(key);
|
||||
polygon = null;
|
||||
}
|
||||
return polygon;
|
||||
}
|
||||
@ -684,7 +710,9 @@ export default abstract class LeafletMap {
|
||||
}
|
||||
|
||||
dragPolygonVertex = (e?, data = {} as FormattedData) => {
|
||||
if (e === undefined || (e.type !== 'editable:vertex:dragend' && e.type !== 'editable:vertex:deleted')) return;
|
||||
if (e === undefined || (e.type !== 'editable:vertex:dragend' && e.type !== 'editable:vertex:deleted')) {
|
||||
return;
|
||||
}
|
||||
this.savePolygonLocation({ ...data, ...this.convertPolygonToCustomFormat(e.layer._latlngs) }).subscribe();
|
||||
}
|
||||
|
||||
|
||||
@ -62,7 +62,7 @@ export type MapSettings = {
|
||||
useCustomProvider: boolean,
|
||||
customProviderTileUrl: string;
|
||||
mapPageSize: number;
|
||||
}
|
||||
};
|
||||
|
||||
export enum MapProviders {
|
||||
google = 'google-map',
|
||||
@ -103,7 +103,7 @@ export type MarkerSettings = {
|
||||
markerImageFunction?: MarkerImageFunction;
|
||||
markerOffsetX: number;
|
||||
markerOffsetY: number;
|
||||
}
|
||||
};
|
||||
|
||||
export interface FormattedData {
|
||||
$datasource: Datasource;
|
||||
@ -112,19 +112,19 @@ export interface FormattedData {
|
||||
entityType: EntityType;
|
||||
dsIndex: number;
|
||||
deviceType: string;
|
||||
[key: string]: any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface ReplaceInfo {
|
||||
variable: string;
|
||||
valDec?: number;
|
||||
dataKeyName: string
|
||||
dataKeyName: string;
|
||||
}
|
||||
|
||||
export type PolygonSettings = {
|
||||
showPolygon: boolean;
|
||||
polygonKeyName: string;
|
||||
polKeyName: string;// deprecated
|
||||
polKeyName: string; // deprecated
|
||||
polygonStrokeOpacity: number;
|
||||
polygonOpacity: number;
|
||||
polygonStrokeWeight: number;
|
||||
@ -141,7 +141,7 @@ export type PolygonSettings = {
|
||||
polygonTooltipFunction: GenericFunction;
|
||||
polygonColorFunction?: GenericFunction;
|
||||
editablePolygon: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export type PolylineSettings = {
|
||||
usePolylineDecorator: any;
|
||||
@ -166,7 +166,7 @@ export type PolylineSettings = {
|
||||
colorFunction: GenericFunction;
|
||||
strokeOpacityFunction: GenericFunction;
|
||||
strokeWeightFunction: GenericFunction;
|
||||
}
|
||||
};
|
||||
|
||||
export interface HistorySelectSettings {
|
||||
buttonColor: string;
|
||||
@ -244,4 +244,5 @@ export const hereProviders = [
|
||||
'HERE.normalDay',
|
||||
'HERE.normalNight',
|
||||
'HERE.hybridDay',
|
||||
'HERE.terrainDay']
|
||||
'HERE.terrainDay'
|
||||
];
|
||||
|
||||
@ -18,15 +18,15 @@ import { JsonSettingsSchema } from '@shared/models/widget.models';
|
||||
import { MapProviders } from './map-models';
|
||||
|
||||
export interface MapWidgetInterface {
|
||||
resize(),
|
||||
update(),
|
||||
onInit(),
|
||||
resize();
|
||||
update();
|
||||
onInit();
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
export interface MapWidgetStaticInterface {
|
||||
settingsSchema(mapProvider?: MapProviders, drawRoutes?: boolean): JsonSettingsSchema;
|
||||
getProvidersSchema(mapProvider?: MapProviders, ignoreImageMap?: boolean): JsonSettingsSchema
|
||||
getProvidersSchema(mapProvider?: MapProviders, ignoreImageMap?: boolean): JsonSettingsSchema;
|
||||
dataKeySettingsSchema(): object;
|
||||
actionSources(): object;
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ import {
|
||||
import { MapWidgetInterface, MapWidgetStaticInterface } from './map-widget.interface';
|
||||
import { addCondition, addGroupInfo, addToSchema, initSchema, mergeSchemes } from '@core/schema-utils';
|
||||
import { WidgetContext } from '@app/modules/home/models/widget-component.models';
|
||||
import { getDefCenterPosition, parseData, parseFunction, parseWithTranslation } from './maps-utils';
|
||||
import { getDefCenterPosition, parseFunction, parseWithTranslation } from './maps-utils';
|
||||
import { Datasource, DatasourceData, JsonSettingsSchema, WidgetActionDescriptor } from '@shared/models/widget.models';
|
||||
import { EntityId } from '@shared/models/id/entity-id';
|
||||
import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
|
||||
@ -102,8 +102,9 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
|
||||
public static getProvidersSchema(mapProvider: MapProviders, ignoreImageMap = false) {
|
||||
const providerSchema = _.cloneDeep(mapProviderSchema);
|
||||
if (mapProvider)
|
||||
providerSchema.schema.properties.provider.default = mapProvider;
|
||||
if (mapProvider) {
|
||||
providerSchema.schema.properties.provider.default = mapProvider;
|
||||
}
|
||||
if (ignoreImageMap) {
|
||||
providerSchema.form[0].items = providerSchema.form[0]?.items.filter(item => item.value !== 'image-map');
|
||||
}
|
||||
@ -129,7 +130,7 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
} else {
|
||||
const clusteringSchema = mergeSchemes([markerClusteringSettingsSchema,
|
||||
addCondition(markerClusteringSettingsSchemaLeaflet,
|
||||
`model.useClusterMarkers === true && model.provider !== "image-map"`)])
|
||||
`model.useClusterMarkers === true && model.provider !== "image-map"`)]);
|
||||
addToSchema(schema, clusteringSchema);
|
||||
addGroupInfo(schema, 'Markers Clustering Settings');
|
||||
}
|
||||
@ -154,10 +155,11 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
}
|
||||
|
||||
translate = (key: string, defaultTranslation?: string): string => {
|
||||
if (key)
|
||||
return (this.ctx.$injector.get(UtilsService).customTranslation(key, defaultTranslation || key)
|
||||
|| this.ctx.$injector.get(TranslateService).instant(key));
|
||||
else return '';
|
||||
if (key) {
|
||||
return (this.ctx.$injector.get(UtilsService).customTranslation(key, defaultTranslation || key)
|
||||
|| this.ctx.$injector.get(TranslateService).instant(key));
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
getDescriptors(name: string): { [name: string]: ($event: Event, datasource: Datasource) => void } {
|
||||
@ -211,10 +213,10 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
}
|
||||
if (value) {
|
||||
if (key.type === DataKeyType.attribute) {
|
||||
attributes.push(value)
|
||||
attributes.push(value);
|
||||
}
|
||||
if (key.type === DataKeyType.timeseries) {
|
||||
timeseries.push(value)
|
||||
timeseries.push(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -253,7 +255,7 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
const coordinatesProperties = this.settings.polygonKeyName;
|
||||
e.$datasource.dataKeys.forEach(key => {
|
||||
let value;
|
||||
if (coordinatesProperties == key.name) {
|
||||
if (coordinatesProperties === key.name) {
|
||||
value = {
|
||||
key: key.name,
|
||||
value: isDefined(coordinates) ? coordinates : e[key.name]
|
||||
@ -261,10 +263,10 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
}
|
||||
if (value) {
|
||||
if (key.type === DataKeyType.attribute) {
|
||||
attributes.push(value)
|
||||
attributes.push(value);
|
||||
}
|
||||
if (key.type === DataKeyType.timeseries) {
|
||||
timeseries.push(value)
|
||||
timeseries.push(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -294,9 +296,11 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
const functionParams = ['data', 'dsData', 'dsIndex'];
|
||||
this.provider = settings.provider || this.mapProvider;
|
||||
if (this.provider === MapProviders.here && !settings.mapProviderHere) {
|
||||
if (settings.mapProvider && hereProviders.includes(settings.mapProvider))
|
||||
settings.mapProviderHere = settings.mapProvider
|
||||
else settings.mapProviderHere = hereProviders[0];
|
||||
if (settings.mapProvider && hereProviders.includes(settings.mapProvider)) {
|
||||
settings.mapProviderHere = settings.mapProvider;
|
||||
} else {
|
||||
settings.mapProviderHere = hereProviders[0];
|
||||
}
|
||||
}
|
||||
const customOptions = {
|
||||
provider: this.provider,
|
||||
@ -317,14 +321,14 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
url: settings.markerImage,
|
||||
size: settings.markerImageSize || 34
|
||||
} : null
|
||||
}
|
||||
};
|
||||
if (isEditMap && !settings.hasOwnProperty('draggableMarker')) {
|
||||
settings.draggableMarker = true;
|
||||
}
|
||||
if (isEditMap && !settings.hasOwnProperty('editablePolygon')) {
|
||||
settings.editablePolygon = true;
|
||||
}
|
||||
return { ...defaultSettings, ...settings, ...customOptions, }
|
||||
return { ...defaultSettings, ...settings, ...customOptions, };
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
@ -31,9 +31,9 @@ import {
|
||||
} from '@core/utils';
|
||||
|
||||
export function createTooltip(target: L.Layer,
|
||||
settings: MarkerSettings | PolylineSettings | PolygonSettings,
|
||||
datasource: Datasource,
|
||||
content?: string | HTMLElement
|
||||
settings: MarkerSettings | PolylineSettings | PolygonSettings,
|
||||
datasource: Datasource,
|
||||
content?: string | HTMLElement
|
||||
): L.Popup {
|
||||
const popup = L.popup();
|
||||
popup.setContent(content);
|
||||
@ -87,7 +87,7 @@ export function interpolateOnLineSegment(
|
||||
}
|
||||
|
||||
export function findAngle(startPoint: FormattedData, endPoint: FormattedData, latKeyName: string, lngKeyName: string): number {
|
||||
if(isUndefined(startPoint) || isUndefined(endPoint)){
|
||||
if (isUndefined(startPoint) || isUndefined(endPoint)) {
|
||||
return 0;
|
||||
}
|
||||
let angle = -Math.atan2(endPoint[latKeyName] - startPoint[latKeyName], endPoint[lngKeyName] - startPoint[lngKeyName]);
|
||||
@ -97,11 +97,13 @@ export function findAngle(startPoint: FormattedData, endPoint: FormattedData, la
|
||||
|
||||
|
||||
export function getDefCenterPosition(position) {
|
||||
if (typeof (position) === 'string')
|
||||
return position.split(',');
|
||||
if (typeof (position) === 'object')
|
||||
return position;
|
||||
return [0, 0];
|
||||
if (typeof (position) === 'string') {
|
||||
return position.split(',');
|
||||
}
|
||||
if (typeof (position) === 'object') {
|
||||
return position;
|
||||
}
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
|
||||
@ -123,7 +125,7 @@ function imageLoader(imageUrl: string): Observable<HTMLImageElement> {
|
||||
document.body.removeChild(image);
|
||||
observer.complete();
|
||||
};
|
||||
document.body.appendChild(image)
|
||||
document.body.appendChild(image);
|
||||
image.src = imageUrl;
|
||||
});
|
||||
}
|
||||
@ -135,11 +137,11 @@ export function aspectCache(imageUrl: string): Observable<number> {
|
||||
if (aspect) {
|
||||
return of(aspect);
|
||||
}
|
||||
else return imageLoader(imageUrl).pipe(map(image => {
|
||||
return imageLoader(imageUrl).pipe(map(image => {
|
||||
aspect = image.width / image.height;
|
||||
imageAspectMap[hash] = aspect;
|
||||
return aspect;
|
||||
}))
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,7 +223,7 @@ function parseTemplate(template: string, data: { $datasource?: Datasource, [key:
|
||||
// res = compiled(data);
|
||||
res = template;
|
||||
} catch (ex) {
|
||||
console.log(ex, template)
|
||||
console.log(ex, template);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -263,7 +265,7 @@ export function processPattern(template: string, data: { $datasource?: Datasourc
|
||||
match = reg.exec(template);
|
||||
}
|
||||
} catch (ex) {
|
||||
console.log(ex, template)
|
||||
console.log(ex, template);
|
||||
}
|
||||
return replaceInfo;
|
||||
}
|
||||
@ -271,7 +273,7 @@ export function processPattern(template: string, data: { $datasource?: Datasourc
|
||||
export function fillPattern(markerLabelText: string, replaceInfoLabelMarker: Array<ReplaceInfo>, data: FormattedData) {
|
||||
let text = createLabelFromDatasource(data.$datasource, markerLabelText);
|
||||
if (replaceInfoLabelMarker) {
|
||||
for(const variableInfo of replaceInfoLabelMarker) {
|
||||
for (const variableInfo of replaceInfoLabelMarker) {
|
||||
let txtVal = '';
|
||||
if (variableInfo.dataKeyName && isDefinedAndNotNull(data[variableInfo.dataKeyName])) {
|
||||
const varData = data[variableInfo.dataKeyName];
|
||||
@ -334,7 +336,7 @@ export const parseWithTranslation = {
|
||||
setTranslate(translateFn: TranslateFunc) {
|
||||
this.translateFn = translateFn;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function parseData(input: DatasourceData[]): FormattedData[] {
|
||||
return _(input).groupBy(el => el?.datasource?.entityName)
|
||||
@ -361,7 +363,7 @@ export function parseData(input: DatasourceData[]): FormattedData[] {
|
||||
|
||||
export function parseArray(input: DatasourceData[]): FormattedData[][] {
|
||||
return _(input).groupBy(el => el?.datasource?.entityName)
|
||||
.values().value().map((entityArray, dsIndex) =>
|
||||
.values().value().map((entityArray) =>
|
||||
entityArray[0].data.map((el, i) => {
|
||||
const obj: FormattedData = {
|
||||
entityName: entityArray[0]?.datasource?.entityName,
|
||||
|
||||
@ -81,7 +81,7 @@ export class Marker {
|
||||
}
|
||||
|
||||
updateMarkerTooltip(data: FormattedData) {
|
||||
if(!this.map.markerTooltipText || this.settings.useTooltipFunction) {
|
||||
if (!this.map.markerTooltipText || this.settings.useTooltipFunction) {
|
||||
const pattern = this.settings.useTooltipFunction ?
|
||||
safeExecute(this.settings.tooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern;
|
||||
this.map.markerTooltipText = parseWithTranslation.prepareProcessPattern(pattern, true);
|
||||
@ -103,7 +103,7 @@ export class Marker {
|
||||
updateMarkerLabel(settings: MarkerSettings) {
|
||||
this.leafletMarker.unbindTooltip();
|
||||
if (settings.showLabel) {
|
||||
if(!this.map.markerLabelText || settings.useLabelFunction) {
|
||||
if (!this.map.markerLabelText || settings.useLabelFunction) {
|
||||
const pattern = settings.useLabelFunction ?
|
||||
safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.label;
|
||||
this.map.markerLabelText = parseWithTranslation.prepareProcessPattern(pattern, true);
|
||||
|
||||
@ -55,7 +55,7 @@ export class Polygon {
|
||||
this.leafletPoly.on('click', (event: LeafletMouseEvent) => {
|
||||
for (const action in this.settings.polygonClick) {
|
||||
if (typeof (this.settings.polygonClick[action]) === 'function') {
|
||||
this.settings.polygonClick[action](event.originalEvent, polyData.datasource);
|
||||
this.settings.polygonClick[action](event.originalEvent, polyData.$datasource);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -70,18 +70,19 @@ export class Polygon {
|
||||
}
|
||||
|
||||
updatePolygon(data: FormattedData, dataSources: FormattedData[], settings: PolygonSettings) {
|
||||
this.data = data;
|
||||
this.dataSources = dataSources;
|
||||
if (settings.editablePolygon) {
|
||||
this.leafletPoly.disableEdit();
|
||||
}
|
||||
this.leafletPoly.setLatLngs(data[this.settings.polygonKeyName]);
|
||||
this.data = data;
|
||||
this.dataSources = dataSources;
|
||||
if (settings.editablePolygon) {
|
||||
this.leafletPoly.disableEdit();
|
||||
}
|
||||
this.leafletPoly.setLatLngs(data[this.settings.polygonKeyName]);
|
||||
if (settings.editablePolygon) {
|
||||
this.leafletPoly.enableEdit(this.map);
|
||||
}
|
||||
if (settings.showPolygonTooltip)
|
||||
this.updateTooltip(this.data);
|
||||
this.updatePolygonColor(settings);
|
||||
if (settings.showPolygonTooltip) {
|
||||
this.updateTooltip(this.data);
|
||||
}
|
||||
this.updatePolygonColor(settings);
|
||||
}
|
||||
|
||||
removePolygon() {
|
||||
|
||||
@ -57,7 +57,7 @@ export class Polyline {
|
||||
})
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
updatePolyline(locations: L.LatLng[], data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings) {
|
||||
@ -65,8 +65,9 @@ export class Polyline {
|
||||
this.dataSources = dataSources;
|
||||
this.leafletPoly.setLatLngs(locations);
|
||||
this.leafletPoly.setStyle(this.getPolyStyle(settings));
|
||||
if (this.polylineDecorator)
|
||||
if (this.polylineDecorator) {
|
||||
this.polylineDecorator.setPaths(this.leafletPoly);
|
||||
}
|
||||
}
|
||||
|
||||
getPolyStyle(settings: PolylineSettings): L.PolylineOptions {
|
||||
@ -78,7 +79,7 @@ export class Polyline {
|
||||
[this.data, this.dataSources, this.data.dsIndex], settings.strokeOpacity),
|
||||
weight: functionValueCalculator(settings.useStrokeWeightFunction, settings.strokeWeightFunction,
|
||||
[this.data, this.dataSources, this.data.dsIndex], settings.strokeWeight)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
removePolyline() {
|
||||
|
||||
@ -49,7 +49,7 @@ export class GoogleMap extends LeafletMap {
|
||||
|
||||
private loadGoogle(callback, apiKey = 'AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q') {
|
||||
if (gmGlobals[apiKey]) {
|
||||
callback()
|
||||
callback();
|
||||
} else {
|
||||
this.resource.loadResource(`https://maps.googleapis.com/maps/api/js?key=${apiKey}`).subscribe(
|
||||
() => {
|
||||
|
||||
@ -26,7 +26,7 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||
import { WidgetSubscriptionOptions } from '@core/api/widget-api.models';
|
||||
import { isDefinedAndNotNull, isEmptyStr } from '@core/utils';
|
||||
|
||||
const maxZoom = 4;// ?
|
||||
const maxZoom = 4; // ?
|
||||
|
||||
export class ImageMap extends LeafletMap {
|
||||
|
||||
@ -162,52 +162,64 @@ export class ImageMap extends LeafletMap {
|
||||
}
|
||||
|
||||
onResize(updateImage?: boolean) {
|
||||
let width = this.$container.clientWidth;
|
||||
if (width > 0 && this.aspect) {
|
||||
let height = width / this.aspect;
|
||||
const imageMapHeight = this.$container.clientHeight;
|
||||
if (imageMapHeight > 0 && height > imageMapHeight) {
|
||||
height = imageMapHeight;
|
||||
width = height * this.aspect;
|
||||
}
|
||||
width *= maxZoom;
|
||||
const prevWidth = this.width;
|
||||
const prevHeight = this.height;
|
||||
if (this.width !== width || updateImage) {
|
||||
this.width = width;
|
||||
this.height = width / this.aspect;
|
||||
if (!this.map) {
|
||||
this.initMap(updateImage);
|
||||
} else {
|
||||
const lastCenterPos = this.latLngToPoint(this.map.getCenter());
|
||||
lastCenterPos.x /= prevWidth;
|
||||
lastCenterPos.y /= prevHeight;
|
||||
this.updateBounds(updateImage, lastCenterPos);
|
||||
this.map.invalidateSize(true);
|
||||
this.updateMarkers(this.markersData);
|
||||
this.updatePolygons(this.polygonsData);
|
||||
}
|
||||
}
|
||||
let width = this.$container.clientWidth;
|
||||
if (width > 0 && this.aspect) {
|
||||
let height = width / this.aspect;
|
||||
const imageMapHeight = this.$container.clientHeight;
|
||||
if (imageMapHeight > 0 && height > imageMapHeight) {
|
||||
height = imageMapHeight;
|
||||
width = height * this.aspect;
|
||||
}
|
||||
width *= maxZoom;
|
||||
const prevWidth = this.width;
|
||||
const prevHeight = this.height;
|
||||
if (this.width !== width || updateImage) {
|
||||
this.width = width;
|
||||
this.height = width / this.aspect;
|
||||
if (!this.map) {
|
||||
this.initMap(updateImage);
|
||||
} else {
|
||||
const lastCenterPos = this.latLngToPoint(this.map.getCenter());
|
||||
lastCenterPos.x /= prevWidth;
|
||||
lastCenterPos.y /= prevHeight;
|
||||
this.updateBounds(updateImage, lastCenterPos);
|
||||
this.map.invalidateSize(true);
|
||||
this.updateMarkers(this.markersData);
|
||||
if (this.options.draggableMarker && this.addMarkers.length) {
|
||||
this.addMarkers.forEach((marker) => {
|
||||
const prevPoint = this.convertToCustomFormat(marker.getLatLng(), prevWidth, prevHeight);
|
||||
marker.setLatLng(this.convertPosition(prevPoint));
|
||||
});
|
||||
}
|
||||
this.updatePolygons(this.polygonsData);
|
||||
if (this.options.showPolygon && this.options.editablePolygon && this.addPolygons.length) {
|
||||
this.addPolygons.forEach((polygon) => {
|
||||
const prevPolygonPoint = this.convertToPolygonFormat(polygon.getLatLngs(), prevWidth, prevHeight);
|
||||
polygon.setLatLngs(this.convertPositionPolygon(prevPolygonPoint));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fitBounds(bounds: LatLngBounds, padding?: LatLngTuple) { }
|
||||
|
||||
initMap(updateImage?: boolean) {
|
||||
if (!this.map && this.aspect > 0) {
|
||||
const center = this.pointToLatLng(this.width / 2, this.height / 2);
|
||||
this.map = L.map(this.$container, {
|
||||
minZoom: 1,
|
||||
maxZoom,
|
||||
scrollWheelZoom: !this.options.disableScrollZooming,
|
||||
center,
|
||||
zoom: 1,
|
||||
crs: L.CRS.Simple,
|
||||
attributionControl: false,
|
||||
editable: !!this.options.editablePolygon
|
||||
});
|
||||
this.updateBounds(updateImage);
|
||||
}
|
||||
if (!this.map && this.aspect > 0) {
|
||||
const center = this.pointToLatLng(this.width / 2, this.height / 2);
|
||||
this.map = L.map(this.$container, {
|
||||
minZoom: 1,
|
||||
maxZoom,
|
||||
scrollWheelZoom: !this.options.disableScrollZooming,
|
||||
center,
|
||||
zoom: 1,
|
||||
crs: L.CRS.Simple,
|
||||
attributionControl: false,
|
||||
editable: !!this.options.editablePolygon
|
||||
});
|
||||
this.updateBounds(updateImage);
|
||||
}
|
||||
}
|
||||
|
||||
convertPosition(expression): L.LatLng {
|
||||
@ -227,13 +239,14 @@ export class ImageMap extends LeafletMap {
|
||||
if (!Array.isArray(el[0]) && !Array.isArray(el[1]) && el.length === 2) {
|
||||
return this.pointToLatLng(
|
||||
el[0] * this.width,
|
||||
el[1] * this.height)
|
||||
el[1] * this.height
|
||||
);
|
||||
} else if (Array.isArray(el) && el.length) {
|
||||
return this.convertPositionPolygon(el as LatLngTuple[] | LatLngTuple[][]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}).filter(el => !!el)
|
||||
}).filter(el => !!el);
|
||||
}
|
||||
|
||||
pointToLatLng(x, y): L.LatLng {
|
||||
@ -244,32 +257,32 @@ export class ImageMap extends LeafletMap {
|
||||
return L.CRS.Simple.latLngToPoint(latLng, maxZoom - 1);
|
||||
}
|
||||
|
||||
convertToCustomFormat(position: L.LatLng): object {
|
||||
const point = this.latLngToPoint(position);
|
||||
return {
|
||||
[this.options.xPosKeyName]: calculateNewPointCoordinate(point.x, this.width),
|
||||
[this.options.yPosKeyName]: calculateNewPointCoordinate(point.y, this.height)
|
||||
}
|
||||
convertToCustomFormat(position: L.LatLng, width = this.width, height = this.height): object {
|
||||
const point = this.latLngToPoint(position);
|
||||
return {
|
||||
[this.options.xPosKeyName]: calculateNewPointCoordinate(point.x, width),
|
||||
[this.options.yPosKeyName]: calculateNewPointCoordinate(point.y, height)
|
||||
};
|
||||
}
|
||||
|
||||
convertToPolygonFormat(points: Array<any>): Array<any> {
|
||||
convertToPolygonFormat(points: Array<any>, width = this.width, height = this.height): Array<any> {
|
||||
if (points.length) {
|
||||
return points.map(point=> {
|
||||
return points.map(point => {
|
||||
if (point.length) {
|
||||
return this.convertToPolygonFormat(point);
|
||||
return this.convertToPolygonFormat(point, width, height);
|
||||
} else {
|
||||
const pos = this.latLngToPoint(point);
|
||||
return [calculateNewPointCoordinate(pos.x, this.width), calculateNewPointCoordinate(pos.y, this.height)];
|
||||
return [calculateNewPointCoordinate(pos.x, width), calculateNewPointCoordinate(pos.y, height)];
|
||||
}
|
||||
})
|
||||
});
|
||||
} else {
|
||||
return []
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
convertPolygonToCustomFormat(expression: any[][]): object {
|
||||
return {
|
||||
[this.options.polygonKeyName] : this.convertToPolygonFormat(expression)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,16 +29,16 @@ import LeafletMap from '@home/components/widget/lib/maps/leaflet-map';
|
||||
import { JsonSettingsSchema } from '@shared/models/widget.models';
|
||||
|
||||
interface IProvider {
|
||||
MapClass: Type<LeafletMap>,
|
||||
schema: JsonSettingsSchema,
|
||||
name: string
|
||||
MapClass: Type<LeafletMap>;
|
||||
schema: JsonSettingsSchema;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const providerSets: { [key: string]: IProvider } = {
|
||||
'openstreet-map': {
|
||||
MapClass: OpenStreetMap,
|
||||
schema: openstreetMapSettingsSchema,
|
||||
name: 'openstreet-map',
|
||||
name: 'openstreet-map'
|
||||
},
|
||||
'tencent-map': {
|
||||
MapClass: TencentMap,
|
||||
|
||||
@ -26,10 +26,11 @@ export class OpenStreetMap extends LeafletMap {
|
||||
editable: !!options.editablePolygon
|
||||
}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
|
||||
let tileLayer;
|
||||
if (options.useCustomProvider)
|
||||
tileLayer = L.tileLayer(options.customProviderTileUrl);
|
||||
else
|
||||
tileLayer = (L.tileLayer as any).provider(options.mapProvider || 'OpenStreetMap.Mapnik');
|
||||
if (options.useCustomProvider) {
|
||||
tileLayer = L.tileLayer(options.customProviderTileUrl);
|
||||
} else {
|
||||
tileLayer = (L.tileLayer as any).provider(options.mapProvider || 'OpenStreetMap.Mapnik');
|
||||
}
|
||||
tileLayer.addTo(map);
|
||||
super.initSettings(options);
|
||||
super.setMap(map);
|
||||
|
||||
@ -1068,31 +1068,31 @@ export const tripAnimationSchema = {
|
||||
key: 'labelFunction',
|
||||
type: 'javascript'
|
||||
}, 'showTooltip', {
|
||||
key: 'tooltipColor',
|
||||
type: 'color'
|
||||
}, {
|
||||
key: 'tooltipFontColor',
|
||||
type: 'color'
|
||||
}, 'tooltipOpacity', {
|
||||
key: 'tooltipPattern',
|
||||
type: 'textarea'
|
||||
}, 'useTooltipFunction', {
|
||||
key: 'tooltipFunction',
|
||||
type: 'javascript'
|
||||
}, 'autocloseTooltip', {
|
||||
key: 'markerImage',
|
||||
type: 'image'
|
||||
}, 'markerImageSize', 'rotationAngle', 'useMarkerImageFunction',
|
||||
{
|
||||
key: 'markerImageFunction',
|
||||
type: 'javascript'
|
||||
}, {
|
||||
key: 'markerImages',
|
||||
items: [
|
||||
{
|
||||
key: 'markerImages[]',
|
||||
type: 'image'
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
key: 'tooltipColor',
|
||||
type: 'color'
|
||||
}, {
|
||||
key: 'tooltipFontColor',
|
||||
type: 'color'
|
||||
}, 'tooltipOpacity', {
|
||||
key: 'tooltipPattern',
|
||||
type: 'textarea'
|
||||
}, 'useTooltipFunction', {
|
||||
key: 'tooltipFunction',
|
||||
type: 'javascript'
|
||||
}, 'autocloseTooltip', {
|
||||
key: 'markerImage',
|
||||
type: 'image'
|
||||
}, 'markerImageSize', 'rotationAngle', 'useMarkerImageFunction',
|
||||
{
|
||||
key: 'markerImageFunction',
|
||||
type: 'javascript'
|
||||
}, {
|
||||
key: 'markerImages',
|
||||
items: [
|
||||
{
|
||||
key: 'markerImages[]',
|
||||
type: 'image'
|
||||
}
|
||||
]
|
||||
}]
|
||||
};
|
||||
|
||||
@ -35,7 +35,6 @@ import {
|
||||
} from '@angular/core';
|
||||
import { DashboardWidget } from '@home/models/dashboard-component.models';
|
||||
import {
|
||||
Datasource,
|
||||
defaultLegendConfig,
|
||||
LegendConfig,
|
||||
LegendData,
|
||||
@ -55,7 +54,7 @@ import { AppState } from '@core/core.state';
|
||||
import { WidgetService } from '@core/http/widget.service';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { forkJoin, Observable, of, ReplaySubject, Subscription, throwError } from 'rxjs';
|
||||
import { deepClone, isDefined, objToBase64 } from '@core/utils';
|
||||
import { deepClone, isDefined, objToBase64URI } from '@core/utils';
|
||||
import {
|
||||
IDynamicWidgetComponent,
|
||||
WidgetContext,
|
||||
@ -68,7 +67,8 @@ import {
|
||||
StateObject,
|
||||
StateParams,
|
||||
SubscriptionEntityInfo,
|
||||
SubscriptionInfo, SubscriptionMessage,
|
||||
SubscriptionInfo,
|
||||
SubscriptionMessage,
|
||||
WidgetSubscriptionContext,
|
||||
WidgetSubscriptionOptions
|
||||
} from '@core/api/widget-api.models';
|
||||
@ -80,11 +80,9 @@ import { catchError, switchMap } from 'rxjs/operators';
|
||||
import { ActionNotificationShow } from '@core/notification/notification.actions';
|
||||
import { TimeService } from '@core/services/time.service';
|
||||
import { DeviceService } from '@app/core/http/device.service';
|
||||
import { AlarmService } from '@app/core/http/alarm.service';
|
||||
import { ExceptionData } from '@shared/models/error.models';
|
||||
import { WidgetComponentService } from './widget-component.service';
|
||||
import { Timewindow } from '@shared/models/time/time.models';
|
||||
import { AlarmSearchStatus } from '@shared/models/alarm.models';
|
||||
import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
|
||||
import { DashboardService } from '@core/http/dashboard.service';
|
||||
import { WidgetSubscription } from '@core/api/widget-subscription';
|
||||
@ -688,7 +686,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
|
||||
|
||||
private destroyDynamicWidgetComponent() {
|
||||
if (this.widgetContext.$containerParent && this.widgetResize$) {
|
||||
this.widgetResize$.disconnect()
|
||||
this.widgetResize$.disconnect();
|
||||
}
|
||||
if (this.dynamicWidgetComponentRef) {
|
||||
this.dynamicWidgetComponentRef.destroy();
|
||||
@ -1023,7 +1021,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
|
||||
if (targetDashboardStateId) {
|
||||
stateObject.id = targetDashboardStateId;
|
||||
}
|
||||
const state = objToBase64([ stateObject ]);
|
||||
const state = objToBase64URI([ stateObject ]);
|
||||
const isSinglePage = this.route.snapshot.data.singlePageMode;
|
||||
let url;
|
||||
if (isSinglePage) {
|
||||
|
||||
@ -23,7 +23,7 @@ import { StateControllerComponent } from './state-controller.component';
|
||||
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service';
|
||||
import { EntityId } from '@app/shared/models/id/entity-id';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { base64toObj, objToBase64 } from '@app/core/utils';
|
||||
import { base64toObj, objToBase64URI } from '@app/core/utils';
|
||||
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
|
||||
import { EntityService } from '@core/http/entity.service';
|
||||
|
||||
@ -237,7 +237,7 @@ export class DefaultStateControllerComponent extends StateControllerComponent im
|
||||
|
||||
private updateLocation() {
|
||||
if (this.stateObject[0].id) {
|
||||
const newState = objToBase64(this.stateObject);
|
||||
const newState = objToBase64URI(this.stateObject);
|
||||
this.updateStateParam(newState);
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import { StateControllerComponent } from './state-controller.component';
|
||||
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service';
|
||||
import { EntityId } from '@app/shared/models/id/entity-id';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { base64toObj, insertVariable, isEmpty, objToBase64 } from '@app/core/utils';
|
||||
import { base64toObj, insertVariable, isEmpty, objToBase64URI } from '@app/core/utils';
|
||||
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
|
||||
import { EntityService } from '@core/http/entity.service';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
@ -281,7 +281,7 @@ export class EntityStateControllerComponent extends StateControllerComponent imp
|
||||
if (this.isDefaultState()) {
|
||||
newState = null;
|
||||
} else {
|
||||
newState = objToBase64(this.stateObject);
|
||||
newState = objToBase64URI(this.stateObject);
|
||||
}
|
||||
this.updateStateParam(newState);
|
||||
}
|
||||
|
||||
@ -2138,7 +2138,7 @@ base64-arraybuffer@0.1.5:
|
||||
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
|
||||
integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg=
|
||||
|
||||
base64-js@^1.0.2, base64-js@^1.3.1:
|
||||
base64-js@^1.0.2:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user