Browse Source

Fix conflicts

pull/2692/head
Igor Kulikov 6 years ago
parent
commit
413ad261e2
  1. 6
      application/src/main/data/json/system/widget_bundles/input_widgets.json
  2. 18
      application/src/main/data/json/system/widget_bundles/maps.json
  3. 40
      ui-ngx/package-lock.json
  4. 4
      ui-ngx/package.json
  5. 22
      ui-ngx/src/app/core/schema-utils.ts
  6. 52
      ui-ngx/src/app/core/utils.ts
  7. 60
      ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts
  8. 7
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts
  9. 170
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
  10. 6
      ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss
  11. 1
      ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts
  12. 9
      ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts
  13. 27
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
  14. 6
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts
  15. 14
      ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts
  16. 2
      ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html
  17. 8
      ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts
  18. 44
      ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss
  19. 2
      ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts
  20. 2
      ui-ngx/src/app/shared/pipe/template.pipe.ts
  21. 3
      ui-ngx/src/tsconfig.app.json
  22. 3
      ui-ngx/tslint.json

6
application/src/main/data/json/system/widget_bundles/input_widgets.json

File diff suppressed because one or more lines are too long

18
application/src/main/data/json/system/widget_bundles/maps.json

File diff suppressed because one or more lines are too long

40
ui-ngx/package-lock.json

@ -1886,19 +1886,19 @@
"@types/geojson": "*"
}
},
"@types/leaflet-polylinedecorator": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@types/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz",
"integrity": "sha512-Z2BXZDjKEqHclwrAmhYdF1RwyFfa/NFxsoF79sitzaj5D/4YWHp/zDRcUZar5cQFKRgK66AYEIF7nKVuMzUGdw==",
"@types/leaflet-markercluster": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@types/leaflet-markercluster/-/leaflet-markercluster-1.0.3.tgz",
"integrity": "sha1-ZBUb5FP2SQ6HUVAEgt65YQZOeCw=",
"dev": true,
"requires": {
"@types/leaflet": "*"
}
},
"@types/leaflet.markercluster": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/leaflet.markercluster/-/leaflet.markercluster-1.4.2.tgz",
"integrity": "sha512-QQ//hevAxMH2dlRQdRre7V/1G+TbtuDtZnZF/75TNwVIgklrsQVCIcS/cvLsl7UUryfPJ6xmoYHfFzK5iGVgpg==",
"@types/leaflet-polylinedecorator": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@types/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz",
"integrity": "sha512-Z2BXZDjKEqHclwrAmhYdF1RwyFfa/NFxsoF79sitzaj5D/4YWHp/zDRcUZar5cQFKRgK66AYEIF7nKVuMzUGdw==",
"dev": true,
"requires": {
"@types/leaflet": "*"
@ -4873,9 +4873,9 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.420",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.420.tgz",
"integrity": "sha512-iVmQhf25F+5bdAyDrfOmCMjyLlIwsr9UT/LyYPQ3J1Vrypr9IgHf2PxqlsnzicnRAYDev6S9cl1tYlDHZUHY/g==",
"version": "1.3.421",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.421.tgz",
"integrity": "sha512-ogxgmvHGfDuLA+GtgfK0jkFWlBb4MCZK2U1MM+l98sf4U3Ixtrfw1iC9w4mQqNvo+lHgM4pR62TqoT4QrvKJCw==",
"dev": true
},
"elliptic": {
@ -5946,9 +5946,9 @@
}
},
"graceful-fs": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"hammerjs": {
@ -8881,9 +8881,9 @@
}
},
"npm-registry-fetch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.3.tgz",
"integrity": "sha512-WGvUx0lkKFhu9MbiGFuT9nG2NpfQ+4dCJwRwwtK2HK5izJEvwDxMeUyqbuMS7N/OkpVCqDorV6rO5E4V9F8lJw==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.4.tgz",
"integrity": "sha512-6jb34hX/iYNQebqWUHtU8YF6Cjb1H6ouTFPClYsyiW6lpFkljTpdeftm53rRojtja1rKAvKNIIiTS5Sjpw4wsA==",
"dev": true,
"requires": {
"JSONStream": "^1.3.4",
@ -10441,9 +10441,9 @@
}
},
"postcss-value-parser": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz",
"integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
"dev": true
},
"prepend-http": {

4
ui-ngx/package.json

@ -61,7 +61,7 @@
"leaflet-geometryutil": "^0.9.3",
"leaflet-polylinedecorator": "^1.6.0",
"leaflet-providers": "^1.9.1",
"leaflet.gridlayer.googlemutant": "^0.8.0",
"leaflet.gridlayer.googlemutant": "0.8.0",
"leaflet.markercluster": "^1.4.1",
"material-design-icons": "^3.0.1",
"messageformat": "^2.3.0",
@ -108,8 +108,8 @@
"@types/jstree": "^3.3.39",
"@types/jszip": "^3.1.7",
"@types/leaflet": "^1.5.12",
"@types/leaflet.markercluster": "^1.4.2",
"@types/leaflet-polylinedecorator": "^1.6.0",
"@types/leaflet-markercluster": "^1.0.3",
"@types/lodash": "^4.14.150",
"@types/raphael": "^2.3.0",
"@types/react": "^16.9.34",

22
ui-ngx/src/app/core/schema-utils.ts

@ -62,19 +62,21 @@ export function mergeSchemes(schemes: JsonSettingsSchema[]): JsonSettingsSchema
}, initSchema());
}
export function addCondition(schema: JsonSettingsSchema, condition: string): JsonSettingsSchema {
export function addCondition(schema: JsonSettingsSchema, condition: string, exclude: string[] = []): JsonSettingsSchema {
schema.form = schema.form.map(element => {
if (typeof element === 'string') {
return {
key: element,
condition
if (!exclude.includes(element) && !exclude.includes(element.key)) {
if (typeof element === 'string') {
return {
key: element,
condition
}
}
}
if (typeof element === 'object') {
if (element.condition) {
element.condition += ' && ' + condition
if (typeof element === 'object') {
if (element.condition) {
element.condition += ' && ' + condition
}
else element.condition = condition;
}
else element.condition = condition;
}
return element;
});

52
ui-ngx/src/app/core/utils.ts

@ -15,8 +15,8 @@
///
import _ from 'lodash';
import { fromEvent, Observable, of, Subject } from 'rxjs';
import { finalize, map, share } from 'rxjs/operators';
import { Observable, Subject, fromEvent, of } from 'rxjs';
import { finalize, share, map } from 'rxjs/operators';
import base64js from 'base64-js';
export function onParentScrollOrWindowResize(el: Node): Observable<Event> {
@ -224,7 +224,7 @@ function scrollParents(node: Node): Node[] {
function hashCode(str) {
let hash = 0;
let i, char;
if (str.length == 0) return hash;
if (str.length === 0) return hash;
for (i = 0; i < str.length; i++) {
char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
@ -464,7 +464,7 @@ export function parseArray(input: any[]): any[] {
time: el[0],
deviceType: null
};
entityArray.forEach(entity => {
entityArray.filter(el => el.data.length).forEach(entity => {
obj[entity?.dataKey?.label] = entity?.data[i][1];
obj[entity?.dataKey?.label + '|ts'] = entity?.data[0][0];
if (entity?.dataKey?.label === 'type') {
@ -485,7 +485,7 @@ export function parseData(input: any[]): any[] {
dsIndex: i,
deviceType: null
};
entityArray.forEach(el => {
entityArray.filter(el => el.data.length).forEach(el => {
obj[el?.dataKey?.label] = el?.data[0][1];
obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
if (el?.dataKey?.label === 'type') {
@ -510,14 +510,13 @@ export function safeExecute(func: Function, params = []) {
return res;
}
export function parseFunction(source: any, params: string[] = []): Function {
export function parseFunction(source: any, params: string[] = ['def']): Function {
let res = null;
if (source?.length) {
try {
res = new Function(...params, source);
}
catch (err) {
console.error(err);
res = null;
}
}
@ -526,33 +525,34 @@ export function parseFunction(source: any, params: string[] = []): Function {
export function parseTemplate(template: string, data: object, translateFn?: (key: string) => string) {
let res = '';
let variables = '';
try {
if (template.match(/<link-act/g)) {
template = template.replace(/<link-act/g, '<a').replace(/link-act>/g, 'a>').replace(/name=(\'|")(.*?)(\'|")/g, `class='tb-custom-action' id='$2'`);
}
if (template.includes('i18n')) {
const translateRegexp = /\{i18n:(.*?)\}/;
template.match(new RegExp(translateRegexp.source, translateRegexp.flags + 'g')).forEach(match => {
template = template.replace(match, translateFn(match.match(translateRegexp)[1]));
});
if (translateFn) {
template = translateFn(template);
}
const expressions = template.match(/\{(.*?)\}/g);
if (expressions) {
// TODO: not supported in IE
// const clearMatches = template.match(/(?<=\{)(.+?)(?=(\}|\:))/g);
const clearMatches = template.match(/\{(.+?)(\}|\:)/g);
for (const key in data) {
if (!key.includes('|'))
variables += `let ${key} = '${clearMatches[key] ? padValue(data[key], +clearMatches[key]) : data[key]}';`;
}
template = template.replace(/\:\d+\}/g, '}');
res = safeExecute(parseFunction(variables + ' return' + '`' + template + '`'));
const formatted = template.match(/\$\{([^}]*)\:\d*\}/g);
if (formatted)
formatted.forEach(value => {
const [variable, digits] = value.replace('${', '').replace('}', '').split(':');
data[variable] = padValue(data[variable], +digits);
if (isNaN(data[variable])) data[value] = '';
template = template.replace(value, '${' + variable + '}');
});
const variables = template.match(/\$\{.*?\}/g);
if (variables) {
variables.forEach(variable => {
variable = variable.replace('${', '').replace('}', '');
if (!data[variable])
data[variable] = '';
})
}
else res = template;
const compiled = _.template(template);
res = compiled(data);
}
catch (ex) {
console.log(ex, variables, template)
console.log(ex, template)
}
return res;
}

60
ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts

@ -14,19 +14,12 @@
/// limitations under the License.
///
import L, { LatLngBounds, LatLngTuple } from 'leaflet';
import L, { LatLngTuple, LatLngBounds, Point, MarkerClusterGroupOptions, markerClusterGroup } from 'leaflet';
import 'leaflet-providers';
import LM from 'leaflet.markercluster/dist/leaflet.markercluster';
import {
FormattedData,
MapSettings,
MarkerSettings,
PolygonSettings,
PolylineSettings,
UnitedMapSettings
} from './map-models';
import 'leaflet.markercluster/dist/leaflet.markercluster';
import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings, PolylineSettings } from './map-models';
import { Marker } from './markers';
import { BehaviorSubject, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
@ -48,7 +41,7 @@ export default abstract class LeafletMap {
bounds: L.LatLngBounds;
newMarker: L.Marker;
datasources: FormattedData[];
markersCluster: LM.markerClusterGroup;
markersCluster;
constructor(public $container: HTMLElement, options: UnitedMapSettings) {
this.options = options;
@ -72,7 +65,7 @@ export default abstract class LeafletMap {
setTimeout(options.initCallback, 0);
}
if (useClusterMarkers) {
const clusteringSettings: LM.MarkerClusterGroupOptions = {
const clusteringSettings: MarkerClusterGroupOptions = {
zoomToBoundsOnClick: zoomOnClick,
showCoverageOnHover,
removeOutsideVisibleBounds,
@ -85,7 +78,7 @@ export default abstract class LeafletMap {
if (maxZoom && maxZoom >= 0 && maxZoom < 19) {
clusteringSettings.disableClusteringAtZoom = Math.floor(maxZoom);
}
this.markersCluster = LM.markerClusterGroup(clusteringSettings);
this.markersCluster = markerClusterGroup(clusteringSettings);
this.ready$.subscribe(map => map.addLayer(this.markersCluster));
}
}
@ -213,7 +206,7 @@ export default abstract class LeafletMap {
});
this.map.fitBounds(bounds, { padding: padding || [50, 50], animate: false });
}
this.bounds = this.bounds.extend(bounds);
this.bounds = bounds;
}
}
@ -236,22 +229,20 @@ export default abstract class LeafletMap {
// Markers
updateMarkers(markersData) {
markersData.forEach(data => {
if (this.convertPosition(data)) {
if (data.rotationAngle || data.rotationAngle === 0) {
this.options.icon = L.divIcon({
html: `<div class="arrow" style="transform: translate(-10px, -10px) rotate(${data.rotationAngle}deg);"><div>`
})
}
else {
this.options.icon = null;
}
if (this.markers.get(data.entityName)) {
this.updateMarker(data.entityName, data, markersData, this.options)
}
else {
this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings);
}
markersData.filter(mdata => !!this.convertPosition(mdata)).forEach(data => {
if (data.rotationAngle || data.rotationAngle === 0) {
this.options.icon = L.divIcon({
html: `<div class="arrow" style="transform: translate(-10px, -10px) rotate(${data.rotationAngle}deg);"><div>`
})
}
else {
this.options.icon = null;
}
if (this.markers.get(data.entityName)) {
this.updateMarker(data.entityName, data, markersData, this.options)
}
else {
this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings);
}
});
}
@ -264,7 +255,7 @@ export default abstract class LeafletMap {
private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) {
this.ready$.subscribe(() => {
const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker);
this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), settings.draggableMarker && this.markers.size > 2);
this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), settings.draggableMarker && this.markers.size < 2);
this.markers.set(key, newMarker);
if (this.options.useClusterMarkers) {
this.markersCluster.addLayer(newMarker.leafletMarker);
@ -299,6 +290,9 @@ export default abstract class LeafletMap {
}
}
setImageAlias(alias: Observable<any>) {
}
// Polyline
updatePolylines(polyData: FormattedData[][]) {
@ -328,7 +322,7 @@ export default abstract class LeafletMap {
updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) {
this.ready$.subscribe(() => {
this.polylines.get(key).updatePolyline(settings, data, dataSources);
this.polylines.get(key).updatePolyline(settings, data.map(el => this.convertPosition(el)), dataSources);
});
}

7
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts

@ -23,6 +23,7 @@ export type MapSettings = {
polygonKeyName: any;
draggableMarker: boolean;
initCallback?: () => any;
posFunction: (origXPos, origYPos) => { x, y };
defaultZoomLevel?: number;
disableScrollZooming?: boolean;
minZoomLevel?: number;
@ -31,6 +32,8 @@ export type MapSettings = {
lngKeyName?: string;
xPosKeyName?: string;
yPosKeyName?: string;
imageEntityAlias: string;
imageUrlAttribute: string;
mapProvider: MapProviders;
mapProviderHere: string;
mapUrl?: string;
@ -49,7 +52,9 @@ export type MapSettings = {
animate: boolean,
maxClusterRadius: number,
chunkedLoading: boolean,
removeOutsideVisibleBounds: boolean
removeOutsideVisibleBounds: boolean,
useCustomProvider: boolean,
customProviderTileUrl: string;
}
export enum MapProviders {

170
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts

@ -17,28 +17,28 @@
import { MapProviders, UnitedMapSettings } from './map-models';
import LeafletMap from './leaflet-map';
import {
commonMapSettingsSchema,
googleMapSettingsSchema,
hereMapSettingsSchema,
imageMapSettingsSchema,
mapPolygonSchema,
mapProviderSchema,
markerClusteringSettingsSchema,
markerClusteringSettingsSchemaLeaflet,
openstreetMapSettingsSchema,
routeMapSettingsSchema,
tencentMapSettingsSchema
openstreetMapSettingsSchema,
googleMapSettingsSchema,
imageMapSettingsSchema,
tencentMapSettingsSchema,
commonMapSettingsSchema,
routeMapSettingsSchema,
markerClusteringSettingsSchema,
markerClusteringSettingsSchemaLeaflet,
hereMapSettingsSchema,
mapProviderSchema,
mapPolygonSchema
} from './schemes';
import { MapWidgetInterface, MapWidgetStaticInterface } from './map-widget.interface';
import { GoogleMap, HEREMap, ImageMap, OpenStreetMap, TencentMap } from './providers';
import { parseArray, parseData, parseFunction, parseWithTranslation } from '@core/utils';
import { addCondition, addGroupInfo, addToSchema, initSchema, mergeSchemes } from '@core/schema-utils';
import { forkJoin } from 'rxjs';
import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
import { parseFunction, parseArray, parseData, parseWithTranslation } from '@core/utils';
import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils';
import { of, Subject } from 'rxjs';
import { WidgetContext } from '@app/modules/home/models/widget-component.models';
import { getDefCenterPosition } from './maps-utils';
import { JsonSettingsSchema, WidgetActionDescriptor } from '@shared/models/widget.models';
import { JsonSettingsSchema, WidgetActionDescriptor, DatasourceType, widgetType } from '@shared/models/widget.models';
import { EntityId } from '@shared/models/id/entity-id';
import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
import { AttributeService } from '@core/http/attribute.service';
import { Type } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@ -47,7 +47,7 @@ import { UtilsService } from '@core/services/utils.service';
// @dynamic
export class MapWidgetController implements MapWidgetInterface {
constructor(public mapProvider: MapProviders, private drawRoutes: boolean, public ctx: WidgetContext, $element: HTMLElement) {
constructor(public mapProvider: MapProviders, private drawRoutes: boolean, public ctx: WidgetContext, $element: HTMLElement, isEdit?) {
if (this.map) {
this.map.map.remove();
delete this.map;
@ -58,6 +58,9 @@ export class MapWidgetController implements MapWidgetInterface {
$element = ctx.$container[0];
}
this.settings = this.initSettings(ctx.settings);
if (isEdit) {
this.settings.draggableMarker = true;
}
this.settings.tooltipAction = this.getDescriptors('tooltipAction');
this.settings.markerClick = this.getDescriptors('markerClick');
this.settings.polygonClick = this.getDescriptors('polygonClick');
@ -69,6 +72,7 @@ export class MapWidgetController implements MapWidgetInterface {
}
parseWithTranslation.setTranslate(this.translate);
this.map = new MapClass($element, this.settings);
this.map.setImageAlias(this.subscribeForImageAttribute());
this.map.saveMarkerLocation = this.setMarkerLocation;
}
@ -85,26 +89,28 @@ export class MapWidgetController implements MapWidgetInterface {
public static getProvidersSchema(mapProvider: MapProviders) {
mapProviderSchema.schema.properties.provider.default = mapProvider;
return mergeSchemes([mapProviderSchema,
...Object.values(providerSets)?.map(
(setting: IProvider) => addCondition(setting?.schema, `model.provider === '${setting.name}'`))]);
...Object.keys(providerSets)?.map(
(key: string) => { const setting = providerSets[key]; return addCondition(setting?.schema, `model.provider === '${setting.name}'`) })]);
}
public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema {
const schema = initSchema();
addToSchema(schema, this.getProvidersSchema(mapProvider));
if(mapProvider!=='image-map'){
addGroupInfo(schema, 'Map Provider Settings');
addToSchema(schema, mergeSchemes([commonMapSettingsSchema, addCondition(mapPolygonSchema, 'model.showPolygon === true')]));
addGroupInfo(schema, 'Map Provider Settings');
addToSchema(schema, addCondition(commonMapSettingsSchema, 'model.provider !== "image-map"'));
addGroupInfo(schema, 'Common Map Settings');
addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon']));
addGroupInfo(schema, 'Polygon Settings');
if (drawRoutes) {
addToSchema(schema, routeMapSettingsSchema);
addGroupInfo(schema, 'Route Map Settings');
} else if (mapProvider !== 'image-map') {
} else {
const clusteringSchema = mergeSchemes([markerClusteringSettingsSchema,
addCondition(markerClusteringSettingsSchemaLeaflet, `model.useClusterMarkers === true`)])
addCondition(markerClusteringSettingsSchemaLeaflet,
`model.useClusterMarkers === true && model.provider !== "image-map"`)])
addToSchema(schema, clusteringSchema);
addGroupInfo(schema, 'Markers Clustering Settings');
}}
}
return schema;
}
@ -125,9 +131,11 @@ export class MapWidgetController implements MapWidgetInterface {
};
}
translate = (key: string, defaultTranslation?: string):string => {
return (this.ctx.$injector.get(UtilsService).customTranslation(key, defaultTranslation || key)
|| this.ctx.$injector.get(TranslateService).instant(key));
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 '';
}
getDescriptors(name: string): { [name: string]: ($event: Event) => void } {
@ -143,7 +151,7 @@ export class MapWidgetController implements MapWidgetInterface {
}
private onCustomAction(descriptor: WidgetActionDescriptor, $event: any) {
if ($event & $event.stopPropagation) {
if ($event && $event.stopPropagation) {
$event?.stopPropagation();
}
// safeExecute(parseFunction(descriptor.customFunction, ['$event', 'widgetContext']), [$event, this.ctx])
@ -156,29 +164,48 @@ export class MapWidgetController implements MapWidgetInterface {
setMarkerLocation = (e) => {
const attributeService = this.ctx.$injector.get(AttributeService);
forkJoin(
this.data.filter(data => !!e[data.dataKey.name])
.map(data => {
const entityId: EntityId = {
entityType: data.datasource.entityType,
id: data.datasource.entityId
};
return attributeService.saveEntityAttributes(
entityId,
AttributeScope.SHARED_SCOPE,
[{
key: data.dataKey.name,
value: e[data.dataKey.name]
}]
);
})).subscribe(res => {
});
const entityId: EntityId = {
entityType: e.$datasource.entityType,
id: e.$datasource.entityId
};
const attributes = [];
const timeseries = [];
const latLngProperties = [this.settings.latKeyName, this.settings.lngKeyName, this.settings.xPosKeyName, this.settings.yPosKeyName];
e.$datasource.dataKeys.forEach(key => {
if (latLngProperties.includes(key.name)) {
const value = {
key: key.name,
value: e[key.name]
};
if (key.type === DataKeyType.attribute) {
attributes.push(value)
}
if (key.type === DataKeyType.timeseries) {
timeseries.push(value)
}
}
});
if (timeseries.length) {
attributeService.saveEntityTimeseries(
entityId,
LatestTelemetry.LATEST_TELEMETRY,
timeseries
).subscribe(() => { });
}
if (attributes.length) {
attributeService.saveEntityAttributes(
entityId,
AttributeScope.SERVER_SCOPE,
attributes
).subscribe(() => { });
}
}
initSettings(settings: UnitedMapSettings): UnitedMapSettings {
const functionParams = ['data', 'dsData', 'dsIndex'];
this.provider = settings.provider || this.mapProvider;
if (!settings.mapProviderHere) {
if (this.provider === MapProviders.here && !settings.mapProviderHere) {
if (settings.mapProvider && hereProviders.includes(settings.mapProvider))
settings.mapProviderHere = settings.mapProvider
else settings.mapProviderHere = hereProviders[0];
@ -213,8 +240,7 @@ export class MapWidgetController implements MapWidgetInterface {
if (this.settings.draggableMarker) {
this.map.setDataSources(parseData(this.data));
}
else
this.map.updateMarkers(parseData(this.data));
this.map.updateMarkers(parseData(this.data));
}
resize() {
@ -222,6 +248,48 @@ export class MapWidgetController implements MapWidgetInterface {
this.map.onResize();
}
subscribeForImageAttribute() {
const imageEntityAlias = this.settings.imageEntityAlias;
const imageUrlAttribute = this.settings.imageUrlAttribute;
if (!imageEntityAlias || !imageUrlAttribute) {
return of(false);
}
const entityAliasId = this.ctx.aliasController.getEntityAliasId(imageEntityAlias);
if (!entityAliasId) {
return of(false);
}
const datasources = [
{
type: DatasourceType.entity,
name: imageEntityAlias,
aliasName: imageEntityAlias,
entityAliasId,
dataKeys: [
{
type: DataKeyType.attribute,
name: imageUrlAttribute,
label: imageUrlAttribute,
settings: {},
_hash: Math.random()
}
]
}
];
const result = new Subject();
const imageUrlSubscriptionOptions = {
datasources,
useDashboardTimewindow: false,
type: widgetType.latest,
callbacks: {
onDataUpdated: (subscription) => {
result.next(subscription?.data[0]?.data[0]);
}
}
};
this.ctx.subscriptionApi.createSubscription(imageUrlSubscriptionOptions, true).subscribe(() => { });
return result;
}
onDestroy() {
}
}

6
ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss

@ -16,8 +16,10 @@
.arrow {
height: 30px;
width: 30px;
background-size: cover;
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAtCAMAAAANxBKoAAACAVBMVEUAAAAAqv9AgP8ggP8udP8qgP8ndv8kbf8kgP8eeP8rcf8mc/8jdP8hev8me/8kdv8jcv8pcP8ic/8neP8leP8ofP8mef8pev8nef8oe/8nef8pef8qfv8qgf8qe/8mev8off8oev8oef8oe/8pev8qe/8oe/8off8pfP8oev8pev8qe/8qfP8pev8pe/8qfP8pe/8oev8pe/8qe/8qfP8qfP8qff8qff8qfP8qff8pe/8qff8qfP8qfP8pev8pev8JYf8KYP8UY/8UZf8WY/8WZv8YZP8bZP8fZv8fZ/8gZv8gZ/8hZP8iZ/8jaf8jav8kaP8kav8ka/8kbP8kbf8kb/8lbP8lbf8lbv8lb/8lcP8mb/8mcP8mcf8ncf8ncv8nc/8ndP8ndf8ocv4odP8odf8odv8od/8oeP8peP8pef8pev8qev8qe/8qfP8rbP8rfP8rff8tc/4vcP4xbv4zcf42dP49df4+gP4/fv5Rif5biP1ul/14mvx4nvyMrfuMrvuWsfuctvuguPuww/vD0fnH1vrI1frK2PrL2fnV3vnb4vnc5fnd5fnd5vne5fnj6vnl6/jm7Pno7fXo7fbo7ffo7fjp7fnp7vjq7vjs8fnt8Pjt8fjw8fXw8fbw8fjy8/jz9fn5+fj7+/f++/j/+/X/+/b/+/f/+/j//Pj//vhoUaHMAAAAQHRSTlMAAwQICwwNDg4REhQWFxscHTI1QkRGSktOU1RdYWFscXJzhYWKqre6wMHDw8PHx8rN09TW1uLi7e/x8vLz9fj5hfwW0QAAAnVJREFUSMeV1fdb00AYB/BzoQjurSjuvQcuFLcgWqTVtLVpL0GS2GuDuLeouPeAiLOuwOFf6WU0ucvq4/d52v6Qz/M+18u97wHgyfh5m5vUYlHt2rV8BojOqGUqonNhVgTegfxZE2JXoMAUFgXYsSoKy3YfnoIiUqxm8UwUnSoaT0WVUuPi6lCUz+ft/+pqNYwqsiwrlm8s45XBVpE7BQgFyeZLbB1ihVwmefjpDSjbi7HwziArCdlU/MArjDHfaestBh4dZs9/JBb/4kVbo5FEr/JaWRL4VPvZ39jMVV4q63qiCx7bIfDJtst/LYt/JnLldaNuACbS9JwiQj4Zu/3HlDr5nEyJivMcgPn0Nogwfaq1Z9igQ7pO9AuqNEJzQINTV5ZghjvywLFG6dYMVRptAF3uPvBc8xPsWH0Q44ccpEqjvaBoa0lI7/ts2kHdCsbDJ5jSSC2/SEU8856x+hDGd+JZujQquvp6ieT7Nyel0o/jCSgzTefq0y81I30f7PRr2i1fbWrd+98yXtMGfOt29wTyiebHpu83dZ+m3U969qSBeje5DHeod8Dx5PeYZ7/rPO8y0XLvk+1J8UcJyLzLScw5kTsgz8Xu2vXJV0uaOSfsGcznZZGcwdi1L5qV13HmDILV3tY1z3fbxa8Wv+Se74VEj/H3jmz0TvuVN4Z+x/ZORF8efU64s/CtUT0vCbk0d/DZTSgxPQ/WhswTSYTZrDNPllaaVQo1q/Y4k63mv+YgmFZxxk6gR/LsCngcO+6nR9lCrfcqqQ2/dxqD7rT1IXhxyB24O8BuCr9eq9axU7F7wYjoy3ty3TZVLaCC2rRxru/hPwVgtEdFK5vOAAAAAElFTkSuQmCC") no-repeat center center;
background-size: contain;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAtCAMAAAANxBKoAAACAVBMVEUAAAAAqv9AgP8ggP8udP8qgP8ndv8kbf8kgP8eeP8rcf8mc/8jdP8hev8me/8kdv8jcv8pcP8ic/8neP8leP8ofP8mef8pev8nef8oe/8nef8pef8qfv8qgf8qe/8mev8off8oev8oef8oe/8pev8qe/8oe/8off8pfP8oev8pev8qe/8qfP8pev8pe/8qfP8pe/8oev8pe/8qe/8qfP8qfP8qff8qff8qfP8qff8pe/8qff8qfP8qfP8pev8pev8JYf8KYP8UY/8UZf8WY/8WZv8YZP8bZP8fZv8fZ/8gZv8gZ/8hZP8iZ/8jaf8jav8kaP8kav8ka/8kbP8kbf8kb/8lbP8lbf8lbv8lb/8lcP8mb/8mcP8mcf8ncf8ncv8nc/8ndP8ndf8ocv4odP8odf8odv8od/8oeP8peP8pef8pev8qev8qe/8qfP8rbP8rfP8rff8tc/4vcP4xbv4zcf42dP49df4+gP4/fv5Rif5biP1ul/14mvx4nvyMrfuMrvuWsfuctvuguPuww/vD0fnH1vrI1frK2PrL2fnV3vnb4vnc5fnd5fnd5vne5fnj6vnl6/jm7Pno7fXo7fbo7ffo7fjp7fnp7vjq7vjs8fnt8Pjt8fjw8fXw8fbw8fjy8/jz9fn5+fj7+/f++/j/+/X/+/b/+/f/+/j//Pj//vhoUaHMAAAAQHRSTlMAAwQICwwNDg4REhQWFxscHTI1QkRGSktOU1RdYWFscXJzhYWKqre6wMHDw8PHx8rN09TW1uLi7e/x8vLz9fj5hfwW0QAAAnVJREFUSMeV1fdb00AYB/BzoQjurSjuvQcuFLcgWqTVtLVpL0GS2GuDuLeouPeAiLOuwOFf6WU0ucvq4/d52v6Qz/M+18u97wHgyfh5m5vUYlHt2rV8BojOqGUqonNhVgTegfxZE2JXoMAUFgXYsSoKy3YfnoIiUqxm8UwUnSoaT0WVUuPi6lCUz+ft/+pqNYwqsiwrlm8s45XBVpE7BQgFyeZLbB1ihVwmefjpDSjbi7HwziArCdlU/MArjDHfaestBh4dZs9/JBb/4kVbo5FEr/JaWRL4VPvZ39jMVV4q63qiCx7bIfDJtst/LYt/JnLldaNuACbS9JwiQj4Zu/3HlDr5nEyJivMcgPn0Nogwfaq1Z9igQ7pO9AuqNEJzQINTV5ZghjvywLFG6dYMVRptAF3uPvBc8xPsWH0Q44ccpEqjvaBoa0lI7/ts2kHdCsbDJ5jSSC2/SEU8856x+hDGd+JZujQquvp6ieT7Nyel0o/jCSgzTefq0y81I30f7PRr2i1fbWrd+98yXtMGfOt29wTyiebHpu83dZ+m3U969qSBeje5DHeod8Dx5PeYZ7/rPO8y0XLvk+1J8UcJyLzLScw5kTsgz8Xu2vXJV0uaOSfsGcznZZGcwdi1L5qV13HmDILV3tY1z3fbxa8Wv+Se74VEj/H3jmz0TvuVN4Z+x/ZORF8efU64s/CtUT0vCbk0d/DZTSgxPQ/WhswTSYTZrDNPllaaVQo1q/Y4k63mv+YgmFZxxk6gR/LsCngcO+6nR9lCrfcqqQ2/dxqD7rT1IXhxyB24O8BuCr9eq9axU7F7wYjoy3ty3TZVLaCC2rRxru/hPwVgtEdFK5vOAAAAAElFTkSuQmCC");
background-position: center;
background-repeat: no-repeat;
}
.leaflet-div-icon,

1
ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts

@ -150,7 +150,6 @@ export class Marker {
}
createDefaultMarkerIcon(color, onMarkerIconReady) {
const pinColor = color.substr(1);
const icon = L.icon({
iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + color,
iconSize: [21, 34],

9
ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts

@ -73,11 +73,14 @@ export class Polyline {
getPolyStyle(settings: PolylineSettings): L.PolylineOptions {
return {
color: settings.useColorFunction ?
safeExecute(settings.colorFunction, [this.data, this.dataSources, this.data[0]?.dsIndex]) : settings.color,
safeExecute(settings.colorFunction,
[this.data, this.dataSources, this.dataSources[0]?.dsIndex]) : settings.color,
opacity: settings.useStrokeOpacityFunction ?
safeExecute(settings.strokeOpacityFunction, [this.data, this.dataSources, this.data[0]?.dsIndex]) : settings.strokeOpacity,
safeExecute(settings.strokeOpacityFunction,
[this.data, this.dataSources, this.dataSources[0]?.dsIndex]) : settings.strokeOpacity,
weight: settings.useStrokeWeightFunction ?
safeExecute(settings.strokeWeightFunction, [this.data, this.dataSources, this.data[0]?.dsIndex]) : settings.strokeWeight,
safeExecute(settings.strokeWeightFunction,
[this.data, this.dataSources, this.dataSources[0]?.dsIndex]) : settings.strokeWeight,
}
}

27
ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts

@ -17,7 +17,9 @@
import L, { LatLngLiteral } from 'leaflet';
import LeafletMap from '../leaflet-map';
import { UnitedMapSettings } from '../map-models';
import { aspectCache } from '@app/core/utils';
import { aspectCache, parseFunction } from '@app/core/utils';
import { Observable } from 'rxjs';
import { map, filter, switchMap } from 'rxjs/operators';
const maxZoom = 4;// ?
@ -27,10 +29,13 @@ export class ImageMap extends LeafletMap {
aspect = 0;
width = 0;
height = 0;
imageUrl;
constructor($container: HTMLElement, options: UnitedMapSettings) {
super($container, options);
aspectCache(options.mapUrl).subscribe(aspect => {
options.posFunction = parseFunction(options.posFunction, ['origXPos', 'origYPos']) as ((origXPos, origYPos) => { x, y });
this.imageUrl = options.mapUrl;
aspectCache(this.imageUrl).subscribe(aspect => {
this.aspect = aspect;
this.onResize();
super.setMap(this.map);
@ -38,6 +43,16 @@ export class ImageMap extends LeafletMap {
});
}
setImageAlias(alias: Observable<any>) {
alias.pipe(filter(result => result), map(el => el[1]), switchMap(res => {
this.imageUrl = res;
return aspectCache(res);
})).subscribe(aspect => {
this.aspect = aspect;
this.onResize(true);
});
}
updateBounds(updateImage?, lastCenterPos?) {
const w = this.width;
const h = this.height;
@ -53,8 +68,7 @@ export class ImageMap extends LeafletMap {
if (this.imageOverlay) {
this.imageOverlay.setBounds(bounds);
} else {
this.imageOverlay = L.imageOverlay(this.options.mapUrl, bounds).addTo(this.map);
this.imageOverlay = L.imageOverlay(this.imageUrl, bounds).addTo(this.map);
}
const padding = 200 * maxZoom;
southWest = this.pointToLatLng(-padding, h + padding);
@ -116,6 +130,7 @@ export class ImageMap extends LeafletMap {
}
convertPosition(expression): L.LatLng {
if (isNaN(expression[this.options.xPosKeyName]) || isNaN(expression[this.options.yPosKeyName])) return null;
return this.pointToLatLng(
expression[this.options.xPosKeyName] * this.width,
expression[this.options.yPosKeyName] * this.height);
@ -129,10 +144,10 @@ export class ImageMap extends LeafletMap {
return L.CRS.Simple.latLngToPoint(latLng, maxZoom - 1);
}
/* convertToCustomFormat(position: L.LatLng): object {
convertToCustomFormat(position: L.LatLng): object {
return {
[this.options.xPosKeyName]: (position.lng + 180) / 360,
[this.options.yPosKeyName]: (position.lat + 180) / 360
}
}*/
}
}

6
ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts

@ -22,7 +22,11 @@ export class OpenStreetMap extends LeafletMap {
constructor($container, options: UnitedMapSettings) {
super($container, options);
const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
const tileLayer = (L.tileLayer as any).provider(options.mapProvider || 'OpenStreetMap.Mapnik');
let tileLayer;
if (options.useCustomProvider)
tileLayer = L.tileLayer(options.customProviderTileUrl);
else
tileLayer = (L.tileLayer as any).provider(options.mapProvider || 'OpenStreetMap.Mapnik');
tileLayer.addTo(map);
super.setMap(map);
super.initSettings(options);

14
ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts

@ -336,11 +336,6 @@ export const commonMapSettingsSchema =
title: 'Color function: f(data, dsData, dsIndex)',
type: 'string'
},
showPolygon: {
title: 'Show polygon',
type: 'boolean',
default: false
},
markerImage: {
title: 'Custom marker image',
type: 'string'
@ -439,8 +434,7 @@ export const commonMapSettingsSchema =
type: 'image'
}
]
},
'showPolygon',
}
]
};
@ -450,6 +444,11 @@ export const mapPolygonSchema =
title: 'Map Polygon Configuration',
type: 'object',
properties: {
showPolygon: {
title: 'Show polygon',
type: 'boolean',
default: false
},
polygonKeyName: {
title: 'Polygon key name',
type: 'string',
@ -491,6 +490,7 @@ export const mapPolygonSchema =
required: []
},
form: [
'showPolygon',
'polygonKeyName',
{
key: 'polygonColor',

2
ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html

@ -17,7 +17,7 @@
-->
<div class="trip-animation-widget">
<div class="trip-animation-label-container" *ngIf="settings.showLabel">
{{label }}
{{label}}
</div>
<div class="trip-animation-container" fxLayout="column">
<div class="map" #map></div>

8
ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts

@ -22,8 +22,8 @@ import { interpolateOnPointSegment } from 'leaflet-geometryutil';
import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core';
import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
import { MapProviders } from '../lib/maps/map-models';
import { parseArray, parseWithTranslation, safeExecute } from '@app/core/utils';
import { addGroupInfo, addToSchema, initSchema } from '@app/core/schema-utils';
import { parseArray, parseWithTranslation, safeExecute, parseTemplate } from '@app/core/utils';
import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils';
import { tripAnimationSchema } from '../lib/maps/schemes';
import { DomSanitizer } from '@angular/platform-browser';
import { WidgetContext } from '@app/modules/home/models/widget-component.models';
@ -100,8 +100,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
timeUpdated(time: number) {
const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]);
this.activeTrip = currentPosition[0];
this.minTime = moment(this.historicalData[0][this.historicalData.length - 1]?.time).format('YYYY-MM-DD HH:mm:ss')
this.maxTime = moment(this.historicalData[0][0]?.time).format('YYYY-MM-DD HH:mm:ss')
this.minTime = moment(this.intervals[this.intervals.length - 1]).format('YYYY-MM-DD HH:mm:ss')
this.maxTime = moment(this.intervals[0]).format('YYYY-MM-DD HH:mm:ss')
this.calcLabel();
this.calcTooltip();
if (this.mapWidget) {

44
ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss

@ -51,6 +51,16 @@
padding: 0 0 2px;
margin: 2px;
line-height: 24px;
mat-icon {
width: 24px;
height: 24px;
svg {
width: inherit;
height: inherit;
}
}
}
}
@ -80,24 +90,24 @@
mat-slider {
min-width: 80px;
}
}
button.mat-button.mat-icon-button {
width: 44px;
min-width: 44px;
height: 44px;
min-height: 44px;
margin: 0;
line-height: 28px;
mat-icon {
width: 28px;
height: 28px;
font-size: 28px;
svg {
width: inherit;
height: inherit;
}
.mat-icon-button {
width: 44px;
min-width: 44px;
height: 48px;
min-height: 48px;
margin: 0;
line-height: 28px;
mat-icon {
width: 24px;
height: 24px;
font-size: 24px;
svg {
width: inherit;
height: inherit;
}
}

2
ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core';
import { interval } from 'rxjs';
import { filter } from 'rxjs/operators';
import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models';

2
ui-ngx/src/app/shared/pipe/template.pipe.ts

@ -15,7 +15,7 @@
///
import { Pipe, PipeTransform } from '@angular/core';
import { parseWithTranslation } from '@app/core/utils';
import { parseTemplate, parseWithTranslation } from '@app/core/utils';
@Pipe({ name: 'tbParseTemplate' })
export class TbTemplatePipe implements PipeTransform {

3
ui-ngx/src/tsconfig.app.json

@ -2,7 +2,8 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": ["node", "jquery", "flot", "tooltipster", "tinycolor2", "js-beautify", "react", "react-dom", "jstree", "raphael", "canvas-gauges"]
"types": ["node", "jquery", "flot", "tooltipster", "tinycolor2", "js-beautify",
"react", "react-dom", "jstree", "raphael", "canvas-gauges", "leaflet", "leaflet-markercluster"]
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true

3
ui-ngx/tslint.json

@ -11,7 +11,8 @@
},
"import-blacklist": [
true,
"rxjs/Rx"
"rxjs/Rx",
"^.*/public-api$"
],
"interface-name": false,
"max-classes-per-file": false,

Loading…
Cancel
Save