Browse Source

Merge branch 'ArtemHalushko-map/3.0' into develop/3.0

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

4
ui-ngx/package.json

@ -61,7 +61,7 @@
"leaflet-geometryutil": "^0.9.3", "leaflet-geometryutil": "^0.9.3",
"leaflet-polylinedecorator": "^1.6.0", "leaflet-polylinedecorator": "^1.6.0",
"leaflet-providers": "^1.9.1", "leaflet-providers": "^1.9.1",
"leaflet.gridlayer.googlemutant": "^0.8.0", "leaflet.gridlayer.googlemutant": "0.8.0",
"leaflet.markercluster": "^1.4.1", "leaflet.markercluster": "^1.4.1",
"material-design-icons": "^3.0.1", "material-design-icons": "^3.0.1",
"messageformat": "^2.3.0", "messageformat": "^2.3.0",
@ -108,8 +108,8 @@
"@types/jstree": "^3.3.39", "@types/jstree": "^3.3.39",
"@types/jszip": "^3.1.7", "@types/jszip": "^3.1.7",
"@types/leaflet": "^1.5.12", "@types/leaflet": "^1.5.12",
"@types/leaflet.markercluster": "^1.4.2",
"@types/leaflet-polylinedecorator": "^1.6.0", "@types/leaflet-polylinedecorator": "^1.6.0",
"@types/leaflet-markercluster": "^1.0.3",
"@types/lodash": "^4.14.150", "@types/lodash": "^4.14.150",
"@types/raphael": "^2.3.0", "@types/raphael": "^2.3.0",
"@types/react": "^16.9.34", "@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()); }, 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 => { schema.form = schema.form.map(element => {
if (typeof element === 'string') { if (!exclude.includes(element) && !exclude.includes(element.key)) {
return { if (typeof element === 'string') {
key: element, return {
condition key: element,
condition
}
} }
} if (typeof element === 'object') {
if (typeof element === 'object') { if (element.condition) {
if (element.condition) { element.condition += ' && ' + condition
element.condition += ' && ' + condition }
else element.condition = condition;
} }
else element.condition = condition;
} }
return element; return element;
}); });

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

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

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

@ -14,19 +14,12 @@
/// limitations under the License. /// limitations under the License.
/// ///
import L, { LatLngBounds, LatLngTuple } from 'leaflet'; import L, { LatLngTuple, LatLngBounds, Point, MarkerClusterGroupOptions, markerClusterGroup } from 'leaflet';
import 'leaflet-providers'; import 'leaflet-providers';
import LM from 'leaflet.markercluster/dist/leaflet.markercluster'; import 'leaflet.markercluster/dist/leaflet.markercluster';
import { import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings, PolylineSettings } from './map-models';
FormattedData,
MapSettings,
MarkerSettings,
PolygonSettings,
PolylineSettings,
UnitedMapSettings
} from './map-models';
import { Marker } from './markers'; import { Marker } from './markers';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { filter } from 'rxjs/operators'; import { filter } from 'rxjs/operators';
@ -48,7 +41,7 @@ export default abstract class LeafletMap {
bounds: L.LatLngBounds; bounds: L.LatLngBounds;
newMarker: L.Marker; newMarker: L.Marker;
datasources: FormattedData[]; datasources: FormattedData[];
markersCluster: LM.markerClusterGroup; markersCluster;
constructor(public $container: HTMLElement, options: UnitedMapSettings) { constructor(public $container: HTMLElement, options: UnitedMapSettings) {
this.options = options; this.options = options;
@ -72,7 +65,7 @@ export default abstract class LeafletMap {
setTimeout(options.initCallback, 0); setTimeout(options.initCallback, 0);
} }
if (useClusterMarkers) { if (useClusterMarkers) {
const clusteringSettings: LM.MarkerClusterGroupOptions = { const clusteringSettings: MarkerClusterGroupOptions = {
zoomToBoundsOnClick: zoomOnClick, zoomToBoundsOnClick: zoomOnClick,
showCoverageOnHover, showCoverageOnHover,
removeOutsideVisibleBounds, removeOutsideVisibleBounds,
@ -85,7 +78,7 @@ export default abstract class LeafletMap {
if (maxZoom && maxZoom >= 0 && maxZoom < 19) { if (maxZoom && maxZoom >= 0 && maxZoom < 19) {
clusteringSettings.disableClusteringAtZoom = Math.floor(maxZoom); clusteringSettings.disableClusteringAtZoom = Math.floor(maxZoom);
} }
this.markersCluster = LM.markerClusterGroup(clusteringSettings); this.markersCluster = markerClusterGroup(clusteringSettings);
this.ready$.subscribe(map => map.addLayer(this.markersCluster)); 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.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 // Markers
updateMarkers(markersData) { updateMarkers(markersData) {
markersData.forEach(data => { markersData.filter(mdata => !!this.convertPosition(mdata)).forEach(data => {
if (this.convertPosition(data)) { if (data.rotationAngle || data.rotationAngle === 0) {
if (data.rotationAngle || data.rotationAngle === 0) { this.options.icon = L.divIcon({
this.options.icon = L.divIcon({ html: `<div class="arrow" style="transform: translate(-10px, -10px) rotate(${data.rotationAngle}deg);"><div>`
html: `<div class="arrow" style="transform: translate(-10px, -10px) rotate(${data.rotationAngle}deg);"><div>` })
}) }
} else {
else { this.options.icon = null;
this.options.icon = null; }
} if (this.markers.get(data.entityName)) {
if (this.markers.get(data.entityName)) { this.updateMarker(data.entityName, data, markersData, this.options)
this.updateMarker(data.entityName, data, markersData, this.options) }
} else {
else { this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings);
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) { private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) {
this.ready$.subscribe(() => { this.ready$.subscribe(() => {
const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker); 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); this.markers.set(key, newMarker);
if (this.options.useClusterMarkers) { if (this.options.useClusterMarkers) {
this.markersCluster.addLayer(newMarker.leafletMarker); this.markersCluster.addLayer(newMarker.leafletMarker);
@ -299,6 +290,9 @@ export default abstract class LeafletMap {
} }
} }
setImageAlias(alias: Observable<any>) {
}
// Polyline // Polyline
updatePolylines(polyData: FormattedData[][]) { updatePolylines(polyData: FormattedData[][]) {
@ -328,7 +322,7 @@ export default abstract class LeafletMap {
updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) { updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) {
this.ready$.subscribe(() => { 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; polygonKeyName: any;
draggableMarker: boolean; draggableMarker: boolean;
initCallback?: () => any; initCallback?: () => any;
posFunction: (origXPos, origYPos) => { x, y };
defaultZoomLevel?: number; defaultZoomLevel?: number;
disableScrollZooming?: boolean; disableScrollZooming?: boolean;
minZoomLevel?: number; minZoomLevel?: number;
@ -31,6 +32,8 @@ export type MapSettings = {
lngKeyName?: string; lngKeyName?: string;
xPosKeyName?: string; xPosKeyName?: string;
yPosKeyName?: string; yPosKeyName?: string;
imageEntityAlias: string;
imageUrlAttribute: string;
mapProvider: MapProviders; mapProvider: MapProviders;
mapProviderHere: string; mapProviderHere: string;
mapUrl?: string; mapUrl?: string;
@ -49,7 +52,9 @@ export type MapSettings = {
animate: boolean, animate: boolean,
maxClusterRadius: number, maxClusterRadius: number,
chunkedLoading: boolean, chunkedLoading: boolean,
removeOutsideVisibleBounds: boolean removeOutsideVisibleBounds: boolean,
useCustomProvider: boolean,
customProviderTileUrl: string;
} }
export enum MapProviders { 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 { MapProviders, UnitedMapSettings } from './map-models';
import LeafletMap from './leaflet-map'; import LeafletMap from './leaflet-map';
import { import {
commonMapSettingsSchema, openstreetMapSettingsSchema,
googleMapSettingsSchema, googleMapSettingsSchema,
hereMapSettingsSchema, imageMapSettingsSchema,
imageMapSettingsSchema, tencentMapSettingsSchema,
mapPolygonSchema, commonMapSettingsSchema,
mapProviderSchema, routeMapSettingsSchema,
markerClusteringSettingsSchema, markerClusteringSettingsSchema,
markerClusteringSettingsSchemaLeaflet, markerClusteringSettingsSchemaLeaflet,
openstreetMapSettingsSchema, hereMapSettingsSchema,
routeMapSettingsSchema, mapProviderSchema,
tencentMapSettingsSchema mapPolygonSchema
} from './schemes'; } from './schemes';
import { MapWidgetInterface, MapWidgetStaticInterface } from './map-widget.interface'; import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
import { GoogleMap, HEREMap, ImageMap, OpenStreetMap, TencentMap } from './providers'; import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
import { parseArray, parseData, parseFunction, parseWithTranslation } from '@core/utils'; import { parseFunction, parseArray, parseData, parseWithTranslation } from '@core/utils';
import { addCondition, addGroupInfo, addToSchema, initSchema, mergeSchemes } from '@core/schema-utils'; import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils';
import { forkJoin } from 'rxjs'; import { of, Subject } from 'rxjs';
import { WidgetContext } from '@app/modules/home/models/widget-component.models'; import { WidgetContext } from '@app/modules/home/models/widget-component.models';
import { getDefCenterPosition } from './maps-utils'; 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 { 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 { AttributeService } from '@core/http/attribute.service';
import { Type } from '@angular/core'; import { Type } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -47,7 +47,7 @@ import { UtilsService } from '@core/services/utils.service';
// @dynamic // @dynamic
export class MapWidgetController implements MapWidgetInterface { 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) { if (this.map) {
this.map.map.remove(); this.map.map.remove();
delete this.map; delete this.map;
@ -58,6 +58,9 @@ export class MapWidgetController implements MapWidgetInterface {
$element = ctx.$container[0]; $element = ctx.$container[0];
} }
this.settings = this.initSettings(ctx.settings); this.settings = this.initSettings(ctx.settings);
if (isEdit) {
this.settings.draggableMarker = true;
}
this.settings.tooltipAction = this.getDescriptors('tooltipAction'); this.settings.tooltipAction = this.getDescriptors('tooltipAction');
this.settings.markerClick = this.getDescriptors('markerClick'); this.settings.markerClick = this.getDescriptors('markerClick');
this.settings.polygonClick = this.getDescriptors('polygonClick'); this.settings.polygonClick = this.getDescriptors('polygonClick');
@ -69,6 +72,7 @@ export class MapWidgetController implements MapWidgetInterface {
} }
parseWithTranslation.setTranslate(this.translate); parseWithTranslation.setTranslate(this.translate);
this.map = new MapClass($element, this.settings); this.map = new MapClass($element, this.settings);
this.map.setImageAlias(this.subscribeForImageAttribute());
this.map.saveMarkerLocation = this.setMarkerLocation; this.map.saveMarkerLocation = this.setMarkerLocation;
} }
@ -85,26 +89,28 @@ export class MapWidgetController implements MapWidgetInterface {
public static getProvidersSchema(mapProvider: MapProviders) { public static getProvidersSchema(mapProvider: MapProviders) {
mapProviderSchema.schema.properties.provider.default = mapProvider; mapProviderSchema.schema.properties.provider.default = mapProvider;
return mergeSchemes([mapProviderSchema, return mergeSchemes([mapProviderSchema,
...Object.values(providerSets)?.map( ...Object.keys(providerSets)?.map(
(setting: IProvider) => addCondition(setting?.schema, `model.provider === '${setting.name}'`))]); (key: string) => { const setting = providerSets[key]; return addCondition(setting?.schema, `model.provider === '${setting.name}'`) })]);
} }
public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema { public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema {
const schema = initSchema(); const schema = initSchema();
addToSchema(schema, this.getProvidersSchema(mapProvider)); addToSchema(schema, this.getProvidersSchema(mapProvider));
if(mapProvider!=='image-map'){ addGroupInfo(schema, 'Map Provider Settings');
addGroupInfo(schema, 'Map Provider Settings'); addToSchema(schema, addCondition(commonMapSettingsSchema, 'model.provider !== "image-map"'));
addToSchema(schema, mergeSchemes([commonMapSettingsSchema, addCondition(mapPolygonSchema, 'model.showPolygon === true')]));
addGroupInfo(schema, 'Common Map Settings'); addGroupInfo(schema, 'Common Map Settings');
addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon']));
addGroupInfo(schema, 'Polygon Settings');
if (drawRoutes) { if (drawRoutes) {
addToSchema(schema, routeMapSettingsSchema); addToSchema(schema, routeMapSettingsSchema);
addGroupInfo(schema, 'Route Map Settings'); addGroupInfo(schema, 'Route Map Settings');
} else if (mapProvider !== 'image-map') { } else {
const clusteringSchema = mergeSchemes([markerClusteringSettingsSchema, const clusteringSchema = mergeSchemes([markerClusteringSettingsSchema,
addCondition(markerClusteringSettingsSchemaLeaflet, `model.useClusterMarkers === true`)]) addCondition(markerClusteringSettingsSchemaLeaflet,
`model.useClusterMarkers === true && model.provider !== "image-map"`)])
addToSchema(schema, clusteringSchema); addToSchema(schema, clusteringSchema);
addGroupInfo(schema, 'Markers Clustering Settings'); addGroupInfo(schema, 'Markers Clustering Settings');
}} }
return schema; return schema;
} }
@ -125,9 +131,11 @@ export class MapWidgetController implements MapWidgetInterface {
}; };
} }
translate = (key: string, defaultTranslation?: string):string => { translate = (key: string, defaultTranslation?: string): string => {
return (this.ctx.$injector.get(UtilsService).customTranslation(key, defaultTranslation || key) if (key)
|| this.ctx.$injector.get(TranslateService).instant(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 } { getDescriptors(name: string): { [name: string]: ($event: Event) => void } {
@ -143,7 +151,7 @@ export class MapWidgetController implements MapWidgetInterface {
} }
private onCustomAction(descriptor: WidgetActionDescriptor, $event: any) { private onCustomAction(descriptor: WidgetActionDescriptor, $event: any) {
if ($event & $event.stopPropagation) { if ($event && $event.stopPropagation) {
$event?.stopPropagation(); $event?.stopPropagation();
} }
// safeExecute(parseFunction(descriptor.customFunction, ['$event', 'widgetContext']), [$event, this.ctx]) // safeExecute(parseFunction(descriptor.customFunction, ['$event', 'widgetContext']), [$event, this.ctx])
@ -156,29 +164,48 @@ export class MapWidgetController implements MapWidgetInterface {
setMarkerLocation = (e) => { setMarkerLocation = (e) => {
const attributeService = this.ctx.$injector.get(AttributeService); const attributeService = this.ctx.$injector.get(AttributeService);
forkJoin(
this.data.filter(data => !!e[data.dataKey.name]) const entityId: EntityId = {
.map(data => { entityType: e.$datasource.entityType,
const entityId: EntityId = { id: e.$datasource.entityId
entityType: data.datasource.entityType, };
id: data.datasource.entityId const attributes = [];
}; const timeseries = [];
return attributeService.saveEntityAttributes( const latLngProperties = [this.settings.latKeyName, this.settings.lngKeyName, this.settings.xPosKeyName, this.settings.yPosKeyName];
entityId, e.$datasource.dataKeys.forEach(key => {
AttributeScope.SHARED_SCOPE, if (latLngProperties.includes(key.name)) {
[{ const value = {
key: data.dataKey.name, key: key.name,
value: e[data.dataKey.name] value: e[key.name]
}] };
); if (key.type === DataKeyType.attribute) {
})).subscribe(res => { 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 { initSettings(settings: UnitedMapSettings): UnitedMapSettings {
const functionParams = ['data', 'dsData', 'dsIndex']; const functionParams = ['data', 'dsData', 'dsIndex'];
this.provider = settings.provider || this.mapProvider; this.provider = settings.provider || this.mapProvider;
if (!settings.mapProviderHere) { if (this.provider === MapProviders.here && !settings.mapProviderHere) {
if (settings.mapProvider && hereProviders.includes(settings.mapProvider)) if (settings.mapProvider && hereProviders.includes(settings.mapProvider))
settings.mapProviderHere = settings.mapProvider settings.mapProviderHere = settings.mapProvider
else settings.mapProviderHere = hereProviders[0]; else settings.mapProviderHere = hereProviders[0];
@ -213,8 +240,7 @@ export class MapWidgetController implements MapWidgetInterface {
if (this.settings.draggableMarker) { if (this.settings.draggableMarker) {
this.map.setDataSources(parseData(this.data)); this.map.setDataSources(parseData(this.data));
} }
else this.map.updateMarkers(parseData(this.data));
this.map.updateMarkers(parseData(this.data));
} }
resize() { resize() {
@ -222,6 +248,48 @@ export class MapWidgetController implements MapWidgetInterface {
this.map.onResize(); 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() { onDestroy() {
} }
} }

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

@ -16,8 +16,10 @@
.arrow { .arrow {
height: 30px; height: 30px;
width: 30px; width: 30px;
background-size: cover; background-size: contain;
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-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, .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) { createDefaultMarkerIcon(color, onMarkerIconReady) {
const pinColor = color.substr(1);
const icon = L.icon({ const icon = L.icon({
iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + color, iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + color,
iconSize: [21, 34], 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 { getPolyStyle(settings: PolylineSettings): L.PolylineOptions {
return { return {
color: settings.useColorFunction ? 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 ? 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 ? 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 L, { LatLngLiteral } from 'leaflet';
import LeafletMap from '../leaflet-map'; import LeafletMap from '../leaflet-map';
import { UnitedMapSettings } from '../map-models'; 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;// ? const maxZoom = 4;// ?
@ -27,10 +29,13 @@ export class ImageMap extends LeafletMap {
aspect = 0; aspect = 0;
width = 0; width = 0;
height = 0; height = 0;
imageUrl;
constructor($container: HTMLElement, options: UnitedMapSettings) { constructor($container: HTMLElement, options: UnitedMapSettings) {
super($container, options); 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.aspect = aspect;
this.onResize(); this.onResize();
super.setMap(this.map); 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?) { updateBounds(updateImage?, lastCenterPos?) {
const w = this.width; const w = this.width;
const h = this.height; const h = this.height;
@ -53,8 +68,7 @@ export class ImageMap extends LeafletMap {
if (this.imageOverlay) { if (this.imageOverlay) {
this.imageOverlay.setBounds(bounds); this.imageOverlay.setBounds(bounds);
} else { } 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; const padding = 200 * maxZoom;
southWest = this.pointToLatLng(-padding, h + padding); southWest = this.pointToLatLng(-padding, h + padding);
@ -116,6 +130,7 @@ export class ImageMap extends LeafletMap {
} }
convertPosition(expression): L.LatLng { convertPosition(expression): L.LatLng {
if (isNaN(expression[this.options.xPosKeyName]) || isNaN(expression[this.options.yPosKeyName])) return null;
return this.pointToLatLng( return this.pointToLatLng(
expression[this.options.xPosKeyName] * this.width, expression[this.options.xPosKeyName] * this.width,
expression[this.options.yPosKeyName] * this.height); expression[this.options.yPosKeyName] * this.height);
@ -129,10 +144,10 @@ export class ImageMap extends LeafletMap {
return L.CRS.Simple.latLngToPoint(latLng, maxZoom - 1); return L.CRS.Simple.latLngToPoint(latLng, maxZoom - 1);
} }
/* convertToCustomFormat(position: L.LatLng): object { convertToCustomFormat(position: L.LatLng): object {
return { return {
[this.options.xPosKeyName]: (position.lng + 180) / 360, [this.options.xPosKeyName]: (position.lng + 180) / 360,
[this.options.yPosKeyName]: (position.lat + 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) { constructor($container, options: UnitedMapSettings) {
super($container, options); super($container, options);
const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); 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); tileLayer.addTo(map);
super.setMap(map); super.setMap(map);
super.initSettings(options); 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)', title: 'Color function: f(data, dsData, dsIndex)',
type: 'string' type: 'string'
}, },
showPolygon: {
title: 'Show polygon',
type: 'boolean',
default: false
},
markerImage: { markerImage: {
title: 'Custom marker image', title: 'Custom marker image',
type: 'string' type: 'string'
@ -439,8 +434,7 @@ export const commonMapSettingsSchema =
type: 'image' type: 'image'
} }
] ]
}, }
'showPolygon',
] ]
}; };
@ -450,6 +444,11 @@ export const mapPolygonSchema =
title: 'Map Polygon Configuration', title: 'Map Polygon Configuration',
type: 'object', type: 'object',
properties: { properties: {
showPolygon: {
title: 'Show polygon',
type: 'boolean',
default: false
},
polygonKeyName: { polygonKeyName: {
title: 'Polygon key name', title: 'Polygon key name',
type: 'string', type: 'string',
@ -491,6 +490,7 @@ export const mapPolygonSchema =
required: [] required: []
}, },
form: [ form: [
'showPolygon',
'polygonKeyName', 'polygonKeyName',
{ {
key: 'polygonColor', 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-widget">
<div class="trip-animation-label-container" *ngIf="settings.showLabel"> <div class="trip-animation-label-container" *ngIf="settings.showLabel">
{{label }} {{label}}
</div> </div>
<div class="trip-animation-container" fxLayout="column"> <div class="trip-animation-container" fxLayout="column">
<div class="map" #map></div> <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 { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core';
import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2'; import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
import { MapProviders } from '../lib/maps/map-models'; import { MapProviders } from '../lib/maps/map-models';
import { parseArray, parseWithTranslation, safeExecute } from '@app/core/utils'; import { parseArray, parseWithTranslation, safeExecute, parseTemplate } from '@app/core/utils';
import { addGroupInfo, addToSchema, initSchema } from '@app/core/schema-utils'; import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils';
import { tripAnimationSchema } from '../lib/maps/schemes'; import { tripAnimationSchema } from '../lib/maps/schemes';
import { DomSanitizer } from '@angular/platform-browser'; import { DomSanitizer } from '@angular/platform-browser';
import { WidgetContext } from '@app/modules/home/models/widget-component.models'; import { WidgetContext } from '@app/modules/home/models/widget-component.models';
@ -100,8 +100,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
timeUpdated(time: number) { timeUpdated(time: number) {
const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]); const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]);
this.activeTrip = currentPosition[0]; this.activeTrip = currentPosition[0];
this.minTime = moment(this.historicalData[0][this.historicalData.length - 1]?.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.historicalData[0][0]?.time).format('YYYY-MM-DD HH:mm:ss') this.maxTime = moment(this.intervals[0]).format('YYYY-MM-DD HH:mm:ss')
this.calcLabel(); this.calcLabel();
this.calcTooltip(); this.calcTooltip();
if (this.mapWidget) { 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; padding: 0 0 2px;
margin: 2px; margin: 2px;
line-height: 24px; line-height: 24px;
mat-icon {
width: 24px;
height: 24px;
svg {
width: inherit;
height: inherit;
}
}
} }
} }
@ -80,24 +90,24 @@
mat-slider { mat-slider {
min-width: 80px; min-width: 80px;
} }
}
button.mat-button.mat-icon-button { .mat-icon-button {
width: 44px; width: 44px;
min-width: 44px; min-width: 44px;
height: 44px; height: 48px;
min-height: 44px; min-height: 48px;
margin: 0; margin: 0;
line-height: 28px; line-height: 28px;
mat-icon { mat-icon {
width: 28px; width: 24px;
height: 28px; height: 24px;
font-size: 28px; font-size: 24px;
svg { svg {
width: inherit; width: inherit;
height: 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. /// 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 { interval } from 'rxjs';
import { filter } from 'rxjs/operators'; import { filter } from 'rxjs/operators';
import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models'; 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 { Pipe, PipeTransform } from '@angular/core';
import { parseWithTranslation } from '@app/core/utils'; import { parseTemplate, parseWithTranslation } from '@app/core/utils';
@Pipe({ name: 'tbParseTemplate' }) @Pipe({ name: 'tbParseTemplate' })
export class TbTemplatePipe implements PipeTransform { export class TbTemplatePipe implements PipeTransform {

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

@ -2,7 +2,8 @@
"extends": "../tsconfig.json", "extends": "../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "../out-tsc/app", "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": { "angularCompilerOptions": {
"fullTemplateTypeCheck": true "fullTemplateTypeCheck": true

3
ui-ngx/tslint.json

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

Loading…
Cancel
Save