Commit 5e65f2a48a109b7359f29c8762fe419ee1b52d58

Authored by Artem Halushko
1 parent 5535c33d

trip-animation map provider & custom markers

... ... @@ -38,7 +38,8 @@
38 38 "src/app/shared/components/json-form/react/json-form.scss",
39 39 "node_modules/rc-select/assets/index.less",
40 40 "node_modules/jstree-bootstrap-theme/dist/themes/proton/style.min.css",
41   - "node_modules/leaflet/dist/leaflet.css"
  41 + "node_modules/leaflet/dist/leaflet.css",
  42 + "src/app/modules/home/components/widget/lib/maps/markers.scss"
42 43 ],
43 44 "stylePreprocessorOptions": {
44 45 "includePaths": [
... ...
  1 +
  2 +export function initSchema() {
  3 + return {
  4 + schema: {
  5 + type: "object",
  6 + properties: {},
  7 + required: []
  8 + },
  9 + form: [],
  10 + groupInfoes: []
  11 + };
  12 +}
  13 +
  14 +export function addGroupInfo(schema, title: string) {
  15 + schema.groupInfoes.push({
  16 + "formIndex": schema.groupInfoes?.length || 0,
  17 + "GroupTitle": title
  18 + });
  19 +}
  20 +
  21 +export function addToSchema(schema, newSchema) {
  22 + Object.assign(schema.schema.properties, newSchema.schema.properties);
  23 + schema.schema.required = schema.schema.required.concat(newSchema.schema.required);
  24 + schema.form.push(newSchema.form);
  25 +}
  26 +
  27 +export function mergeSchemes(schemes: any[]) {
  28 + return schemes.reduce((finalSchema, schema) => {
  29 + return {
  30 + schema: {
  31 + properties: {
  32 + ...finalSchema.schema.properties,
  33 + ...schema.schema.properties
  34 + },
  35 + required: [
  36 + ...finalSchema.schema.required,
  37 + ...schema.schema.required
  38 + ]
  39 + },
  40 + form: [
  41 + ...finalSchema.form,
  42 + ...schema.form
  43 + ]
  44 + }
  45 + }, initSchema());
  46 +}
  47 +
  48 +export function addCondition(schema, condition: String) {
  49 + schema.form = schema.form.map(element => {
  50 + if (typeof element === 'string') {
  51 + return {
  52 + key: element,
  53 + condition: condition
  54 + }
  55 + }
  56 + if (typeof element == 'object') {
  57 + if (element.condition) {
  58 + element.condition += ' && ' + condition
  59 + }
  60 + else element.condition = condition;
  61 + }
  62 + return element;
  63 + });
  64 + return schema;
  65 +}
\ No newline at end of file
... ...
... ... @@ -452,54 +452,55 @@ export function aspectCache(imageUrl: string): Observable<number> {
452 452 export function parseArray(input: any[]): any[] {
453 453 let alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value();
454 454 return alliases.map((alliasArray, dsIndex) =>
455   - alliasArray[0].data.map((el, i) => {
456   - const obj = {
457   - aliasName: alliasArray[0]?.datasource?.aliasName,
458   - $datasource: alliasArray[0]?.datasource,
459   - dsIndex: dsIndex,
460   - time: el[0]
461   - };
462   - alliasArray.forEach(el => {
463   - obj[el?.dataKey?.label] = el?.data[i][1];
464   - obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
465   - if (el?.dataKey?.label == 'type') {
466   - obj['deviceType'] = el?.data[0][1];
467   - }
468   - });
469   - return obj;
470   - })
471   - );
472   -}
473   -
474   -export function parseData(input: any[]): any[] {
475   - return _(input).groupBy(el => el?.datasource?.aliasName).values().value().map((alliasArray, i) => {
  455 + alliasArray[0].data.map((el, i) => {
476 456 const obj = {
477   - aliasName: alliasArray[0]?.datasource?.aliasName,
478   - entityName: alliasArray[0]?.datasource?.entityName,
479   - $datasource: alliasArray[0]?.datasource,
480   - dsIndex: i
  457 + aliasName: alliasArray[0]?.datasource?.aliasName,
  458 + entityName: alliasArray[0]?.datasource?.entityName,
  459 + $datasource: alliasArray[0]?.datasource,
  460 + dsIndex: dsIndex,
  461 + time: el[0]
481 462 };
482 463 alliasArray.forEach(el => {
483   - obj[el?.dataKey?.label] = el?.data[0][1];
484   - obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
485   - if (el?.dataKey?.label == 'type') {
486   - obj['deviceType'] = el?.data[0][1];
487   - }
  464 + obj[el?.dataKey?.label] = el?.data[i][1];
  465 + obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
  466 + if (el?.dataKey?.label == 'type') {
  467 + obj['deviceType'] = el?.data[0][1];
  468 + }
488 469 });
489 470 return obj;
  471 + })
  472 + );
  473 +}
  474 +
  475 +export function parseData(input: any[]): any[] {
  476 + return _(input).groupBy(el => el?.datasource?.aliasName).values().value().map((alliasArray, i) => {
  477 + const obj = {
  478 + aliasName: alliasArray[0]?.datasource?.aliasName,
  479 + entityName: alliasArray[0]?.datasource?.entityName,
  480 + $datasource: alliasArray[0]?.datasource,
  481 + dsIndex: i
  482 + };
  483 + alliasArray.forEach(el => {
  484 + obj[el?.dataKey?.label] = el?.data[0][1];
  485 + obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
  486 + if (el?.dataKey?.label == 'type') {
  487 + obj['deviceType'] = el?.data[0][1];
  488 + }
  489 + });
  490 + return obj;
490 491 });
491 492 }
492 493
493 494 export function safeExecute(func: Function, params = []) {
494 495 let res = null;
495 496 if (func && typeof (func) == "function") {
496   - try {
497   - res = func(...params);
498   - }
499   - catch (err) {
500   - console.error(err);
501   - res = null;
502   - }
  497 + try {
  498 + res = func(...params);
  499 + }
  500 + catch (err) {
  501 + console.error(err);
  502 + res = null;
  503 + }
503 504 }
504 505 return res;
505 506 }
... ... @@ -507,13 +508,13 @@ export function safeExecute(func: Function, params = []) {
507 508 export function parseFunction(source: string, params: string[] = []): Function {
508 509 let res = null;
509 510 if (source?.length) {
510   - try {
511   - res = new Function(...params, source);
512   - }
513   - catch (err) {
514   - console.error(err);
515   - res = null;
516   - }
  511 + try {
  512 + res = new Function(...params, source);
  513 + }
  514 + catch (err) {
  515 + console.error(err);
  516 + res = null;
  517 + }
517 518 }
518 519 return res;
519 520 }
\ No newline at end of file
... ...
... ... @@ -122,6 +122,11 @@ export default abstract class LeafletMap {
122 122 ////Markers
123 123 updateMarkers(markersData) {
124 124 markersData.forEach(data => {
  125 + if(data.rotationAngle){
  126 + this.options.icon= L.divIcon({
  127 + html: `<div class="arrow" style="transform: rotate(${data.rotationAngle}deg)"><div>`
  128 + })
  129 + }
125 130 if (this.markers.get(data.aliasName)) {
126 131 this.updateMarker(data.aliasName, data, markersData, this.options as MarkerSettings)
127 132 }
... ...
... ... @@ -18,6 +18,7 @@ export interface MapOptions {
18 18 useDefaultCenterPosition?: boolean,
19 19 gmDefaultMapType?: string,
20 20 useLabelFunction: string;
  21 + icon?: any;
21 22 }
22 23
23 24 export enum MapProviders {
... ... @@ -29,6 +30,7 @@ export enum MapProviders {
29 30 }
30 31
31 32 export interface MarkerSettings extends MapOptions {
  33 + icon?: any;
32 34 showLabel?: boolean,
33 35 draggable?: boolean,
34 36 displayTooltip?: boolean,
... ...
... ... @@ -8,7 +8,8 @@ export interface MapWidgetInterface {
8 8 }
9 9
10 10 export interface MapWidgetStaticInterface {
11   - settingsSchema(mapProvider, drawRoutes): Object;
  11 + settingsSchema(mapProvider?, drawRoutes?): Object;
  12 + getProvidersSchema():Object
12 13 dataKeySettingsSchema(): Object;
13 14 actionSources(): Object;
14 15 }
... ...
... ... @@ -15,6 +15,7 @@ import {
15 15 import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
16 16 import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
17 17 import { parseFunction, parseArray, parseData } from '@app/core/utils';
  18 +import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@app/core/schema-utils';
18 19
19 20 export class MapWidgetController implements MapWidgetInterface {
20 21
... ... @@ -73,7 +74,7 @@ export class MapWidgetController implements MapWidgetInterface {
73 74 this.map.updateMarkers(parseData(this.data));
74 75 }
75 76
76   - public updateHistoryData(dataSources){
  77 + public updateHistoryData(dataSources) {
77 78 dataSources.map()
78 79 }
79 80
... ... @@ -93,90 +94,28 @@ export class MapWidgetController implements MapWidgetInterface {
93 94 return {};
94 95 }
95 96
  97 + public static getProvidersSchema(){
  98 + return mergeSchemes([mapProviderSchema,
  99 + ...Object.values(providerSets)?.map(
  100 + setting => addCondition(setting?.schema, `model.provider === '${setting.name}'`))])
  101 + }
  102 +
96 103 public static settingsSchema(mapProvider, drawRoutes): Object {
97 104 //const providerInfo = providerSets[mapProvider];
98 105 let schema = initSchema();
  106 + addToSchema(schema,this.getProvidersSchema());
99 107
100   - function initSchema() {
101   - return {
102   - schema: {
103   - type: "object",
104   - properties: {},
105   - required: []
106   - },
107   - form: [],
108   - groupInfoes: []
109   - };
110   - }
111   -
112   - function addGroupInfo(title: string) {
113   - schema.groupInfoes.push({
114   - "formIndex": schema.groupInfoes?.length || 0,
115   - "GroupTitle": title
116   - });
117   - }
118   -
119   - function addToSchema(newSchema) {
120   - Object.assign(schema.schema.properties, newSchema.schema.properties);
121   - schema.schema.required = schema.schema.required.concat(newSchema.schema.required);
122   - schema.form.push(newSchema.form);//schema.form.concat(commonMapSettingsSchema.form);
123   - }
124   -
125   - function mergeSchemes(schemes: any[]) {
126   - return schemes.reduce((finalSchema, schema) => {
127   - return {
128   - schema: {
129   - properties: {
130   - ...finalSchema.schema.properties,
131   - ...schema.schema.properties
132   - },
133   - required: [
134   - ...finalSchema.schema.required,
135   - ...schema.schema.required
136   - ]
137   - },
138   - form: [
139   - ...finalSchema.form,
140   - ...schema.form
141   - ]
142   - }
143   - }, initSchema());
144   - }
145   -
146   - function addCondition(schema, condition: String) {
147   - schema.form = schema.form.map(element => {
148   - if (typeof element === 'string') {
149   - return {
150   - key: element,
151   - condition: condition
152   - }
153   - }
154   - if (typeof element == 'object') {
155   - if (element.condition) {
156   - element.condition += ' && ' + condition
157   - }
158   - else element.condition = condition;
159   - }
160   - return element;
161   - });
162   - return schema;
163   - }
164   -
165   - addToSchema(mergeSchemes([mapProviderSchema,
166   - ...Object.values(providerSets)?.map(
167   - setting => addCondition(setting?.schema, `model.provider === '${setting.name}'`))]));
168   -
169   - addGroupInfo("Map Provider Settings");
170   - addToSchema(commonMapSettingsSchema);
171   - addGroupInfo("Common Map Settings");
  108 + addGroupInfo(schema, "Map Provider Settings");
  109 + addToSchema(schema, commonMapSettingsSchema);
  110 + addGroupInfo(schema, "Common Map Settings");
172 111
173 112 if (drawRoutes) {
174   - addToSchema(routeMapSettingsSchema);
175   - addGroupInfo("Route Map Settings");
  113 + addToSchema(schema, routeMapSettingsSchema);
  114 + addGroupInfo(schema, "Route Map Settings");
176 115 } else if (mapProvider !== 'image-map') {
177 116 let clusteringSchema = mergeSchemes([markerClusteringSettingsSchemaLeaflet, markerClusteringSettingsSchema])
178   - addToSchema(clusteringSchema);
179   - addGroupInfo("Markers Clustering Settings");
  117 + addToSchema(schema, clusteringSchema);
  118 + addGroupInfo(schema, "Markers Clustering Settings");
180 119 }
181 120 console.log(11, schema);
182 121
... ... @@ -243,7 +182,7 @@ const defaultSettings = {
243 182 latKeyName: 'latitude',
244 183 lngKeyName: 'longitude',
245 184 polygonKeyName: 'coordinates',
246   - showLabel: true,
  185 + showLabel: false,
247 186 label: "${entityName}",
248 187 showTooltip: false,
249 188 useDefaultCenterPosition: false,
... ...
... ... @@ -30,6 +30,12 @@ export function interpolateArray(originData, interpolatedIntervals) {
30 30 return (intermediateMoment - firsMoment) / (secondMoment - firsMoment);
31 31 };
32 32
  33 + function findAngle(startPoint, endPoint) {
  34 + let angle = -Math.atan2(endPoint.latitude - startPoint.longitude, endPoint.longitude - startPoint.latitude);
  35 + angle = angle * 180 / Math.PI;
  36 + return parseInt(angle.toFixed(2));
  37 + }
  38 +
33 39 const result = {};
34 40
35 41 for (let i = 1, j = 0; i < originData.length, j < interpolatedIntervals.length;) {
... ... @@ -37,10 +43,16 @@ export function interpolateArray(originData, interpolatedIntervals) {
37 43 while (originData[i].time < currentTime) i++;
38 44 const before = originData[i - 1];
39 45 const after = originData[i];
40   - result[currentTime] = (interpolateOnPointSegment(
  46 + const interpolation = interpolateOnPointSegment(
41 47 new L.Point(before.latitude, before.longitude),
42 48 new L.Point(after.latitude, after.longitude),
43   - getRatio(before.time, after.time, currentTime)));
  49 + getRatio(before.time, after.time, currentTime));
  50 + result[currentTime] = ({
  51 + ...originData[i],
  52 + rotationAngle: findAngle(before, after),
  53 + latitude: interpolation.x,
  54 + longitude: interpolation.y
  55 + });
44 56 j++;
45 57 }
46 58
... ...
  1 +
  2 +.arrow{
  3 + background:#222;
  4 + text-align:center;
  5 + font-size:180%;
  6 + margin:2em;
  7 + font-family: Calibri, arial, sans-serif;
  8 + color:white;
  9 + padding-top:1.8em;
  10 + display:inline-block;/* or block */
  11 + position:relative;
  12 + border-color:white;
  13 + text-decoration:none;
  14 + transition:all .3s ease-out;
  15 +}
  16 +.arrow:before{
  17 + content:'▲';
  18 + font-size:.9em;
  19 + position:absolute;
  20 + top:0;
  21 + left:50%;
  22 + margin-left:-.7em;
  23 + border:solid .13em white;
  24 + border-radius:10em;
  25 + width:1.4em;
  26 + height:1.4em;
  27 + line-height:1.3em;
  28 + border-color:inherit;
  29 + transition:transform .5s ease-in;
  30 +}
  31 +.arrow:hover{
  32 + color:pink;
  33 + border-color:pink;
  34 +}
\ No newline at end of file
... ...
... ... @@ -25,7 +25,7 @@ export class Marker {
25 25 this.leafletMarker.setIcon(iconInfo.icon);
26 26 if (settings.showLabel) {
27 27 this.tooltipOffset = [0, -iconInfo.size[1] + 10];
28   - this.updateMarkerLabel(settings)
  28 + // this.updateMarkerLabel(settings)
29 29 }
30 30
31 31 this.leafletMarker.addTo(map)
... ... @@ -55,8 +55,6 @@ export class Marker {
55 55 this.leafletMarker.setLatLng(position);
56 56 }
57 57
58   -
59   -
60 58 updateMarkerLabel(settings) {
61 59
62 60 function getText(template, data) {
... ... @@ -74,7 +72,7 @@ export class Marker {
74 72 return res;
75 73 }
76 74
77   -
  75 +
78 76 this.leafletMarker.unbindTooltip();
79 77 if (settings.showLabel) {
80 78 if (settings.useLabelFunction) {
... ... @@ -104,9 +102,20 @@ export class Marker {
104 102 }
105 103
106 104 createMarkerIcon(onMarkerIconReady) {
107   - const currentImage = this.settings.useMarkerImageFunction ?
108   - safeExecute(this.settings.markerImageFunction, [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage;
109   - // var opMap = this;
  105 +
  106 + if (this.settings.icon) {
  107 + onMarkerIconReady({
  108 + size: [30,30],
  109 + icon: this.settings.icon,
  110 + });
  111 + return;
  112 + }
  113 +
  114 + let currentImage = this.settings.useMarkerImageFunction ?
  115 + safeExecute(this.settings.markerImageFunction,
  116 + [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage;
  117 +
  118 +
110 119 if (currentImage && currentImage.url) {
111 120 aspectCache(currentImage.url).subscribe(
112 121 (aspect) => {
... ...
... ... @@ -856,4 +856,373 @@ export const mapProviderSchema =
856 856 ]
857 857 }
858 858 ]
859   -};
\ No newline at end of file
  859 +};
  860 +
  861 +
  862 +export const tripAnimationSchema = {
  863 + "schema": {
  864 + "title": "Openstreet Map Configuration",
  865 + "type": "object",
  866 + "properties": {
  867 + "normalizationStep": {
  868 + "title": "Normalization data step (ms)",
  869 + "type": "number",
  870 + "default": 1000
  871 + },
  872 + "latKeyName": {
  873 + "title": "Latitude key name",
  874 + "type": "string",
  875 + "default": "latitude"
  876 + },
  877 + "lngKeyName": {
  878 + "title": "Longitude key name",
  879 + "type": "string",
  880 + "default": "longitude"
  881 + },
  882 + "polKeyName": {
  883 + "title": "Polygon key name",
  884 + "type": "string",
  885 + "default": "coordinates"
  886 + },
  887 + "showLabel": {
  888 + "title": "Show label",
  889 + "type": "boolean",
  890 + "default": true
  891 + },
  892 + "label": {
  893 + "title": "Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )",
  894 + "type": "string",
  895 + "default": "${entityName}"
  896 + },
  897 + "useLabelFunction": {
  898 + "title": "Use label function",
  899 + "type": "boolean",
  900 + "default": false
  901 + },
  902 + "labelFunction": {
  903 + "title": "Label function: f(data, dsData, dsIndex)",
  904 + "type": "string"
  905 + },
  906 + "showTooltip": {
  907 + "title": "Show tooltip",
  908 + "type": "boolean",
  909 + "default": true
  910 + },
  911 + "tooltipColor": {
  912 + "title": "Tooltip background color",
  913 + "type": "string",
  914 + "default": "#fff"
  915 + },
  916 + "tooltipFontColor": {
  917 + "title": "Tooltip font color",
  918 + "type": "string",
  919 + "default": "#000"
  920 + },
  921 + "tooltipOpacity": {
  922 + "title": "Tooltip opacity (0-1)",
  923 + "type": "number",
  924 + "default": 1
  925 + },
  926 + "tooltipPattern": {
  927 + "title": "Tooltip (for ex. 'Text ${keyName} units.' or <link-act name='my-action'>Link text</link-act>')",
  928 + "type": "string",
  929 + "default": "<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}"
  930 + },
  931 + "useTooltipFunction": {
  932 + "title": "Use tooltip function",
  933 + "type": "boolean",
  934 + "default": false
  935 + },
  936 + "tooltipFunction": {
  937 + "title": "Tooltip function: f(data, dsData, dsIndex)",
  938 + "type": "string"
  939 + },
  940 + "color": {
  941 + "title": "Path color",
  942 + "type": "string"
  943 + },
  944 + "strokeWeight": {
  945 + "title": "Stroke weight",
  946 + "type": "number",
  947 + "default": 2
  948 + },
  949 + "strokeOpacity": {
  950 + "title": "Stroke opacity",
  951 + "type": "number",
  952 + "default": 1
  953 + },
  954 + "useColorFunction": {
  955 + "title": "Use path color function",
  956 + "type": "boolean",
  957 + "default": false
  958 + },
  959 + "colorFunction": {
  960 + "title": "Path color function: f(data, dsData, dsIndex)",
  961 + "type": "string"
  962 + },
  963 + "usePolylineDecorator": {
  964 + "title": "Use path decorator",
  965 + "type": "boolean",
  966 + "default": false
  967 + },
  968 + "decoratorSymbol": {
  969 + "title": "Decorator symbol",
  970 + "type": "string",
  971 + "default": "arrowHead"
  972 + },
  973 + "decoratorSymbolSize": {
  974 + "title": "Decorator symbol size (px)",
  975 + "type": "number",
  976 + "default": 10
  977 + },
  978 + "useDecoratorCustomColor": {
  979 + "title": "Use path decorator custom color",
  980 + "type": "boolean",
  981 + "default": false
  982 + },
  983 + "decoratorCustomColor": {
  984 + "title": "Decorator custom color",
  985 + "type": "string",
  986 + "default": "#000"
  987 + },
  988 + "decoratorOffset": {
  989 + "title": "Decorator offset",
  990 + "type": "string",
  991 + "default": "20px"
  992 + },
  993 + "endDecoratorOffset": {
  994 + "title": "End decorator offset",
  995 + "type": "string",
  996 + "default": "20px"
  997 + },
  998 + "decoratorRepeat": {
  999 + "title": "Decorator repeat",
  1000 + "type": "string",
  1001 + "default": "20px"
  1002 + },
  1003 + "showPolygon": {
  1004 + "title": "Show polygon",
  1005 + "type": "boolean",
  1006 + "default": false
  1007 + },
  1008 + "polygonTooltipPattern": {
  1009 + "title": "Tooltip (for ex. 'Text ${keyName} units.' or <link-act name='my-action'>Link text</link-act>')",
  1010 + "type": "string",
  1011 + "default": "<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}"
  1012 + },
  1013 + "usePolygonTooltipFunction": {
  1014 + "title": "Use polygon tooltip function",
  1015 + "type": "boolean",
  1016 + "default": false
  1017 + },
  1018 + "polygonTooltipFunction": {
  1019 + "title": "Polygon tooltip function: f(data, dsData, dsIndex)",
  1020 + "type": "string"
  1021 + },
  1022 + "polygonColor": {
  1023 + "title": "Polygon color",
  1024 + "type": "string"
  1025 + },
  1026 + "polygonOpacity": {
  1027 + "title": "Polygon opacity",
  1028 + "type": "number",
  1029 + "default": 0.5
  1030 + },
  1031 + "polygonStrokeColor": {
  1032 + "title": "Polygon border color",
  1033 + "type": "string"
  1034 + },
  1035 + "polygonStrokeOpacity": {
  1036 + "title": "Polygon border opacity",
  1037 + "type": "number",
  1038 + "default": 1
  1039 + },
  1040 + "polygonStrokeWeight": {
  1041 + "title": "Polygon border weight",
  1042 + "type": "number",
  1043 + "default": 1
  1044 + },
  1045 + "usePolygonColorFunction": {
  1046 + "title": "Use polygon color function",
  1047 + "type": "boolean",
  1048 + "default": false
  1049 + },
  1050 + "polygonColorFunction": {
  1051 + "title": "Polygon Color function: f(data, dsData, dsIndex)",
  1052 + "type": "string"
  1053 + },
  1054 + "showPoints": {
  1055 + "title": "Show points",
  1056 + "type": "boolean",
  1057 + "default": false
  1058 + },
  1059 + "pointColor": {
  1060 + "title": "Point color",
  1061 + "type": "string"
  1062 + },
  1063 + "pointSize": {
  1064 + "title": "Point size (px)",
  1065 + "type": "number",
  1066 + "default": 10
  1067 + },
  1068 + "usePointAsAnchor": {
  1069 + "title": "Use point as anchor",
  1070 + "type": "boolean",
  1071 + "default": false
  1072 + },
  1073 + "pointAsAnchorFunction": {
  1074 + "title": "Point as anchor function: f(data, dsData, dsIndex)",
  1075 + "type": "string"
  1076 + },
  1077 + "pointTooltipOnRightPanel": {
  1078 + "title": "Independant point tooltip",
  1079 + "type": "boolean",
  1080 + "default": true
  1081 + },
  1082 + "autocloseTooltip": {
  1083 + "title": "Auto-close point popup",
  1084 + "type": "boolean",
  1085 + "default": true
  1086 + },
  1087 + "markerImage": {
  1088 + "title": "Custom marker image",
  1089 + "type": "string"
  1090 + },
  1091 + "markerImageSize": {
  1092 + "title": "Custom marker image size (px)",
  1093 + "type": "number",
  1094 + "default": 34
  1095 + },
  1096 + "rotationAngle": {
  1097 + "title": "Set additional rotation angle for marker (deg)",
  1098 + "type": "number",
  1099 + "default": 180
  1100 + },
  1101 + "useMarkerImageFunction": {
  1102 + "title": "Use marker image function",
  1103 + "type": "boolean",
  1104 + "default": false
  1105 + },
  1106 + "markerImageFunction": {
  1107 + "title": "Marker image function: f(data, images, dsData, dsIndex)",
  1108 + "type": "string"
  1109 + },
  1110 + "markerImages": {
  1111 + "title": "Marker images",
  1112 + "type": "array",
  1113 + "items": {
  1114 + "title": "Marker image",
  1115 + "type": "string"
  1116 + }
  1117 + }
  1118 + },
  1119 + "required": []
  1120 + },
  1121 + "form": [{
  1122 + "key": "mapProvider",
  1123 + "type": "rc-select",
  1124 + "multiple": false,
  1125 + "items": [{
  1126 + "value": "OpenStreetMap.Mapnik",
  1127 + "label": "OpenStreetMap.Mapnik (Default)"
  1128 + }, {
  1129 + "value": "OpenStreetMap.BlackAndWhite",
  1130 + "label": "OpenStreetMap.BlackAndWhite"
  1131 + }, {
  1132 + "value": "OpenStreetMap.HOT",
  1133 + "label": "OpenStreetMap.HOT"
  1134 + }, {
  1135 + "value": "Esri.WorldStreetMap",
  1136 + "label": "Esri.WorldStreetMap"
  1137 + }, {
  1138 + "value": "Esri.WorldTopoMap",
  1139 + "label": "Esri.WorldTopoMap"
  1140 + }, {
  1141 + "value": "CartoDB.Positron",
  1142 + "label": "CartoDB.Positron"
  1143 + }, {
  1144 + "value": "CartoDB.DarkMatter",
  1145 + "label": "CartoDB.DarkMatter"
  1146 + }]
  1147 + }, "normalizationStep", "latKeyName", "lngKeyName", "polKeyName", "showLabel", "label", "useLabelFunction", {
  1148 + "key": "labelFunction",
  1149 + "type": "javascript"
  1150 + }, "showTooltip", {
  1151 + "key": "tooltipColor",
  1152 + "type": "color"
  1153 + }, {
  1154 + "key": "tooltipFontColor",
  1155 + "type": "color"
  1156 + }, "tooltipOpacity", {
  1157 + "key": "tooltipPattern",
  1158 + "type": "textarea"
  1159 + }, "useTooltipFunction", {
  1160 + "key": "tooltipFunction",
  1161 + "type": "javascript"
  1162 + }, {
  1163 + "key": "color",
  1164 + "type": "color"
  1165 + }, "useColorFunction", {
  1166 + "key": "colorFunction",
  1167 + "type": "javascript"
  1168 + }, "usePolylineDecorator", {
  1169 + "key": "decoratorSymbol",
  1170 + "type": "rc-select",
  1171 + "multiple": false,
  1172 + "items": [{
  1173 + "value": "arrowHead",
  1174 + "label": "Arrow"
  1175 + }, {
  1176 + "value": "dash",
  1177 + "label": "Dash"
  1178 + }]
  1179 + }, "decoratorSymbolSize", "useDecoratorCustomColor", {
  1180 + "key": "decoratorCustomColor",
  1181 + "type": "color"
  1182 + }, {
  1183 + "key": "decoratorOffset",
  1184 + "type": "textarea"
  1185 + }, {
  1186 + "key": "endDecoratorOffset",
  1187 + "type": "textarea"
  1188 + }, {
  1189 + "key": "decoratorRepeat",
  1190 + "type": "textarea"
  1191 + }, "strokeWeight", "strokeOpacity", "showPolygon", {
  1192 + "key": "polygonTooltipPattern",
  1193 + "type": "textarea"
  1194 + }, "usePolygonTooltipFunction", {
  1195 + "key": "polygonTooltipFunction",
  1196 + "type": "javascript"
  1197 + }, {
  1198 + "key": "polygonColor",
  1199 + "type": "color"
  1200 + }, "polygonOpacity", {
  1201 + "key": "polygonStrokeColor",
  1202 + "type": "color"
  1203 + }, "polygonStrokeOpacity", "polygonStrokeWeight", "usePolygonColorFunction", {
  1204 + "key": "polygonColorFunction",
  1205 + "type": "javascript"
  1206 + }, "showPoints", {
  1207 + "key": "pointColor",
  1208 + "type": "color"
  1209 + }, "pointSize", "usePointAsAnchor", {
  1210 + "key": "pointAsAnchorFunction",
  1211 + "type": "javascript"
  1212 + }, "pointTooltipOnRightPanel", "autocloseTooltip", {
  1213 + "key": "markerImage",
  1214 + "type": "image"
  1215 + }, "markerImageSize", "rotationAngle", "useMarkerImageFunction",
  1216 + {
  1217 + "key": "markerImageFunction",
  1218 + "type": "javascript"
  1219 + }, {
  1220 + "key": "markerImages",
  1221 + "items": [
  1222 + {
  1223 + "key": "markerImages[]",
  1224 + "type": "image"
  1225 + }
  1226 + ]
  1227 + }]
  1228 +}
\ No newline at end of file
... ...
1 1 <div class="map" #map ></div>
2   -<div>{{historicalData?.lenth}}</div>
3   -<tb-history-selector *ngIf="historicalData" [settings]="ctx.settings" [intervals]="intervals"
  2 +<tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals"
4 3 (onTimeUpdated)="timeUpdated($event)"></tb-history-selector>
... ...
1 1 import { Component, OnInit, Input, ViewChild, AfterViewInit, ChangeDetectorRef } from '@angular/core';
2   -import { MapWidgetController } from '../lib/maps/map-widget2';
  2 +import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
3 3 import { MapProviders } from '../lib/maps/map-models';
4 4 import { parseArray } from '@app/core/utils';
5 5 import { interpolateArray } from '../lib/maps/maps-utils';
  6 +import tinycolor from "tinycolor2";
  7 +import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils';
  8 +import { tripAnimationSchema } from '../lib/maps/schemes';
  9 +import L from 'leaflet';
6 10
7 11 @Component({
8 12 selector: 'trip-animation',
... ... @@ -18,35 +22,45 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
18 22 mapWidget: MapWidgetController;
19 23 historicalData;
20 24 intervals;
21   - normalizationStep = 500;
  25 + normalizationStep = 1000;
22 26 interpolatedData = [];
23   -
  27 + widgetConfig;
  28 + settings;
24 29
25 30 constructor(private cd: ChangeDetectorRef) { }
26 31
27 32 ngOnInit(): void {
  33 + this.widgetConfig = this.ctx.widgetConfig;
  34 + const settings = {
  35 + normalizationStep: 1000,
  36 + buttonColor: tinycolor(this.widgetConfig.color).setAlpha(0.54).toRgbString(),
  37 + disabledButtonColor: tinycolor(this.widgetConfig.color).setAlpha(0.3).toRgbString(),
  38 + rotationAngle: 0
  39 + }
  40 + this.settings = { ...settings, ...this.ctx.settings };
  41 + //this.ctx.settings = settings;
  42 + console.log("TripAnimationComponent -> ngOnInit -> this.ctx.settings", this.ctx.settings)
28 43 let subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]];
29 44 if (subscription) subscription.callbacks.onDataUpdated = (updated) => {
30 45 this.historicalData = parseArray(this.ctx.data);
31   - this.historicalData.forEach(el => {
32   - console.log("TripAnimationComponent -> if -> el", el)
  46 + this.historicalData.forEach(ds => ds.forEach(el => {
33 47 el.longitude += (Math.random() - 0.5)
34 48 el.latitude += (Math.random() - 0.5)
35   - });
  49 + }));
36 50 this.calculateIntervals();
  51 + this.timeUpdated(this.intervals[0]);
37 52 this.cd.detectChanges();
  53 + this.mapWidget.map.map.invalidateSize();
38 54 }
39 55 }
40 56
41 57 ngAfterViewInit() {
42 58 this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement);
43   - this.mapWidget.data
44 59 }
45 60
46 61 timeUpdated(time) {
47   - //this.mapWidget.ma
48   - const currentPosition = this.interpolatedData.map(dataSource=>dataSource[time]);
49   - console.log("TripAnimationComponent -> timeUpdated -> currentPosition", currentPosition)
  62 + const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]);
  63 + this.mapWidget.map.updateMarkers(currentPosition);
50 64 }
51 65
52 66 calculateIntervals() {
... ... @@ -57,9 +71,17 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
57 71 }
58 72 this.intervals.push(dataSource[dataSource.length - 1]?.time);
59 73 this.interpolatedData[index] = interpolateArray(dataSource, this.intervals);
60   - console.log("TripAnimationComponent -> calculateIntervals -> this.intervals", this.intervals)
61   -
62 74 });
63 75 }
64 76
  77 + static getSettingsSchema() {
  78 + let schema = initSchema();
  79 + addToSchema(schema, TbMapWidgetV2.getProvidersSchema());
  80 + addGroupInfo(schema, "Map Provider Settings");
  81 + addToSchema(schema, tripAnimationSchema);
  82 + addGroupInfo(schema, "Trip Animation Settings");
  83 + return schema;
  84 + }
65 85 }
  86 +
  87 +export let TbTripAnimationWidget = TripAnimationComponent;
\ No newline at end of file
... ...
1 1 import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core';
2   -import { interval } from 'rxjs';
  2 +import { interval, Subscription } from 'rxjs';
3 3 import { filter, tap } from 'rxjs/operators';
4 4
5 5 @Component({
... ... @@ -34,23 +34,25 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
34 34
35 35 play() {
36 36 this.playing = true;
37   - this.interval = interval(1000 / this.speed)
38   - .pipe(
39   - filter(() => this.playing),
40   - tap(() => this.index++)).subscribe(() => {
41   - if (this.index < this.maxTimeIndex) {
42   - this.cd.detectChanges();
43   - this.onTimeUpdated.emit(this.intervals[this.index]);
44   - }
45   - else {
46   - this.interval.complete();
47   - }
48   - }, err => {
49   - console.log(err);
50   - }, () => {
51   - this.index = this.minTimeIndex;
52   - this.playing = false;
53   - })
  37 + if (!this.interval)
  38 + this.interval = interval(1000 / this.speed)
  39 + .pipe(
  40 + filter(() => this.playing),
  41 + tap(() => this.index++)).subscribe(() => {
  42 + if (this.index < this.maxTimeIndex) {
  43 + this.cd.detectChanges();
  44 + this.onTimeUpdated.emit(this.intervals[this.index]);
  45 + }
  46 + else {
  47 + this.interval.complete();
  48 + }
  49 + }, err => {
  50 + console.log(err);
  51 + }, () => {
  52 + this.index = this.minTimeIndex;
  53 + this.playing = false;
  54 + this.interval = null;
  55 + });
54 56 }
55 57
56 58 pause() {
... ...
... ... @@ -93,6 +93,7 @@ import { TbAnalogueRadialGauge } from '@home/components/widget/lib/analogue-radi
93 93 import { TbAnalogueLinearGauge } from '@home/components/widget/lib/analogue-linear-gauge';
94 94 import { TbCanvasDigitalGauge } from '@home/components/widget/lib/digital-gauge';
95 95 import { TbMapWidgetV2 } from '@home/components/widget/lib/maps/map-widget2';
  96 +import { TbTripAnimationWidget } from '@app/modules/home/components/widget/trip-animation/trip-animation.component';
96 97
97 98 import * as tinycolor_ from 'tinycolor2';
98 99
... ... @@ -106,3 +107,5 @@ const tinycolor = tinycolor_;
106 107 (window as any).TbAnalogueLinearGauge = TbAnalogueLinearGauge;
107 108 (window as any).TbCanvasDigitalGauge = TbCanvasDigitalGauge;
108 109 (window as any).TbMapWidgetV2 = TbMapWidgetV2;
  110 +(window as any).TbTripAnimationWidget = TbTripAnimationWidget;
  111 +console.log("TbTripAnimationWidget", TbTripAnimationWidget)
... ...