Commit f6ca099613fbfe013bb1ab66a3e5c436bf497e24

Authored by Artem Halushko
1 parent 8149d9d4

data update & polyline support

@@ -1897,6 +1897,11 @@ @@ -1897,6 +1897,11 @@
1897 "@types/geojson": "7946.0.7" 1897 "@types/geojson": "7946.0.7"
1898 } 1898 }
1899 }, 1899 },
  1900 + "@types/lodash": {
  1901 + "version": "4.14.149",
  1902 + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
  1903 + "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ=="
  1904 + },
1900 "@types/minimatch": { 1905 "@types/minimatch": {
1901 "version": "3.0.3", 1906 "version": "3.0.3",
1902 "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", 1907 "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -38,6 +38,7 @@ @@ -38,6 +38,7 @@
38 "@ngx-share/core": "^7.1.4", 38 "@ngx-share/core": "^7.1.4",
39 "@ngx-translate/core": "^12.1.1", 39 "@ngx-translate/core": "^12.1.1",
40 "@ngx-translate/http-loader": "^4.0.0", 40 "@ngx-translate/http-loader": "^4.0.0",
  41 + "@types/lodash": "^4.14.149",
41 "ace-builds": "^1.4.8", 42 "ace-builds": "^1.4.8",
42 "angular-gridster2": "^9.0.1", 43 "angular-gridster2": "^9.0.1",
43 "angular2-hotkeys": "^2.1.5", 44 "angular2-hotkeys": "^2.1.5",
@@ -101,7 +101,7 @@ export function isNumber(value: any): boolean { @@ -101,7 +101,7 @@ export function isNumber(value: any): boolean {
101 } 101 }
102 102
103 export function isNumeric(value: any): boolean { 103 export function isNumeric(value: any): boolean {
104 - return (value - parseFloat( value ) + 1) >= 0; 104 + return (value - parseFloat(value) + 1) >= 0;
105 } 105 }
106 106
107 export function isString(value: any): boolean { 107 export function isString(value: any): boolean {
@@ -226,9 +226,9 @@ function hashCode(str) { @@ -226,9 +226,9 @@ function hashCode(str) {
226 var i, char; 226 var i, char;
227 if (str.length == 0) return hash; 227 if (str.length == 0) return hash;
228 for (i = 0; i < str.length; i++) { 228 for (i = 0; i < str.length; i++) {
229 - char = str.charCodeAt(i);  
230 - hash = ((hash << 5) - hash) + char;  
231 - hash = hash & hash; // Convert to 32bit integer 229 + char = str.charCodeAt(i);
  230 + hash = ((hash << 5) - hash) + char;
  231 + hash = hash & hash; // Convert to 32bit integer
232 } 232 }
233 return hash; 233 return hash;
234 } 234 }
@@ -424,24 +424,24 @@ export function getDescendantProp(obj: any, path: string): any { @@ -424,24 +424,24 @@ export function getDescendantProp(obj: any, path: string): any {
424 return path.split('.').reduce((acc, part) => acc && acc[part], obj); 424 return path.split('.').reduce((acc, part) => acc && acc[part], obj);
425 } 425 }
426 426
427 -export function imageLoader(imageUrl: string): Observable<HTMLImageElement>{ 427 +export function imageLoader(imageUrl: string): Observable<HTMLImageElement> {
428 const image = new Image(); 428 const image = new Image();
429 - const imageLoad$ = fromEvent(image, 'load').pipe(map(event=>image)); 429 + const imageLoad$ = fromEvent(image, 'load').pipe(map(event => image));
430 image.src = imageUrl; 430 image.src = imageUrl;
431 return imageLoad$; 431 return imageLoad$;
432 } 432 }
433 433
434 const imageAspectMap = {}; 434 const imageAspectMap = {};
435 435
436 -export function aspectCache(imageUrl: string): Observable<number>{  
437 - if(imageUrl?.length){ 436 +export function aspectCache(imageUrl: string): Observable<number> {
  437 + if (imageUrl?.length) {
438 const hash = hashCode(imageUrl); 438 const hash = hashCode(imageUrl);
439 let aspect = imageAspectMap[hash]; 439 let aspect = imageAspectMap[hash];
440 - if(aspect){ 440 + if (aspect) {
441 return of(aspect); 441 return of(aspect);
442 } 442 }
443 - else return imageLoader(imageUrl).pipe(map(image=>{  
444 - aspect = image.width/image.height; 443 + else return imageLoader(imageUrl).pipe(map(image => {
  444 + aspect = image.width / image.height;
445 imageAspectMap[hash] = aspect; 445 imageAspectMap[hash] = aspect;
446 return aspect; 446 return aspect;
447 })) 447 }))
@@ -9,11 +9,13 @@ import { MapOptions, MarkerSettings } from './map-models'; @@ -9,11 +9,13 @@ import { MapOptions, MarkerSettings } from './map-models';
9 import { Marker } from './markers'; 9 import { Marker } from './markers';
10 import { Observable, of, BehaviorSubject, Subject } from 'rxjs'; 10 import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
11 import { filter } from 'rxjs/operators'; 11 import { filter } from 'rxjs/operators';
  12 +import { Polyline } from './polyline';
12 13
13 -export default class LeafletMap { 14 +export default abstract class LeafletMap {
14 15
15 - markers = []; 16 + markers: Map<string, Marker> = new Map();
16 tooltips = []; 17 tooltips = [];
  18 + poly: Polyline;
17 map: L.Map; 19 map: L.Map;
18 map$: BehaviorSubject<L.Map> = new BehaviorSubject(null); 20 map$: BehaviorSubject<L.Map> = new BehaviorSubject(null);
19 ready$: Observable<L.Map> = this.map$.pipe(filter(map => !!map)); 21 ready$: Observable<L.Map> = this.map$.pipe(filter(map => !!map));
@@ -22,6 +24,7 @@ export default class LeafletMap { @@ -22,6 +24,7 @@ export default class LeafletMap {
22 24
23 25
24 constructor($container: HTMLElement, options: MapOptions) { 26 constructor($container: HTMLElement, options: MapOptions) {
  27 + console.log("LeafletMap -> constructor -> options", options)
25 this.options = options; 28 this.options = options;
26 } 29 }
27 30
@@ -123,29 +126,69 @@ export default class LeafletMap { @@ -123,29 +126,69 @@ export default class LeafletMap {
123 return this.map.getCenter(); 126 return this.map.getCenter();
124 } 127 }
125 128
126 - convertPosition(expression: L.LatLngExpression | { x, y }): L.LatLngExpression {  
127 - return expression as L.LatLngExpression; 129 + convertPosition(expression: any): L.LatLng {
  130 + return L.latLng(expression[this.options.latKeyName], expression[this.options.lngKeyName]) as L.LatLng;
128 } 131 }
129 132
130 ////Markers 133 ////Markers
  134 + updateMarkers(markersData) {
  135 + markersData.forEach(data => {
  136 + if (this.markers.get(data.aliasName)) {
  137 + this.updateMarker(data.aliasName, this.convertPosition(data), this.options as MarkerSettings)
  138 + }
  139 + else {
  140 + this.createMarker(data.aliasName, this.convertPosition(data), this.options as MarkerSettings);
  141 + }
  142 + });
  143 + }
131 144
132 -  
133 - createMarker(location, settings: MarkerSettings) { 145 + private createMarker(key, location, settings: MarkerSettings) {
134 this.ready$.subscribe(() => { 146 this.ready$.subscribe(() => {
135 let defaultSettings: MarkerSettings = { 147 let defaultSettings: MarkerSettings = {
136 color: '#FD2785' 148 color: '#FD2785'
137 } 149 }
138 - this.markers.push(new Marker(this.map, this.convertPosition(location), { ...defaultSettings, ...settings })) 150 + this.markers.set(key, new Marker(this.map, location, { ...defaultSettings, ...settings }))
139 }); 151 });
140 } 152 }
141 153
142 - updateMarker() { 154 + private updateMarker(key, location: L.LatLng, settings: MarkerSettings) {
  155 + const marker: Marker = this.markers.get(key);
  156 + if (!location.equals(marker.location)) {
  157 + marker.updateMarkerPosition(location);
  158 + }
  159 + //other implements later
143 160
144 } 161 }
145 162
146 - deleteMarker() { 163 + private deleteMarker() {
147 164
148 } 165 }
149 166
  167 + //polyline
  168 +
  169 + updatePolylines(polyData) {
  170 + if (this.poly) {
150 171
  172 + }
  173 +
  174 + else {
  175 + this.map$
  176 + this.createPolyline(polyData.map(data => this.convertPosition(data)), this.options);
  177 + }
  178 +
  179 + /* markersData.forEach(data => {
  180 + if (this.markers.get(data.aliasName)) {
  181 + this.updateMarker(data.aliasName, this.convertPosition(data), this.options as MarkerSettings)
  182 + }
  183 + else {
  184 + this.createMarker(data.aliasName, this.convertPosition(data), this.options as MarkerSettings);
  185 + }
  186 + });*/
  187 + }
  188 +
  189 + createPolyline(locations, settings) {
  190 + this.ready$.subscribe(() =>
  191 + this.poly = new Polyline(this.map, locations, settings)
  192 + )
  193 + }
151 } 194 }
@@ -4,6 +4,10 @@ export interface MapOptions { @@ -4,6 +4,10 @@ export interface MapOptions {
4 dontFitMapBounds?: boolean, 4 dontFitMapBounds?: boolean,
5 disableScrollZooming?: boolean, 5 disableScrollZooming?: boolean,
6 minZoomLevel?: number, 6 minZoomLevel?: number,
  7 + latKeyName?: string,
  8 + lngKeyName?: string,
  9 + xPosKeyName?: string,
  10 + yPosKeyName?: string,
7 mapProvider: MapProviders, 11 mapProvider: MapProviders,
8 mapUrl?: string; 12 mapUrl?: string;
9 credentials?: any, // declare credentials format 13 credentials?: any, // declare credentials format
@@ -19,9 +23,9 @@ export enum MapProviders { @@ -19,9 +23,9 @@ export enum MapProviders {
19 tencent = 'tencent-map' 23 tencent = 'tencent-map'
20 } 24 }
21 25
22 -export interface MarkerSettings{ 26 +export interface MarkerSettings {
23 showLabel?: boolean, 27 showLabel?: boolean,
24 draggable?: boolean, 28 draggable?: boolean,
25 displayTooltip?: boolean, 29 displayTooltip?: boolean,
26 - color: string 30 + color?: string
27 } 31 }
1 export interface MapWidgetInterface { 1 export interface MapWidgetInterface {
2 resize(), 2 resize(),
  3 + update(),
3 onInit(), 4 onInit(),
4 onDataUpdated(); 5 onDataUpdated();
5 onResize(); 6 onResize();
@@ -13,44 +13,21 @@ import { @@ -13,44 +13,21 @@ import {
13 } from './schemes'; 13 } from './schemes';
14 import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface'; 14 import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
15 import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers'; 15 import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
16 -  
17 -  
18 -const providerSets = {  
19 - 'openstreet-map': {  
20 - MapClass: OpenStreetMap,  
21 - schema: openstreetMapSettingsSchema,  
22 - name: "Openstreet"  
23 - },  
24 - 'tencent-map': {  
25 - MapClass: TencentMap,  
26 - schema: tencentMapSettingsSchema,  
27 - name: "Tencent"  
28 - },  
29 - 'google-map': {  
30 - MapClass: GoogleMap,  
31 - schema: googleMapSettingsSchema,  
32 - name: "Openstreet"  
33 - },  
34 - 'here': {  
35 - MapClass: HEREMap,  
36 - schema: hereMapSettingsSchema,  
37 - name: "HERE"  
38 - },  
39 - 'image-map': {  
40 - MapClass: ImageMap,  
41 - schema: imageMapSettingsSchema  
42 - }  
43 -} 16 +import { WidgetSubscription } from '@app/core/public-api';
  17 +import { parseData, parseArray } from './maps-utils';
44 18
45 export let TbMapWidgetV2: MapWidgetStaticInterface; 19 export let TbMapWidgetV2: MapWidgetStaticInterface;
46 TbMapWidgetV2 = class TbMapWidgetV2 implements MapWidgetInterface { 20 TbMapWidgetV2 = class TbMapWidgetV2 implements MapWidgetInterface {
47 map: LeafletMap; 21 map: LeafletMap;
48 provider: MapProviders; 22 provider: MapProviders;
49 schema; 23 schema;
  24 + data;
50 25
51 - constructor(mapProvider: MapProviders, drawRoutes, ctx, $element) {  
52 - console.log(ctx.settings);  
53 - 26 + constructor(mapProvider: MapProviders, private drawRoutes, ctx, $element) {
  27 + console.log("TbMapWidgetV2 -> constructor -> ctx", ctx)
  28 + // console.log(ctx.subscriptions, ctx.data, ctx.datasources);
  29 + this.data = ctx.data;
  30 + //this.subsciptions.
54 if (!$element) { 31 if (!$element) {
55 $element = ctx.$container[0]; 32 $element = ctx.$container[0];
56 } 33 }
@@ -71,17 +48,21 @@ TbMapWidgetV2 = class TbMapWidgetV2 implements MapWidgetInterface { @@ -71,17 +48,21 @@ TbMapWidgetV2 = class TbMapWidgetV2 implements MapWidgetInterface {
71 if (!MapClass) { 48 if (!MapClass) {
72 return; 49 return;
73 } 50 }
74 - this.map = new MapClass($element, { ...baseOptions, ...ctx.settings })  
75 - if(mapProvider !== "image-map")  
76 - this.map.createMarker({ lat: 0, lng: 0 }, { color: '#FD2785' })  
77 - else  
78 - this.map.createMarker({ x: 500, y: 500 }, { color: '#6D2785' }); 51 + this.map = new MapClass($element, { ...baseOptions, ...ctx.settings });
79 this.schema = providerSets[mapProvider]?.schema; 52 this.schema = providerSets[mapProvider]?.schema;
80 } 53 }
81 54
82 onInit() { 55 onInit() {
83 } 56 }
84 57
  58 + update() {
  59 + console.log(this.data,parseData(this.data) );
  60 +
  61 + if (this.drawRoutes)
  62 + this.map.updatePolylines(parseArray(this.data));
  63 + this.map.updateMarkers(parseData(this.data));
  64 + }
  65 +
85 onDataUpdated() { 66 onDataUpdated() {
86 } 67 }
87 68
@@ -173,4 +154,31 @@ TbMapWidgetV2 = class TbMapWidgetV2 implements MapWidgetInterface { @@ -173,4 +154,31 @@ TbMapWidgetV2 = class TbMapWidgetV2 implements MapWidgetInterface {
173 154
174 onDestroy() { 155 onDestroy() {
175 } 156 }
  157 +}
  158 +
  159 +const providerSets = {
  160 + 'openstreet-map': {
  161 + MapClass: OpenStreetMap,
  162 + schema: openstreetMapSettingsSchema,
  163 + name: "Openstreet"
  164 + },
  165 + 'tencent-map': {
  166 + MapClass: TencentMap,
  167 + schema: tencentMapSettingsSchema,
  168 + name: "Tencent"
  169 + },
  170 + 'google-map': {
  171 + MapClass: GoogleMap,
  172 + schema: googleMapSettingsSchema,
  173 + name: "Openstreet"
  174 + },
  175 + 'here': {
  176 + MapClass: HEREMap,
  177 + schema: hereMapSettingsSchema,
  178 + name: "HERE"
  179 + },
  180 + 'image-map': {
  181 + MapClass: ImageMap,
  182 + schema: imageMapSettingsSchema
  183 + }
176 } 184 }
1 import L from 'leaflet'; 1 import L from 'leaflet';
  2 +import _ from 'lodash';
2 3
3 export function createTooltip(target, settings, targetArgs?) { 4 export function createTooltip(target, settings, targetArgs?) {
4 var popup = L.popup(); 5 var popup = L.popup();
@@ -19,4 +20,27 @@ export function createTooltip(target, settings, targetArgs?) { @@ -19,4 +20,27 @@ export function createTooltip(target, settings, targetArgs?) {
19 locationSettings: settings, 20 locationSettings: settings,
20 dsIndex: settings.dsIndex 21 dsIndex: settings.dsIndex
21 }; 22 };
  23 +}
  24 +
  25 +export function parseArray(input: any[]): any[] {
  26 + let alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value();
  27 + return alliases.map(alliasArray =>
  28 + alliasArray[0].data.map((el, i) => {
  29 + const obj = { aliasName: alliasArray[0]?.datasource?.aliasName };
  30 + alliasArray.forEach(el => {
  31 + obj[el?.dataKey?.label] = el?.data[i][1]
  32 + });
  33 + return obj;
  34 + })
  35 + ).flat();
  36 +}
  37 +
  38 +export function parseData(input: any[]): any[] {
  39 + return _(input).groupBy(el => el?.datasource?.aliasName).values().value().map(alliasArray => {
  40 + const obj = { aliasName: alliasArray[0]?.datasource?.aliasName };
  41 + alliasArray.forEach(el => {
  42 + obj[el?.dataKey?.label] = el?.data[0][1]
  43 + });
  44 + return obj;
  45 + });
22 } 46 }
@@ -7,13 +7,14 @@ import { aspectCache } from '@app/core/utils'; @@ -7,13 +7,14 @@ import { aspectCache } from '@app/core/utils';
7 export class Marker { 7 export class Marker {
8 8
9 leafletMarker: L.Marker; 9 leafletMarker: L.Marker;
10 - // map: L.Map;  
11 10
12 tooltipOffset; 11 tooltipOffset;
13 tooltip; 12 tooltip;
  13 + location;
14 14
15 constructor(private map: L.Map, location: L.LatLngExpression, settings: MarkerSettings, onClickListener?, markerArgs?, onDragendListener?) { 15 constructor(private map: L.Map, location: L.LatLngExpression, settings: MarkerSettings, onClickListener?, markerArgs?, onDragendListener?) {
16 //this.map = map; 16 //this.map = map;
  17 + this.location = location;
17 this.leafletMarker = L.marker(location, { 18 this.leafletMarker = L.marker(location, {
18 draggable: settings.draggable 19 draggable: settings.draggable
19 }); 20 });
@@ -43,6 +44,10 @@ export class Marker { @@ -43,6 +44,10 @@ export class Marker {
43 44
44 } 45 }
45 46
  47 + updateMarkerPosition(position: L.LatLngExpression){
  48 + this.leafletMarker.setLatLng(position);
  49 + }
  50 +
46 updateMarkerLabel(settings) { 51 updateMarkerLabel(settings) {
47 this.leafletMarker.unbindTooltip(); 52 this.leafletMarker.unbindTooltip();
48 if (settings.showLabel) 53 if (settings.showLabel)
@@ -56,7 +61,7 @@ export class Marker { @@ -56,7 +61,7 @@ export class Marker {
56 }); 61 });
57 } 62 }
58 63
59 - updateMarkerIcon( settings) { 64 + updateMarkerIcon(settings) {
60 this.createMarkerIcon(settings, (iconInfo) => { 65 this.createMarkerIcon(settings, (iconInfo) => {
61 this.leafletMarker.setIcon(iconInfo.icon); 66 this.leafletMarker.setIcon(iconInfo.icon);
62 if (settings.showLabel) { 67 if (settings.showLabel) {
@@ -2,10 +2,9 @@ import L from 'leaflet'; @@ -2,10 +2,9 @@ import L from 'leaflet';
2 2
3 export class Polyline { 3 export class Polyline {
4 4
5 - map: L.Map;  
6 leafletPoly: L.Polyline; 5 leafletPoly: L.Polyline;
7 6
8 - constructor(locations, settings) { 7 + constructor(private map: L.Map, locations, settings) {
9 this.leafletPoly = L.polyline(locations, 8 this.leafletPoly = L.polyline(locations,
10 { 9 {
11 color: settings.color, 10 color: settings.color,
@@ -101,13 +101,13 @@ export class ImageMap extends LeafletMap { @@ -101,13 +101,13 @@ export class ImageMap extends LeafletMap {
101 } 101 }
102 } 102 }
103 103
104 - convertPosition(expression: { x, y }): L.LatLngExpression {  
105 - console.log("ImageMap -> expression", expression)  
106 - return this.pointToLatLng(expression.x, expression.y) as L.LatLngExpression; 104 + convertPosition(expression): L.LatLng {
  105 + return this.pointToLatLng(
  106 + expression[this.options.xPosKeyName] * this.width,
  107 + expression[this.options.yPosKeyName] * this.height);
107 } 108 }
108 109
109 -  
110 - pointToLatLng(x, y) { 110 + pointToLatLng(x, y): L.LatLng {
111 return L.CRS.Simple.pointToLatLng({ x, y } as L.PointExpression, maxZoom - 1); 111 return L.CRS.Simple.pointToLatLng({ x, y } as L.PointExpression, maxZoom - 1);
112 } 112 }
113 113
@@ -33,6 +33,7 @@ @@ -33,6 +33,7 @@
33 }, 33 },
34 "lib": [ 34 "lib": [
35 "es2018", 35 "es2018",
  36 + "es2019",
36 "dom" 37 "dom"
37 ] 38 ]
38 } 39 }