Commit 416d0458933548db86aed4cb5dcd2efbea13d54f
Committed by
GitHub
1 parent
7a87e6c1
Map/3.0 (#2738)
* WIP on trip-animation settings * trip-animation points & anchors * fixes Co-authored-by: Adsumus <artemtv42@gmail.com> Co-authored-by: Igor Kulikov <ikulikov@thingsboard.io>
Showing
11 changed files
with
439 additions
and
396 deletions
... | ... | @@ -14,18 +14,18 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import L, { LatLngBounds, LatLngTuple, markerClusterGroup, MarkerClusterGroupOptions } from 'leaflet'; | |
17 | +import L, { LatLngBounds, LatLngTuple, markerClusterGroup, MarkerClusterGroupOptions, FeatureGroup, LayerGroup } from 'leaflet'; | |
18 | 18 | |
19 | 19 | import 'leaflet-providers'; |
20 | 20 | import 'leaflet.markercluster/dist/leaflet.markercluster'; |
21 | 21 | |
22 | 22 | import { |
23 | - FormattedData, | |
24 | - MapSettings, | |
25 | - MarkerSettings, | |
26 | - PolygonSettings, | |
27 | - PolylineSettings, | |
28 | - UnitedMapSettings | |
23 | + FormattedData, | |
24 | + MapSettings, | |
25 | + MarkerSettings, | |
26 | + PolygonSettings, | |
27 | + PolylineSettings, | |
28 | + UnitedMapSettings | |
29 | 29 | } from './map-models'; |
30 | 30 | import { Marker } from './markers'; |
31 | 31 | import { BehaviorSubject, Observable } from 'rxjs'; |
... | ... | @@ -33,7 +33,7 @@ import { filter } from 'rxjs/operators'; |
33 | 33 | import { Polyline } from './polyline'; |
34 | 34 | import { Polygon } from './polygon'; |
35 | 35 | import { DatasourceData } from '@app/shared/models/widget.models'; |
36 | -import { safeExecute } from '@home/components/widget/lib/maps/maps-utils'; | |
36 | +import { safeExecute, createTooltip } from '@home/components/widget/lib/maps/maps-utils'; | |
37 | 37 | |
38 | 38 | export default abstract class LeafletMap { |
39 | 39 | |
... | ... | @@ -47,6 +47,8 @@ export default abstract class LeafletMap { |
47 | 47 | bounds: L.LatLngBounds; |
48 | 48 | datasources: FormattedData[]; |
49 | 49 | markersCluster; |
50 | + points: FeatureGroup; | |
51 | + markersData = []; | |
50 | 52 | |
51 | 53 | protected constructor(public $container: HTMLElement, options: UnitedMapSettings) { |
52 | 54 | this.options = options; |
... | ... | @@ -157,9 +159,9 @@ export default abstract class LeafletMap { |
157 | 159 | this.map = map; |
158 | 160 | if (this.options.useDefaultCenterPosition) { |
159 | 161 | this.map.panTo(this.options.defaultCenterPosition); |
160 | - this.bounds = map.getBounds(); | |
162 | + this.bounds = map.getBounds(); | |
161 | 163 | } |
162 | - else this.bounds = new L.LatLngBounds(null, null); | |
164 | + else this.bounds = new L.LatLngBounds(null, null); | |
163 | 165 | if (this.options.draggableMarker) { |
164 | 166 | this.addMarkerControl(); |
165 | 167 | } |
... | ... | @@ -200,9 +202,9 @@ export default abstract class LeafletMap { |
200 | 202 | return this.map.getCenter(); |
201 | 203 | } |
202 | 204 | |
203 | - fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { | |
205 | + fitBounds(bounds: LatLngBounds, padding?: LatLngTuple) { | |
204 | 206 | if (bounds.isValid()) { |
205 | - this.bounds = this.bounds.extend(bounds); | |
207 | + this.bounds = !!this.bounds ? this.bounds.extend(bounds) : bounds; | |
206 | 208 | if (!this.options.fitMapBounds && this.options.defaultZoomLevel) { |
207 | 209 | this.map.setZoom(this.options.defaultZoomLevel, { animate: false }); |
208 | 210 | if (this.options.useDefaultCenterPosition) { |
... | ... | @@ -218,9 +220,9 @@ export default abstract class LeafletMap { |
218 | 220 | } |
219 | 221 | }); |
220 | 222 | if (this.options.useDefaultCenterPosition) { |
221 | - bounds = bounds.extend(this.options.defaultCenterPosition); | |
223 | + this.bounds = this.bounds.extend(this.options.defaultCenterPosition); | |
222 | 224 | } |
223 | - this.map.fitBounds(bounds, { padding: padding || [50, 50], animate: false }); | |
225 | + this.map.fitBounds(this.bounds, { padding: padding || [50, 50], animate: false }); | |
224 | 226 | } |
225 | 227 | } |
226 | 228 | } |
... | ... | @@ -252,11 +254,10 @@ export default abstract class LeafletMap { |
252 | 254 | const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : ''; |
253 | 255 | this.options.icon = L.divIcon({ |
254 | 256 | html: `<div class="arrow" |
255 | - style="transform: translate(-10px, -10px); | |
256 | - ${style} | |
257 | - rotate(${data.rotationAngle}deg); | |
258 | - "><div>` | |
259 | - }) | |
257 | + style="transform: translate(-10px, -10px) | |
258 | + rotate(${data.rotationAngle}deg); | |
259 | + ${style}"><div>` | |
260 | + }); | |
260 | 261 | } |
261 | 262 | else { |
262 | 263 | this.options.icon = null; |
... | ... | @@ -268,6 +269,7 @@ export default abstract class LeafletMap { |
268 | 269 | this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings); |
269 | 270 | } |
270 | 271 | }); |
272 | + this.markersData = markersData; | |
271 | 273 | } |
272 | 274 | |
273 | 275 | dragMarker = (e, data?) => { |
... | ... | @@ -278,7 +280,8 @@ export default abstract class LeafletMap { |
278 | 280 | private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) { |
279 | 281 | this.ready$.subscribe(() => { |
280 | 282 | const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker); |
281 | - this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), settings.draggableMarker && this.markers.size < 2); | |
283 | + if (this.bounds) | |
284 | + this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng())); | |
282 | 285 | this.markers.set(key, newMarker); |
283 | 286 | if (this.options.useClusterMarkers) { |
284 | 287 | this.markersCluster.addLayer(newMarker.leafletMarker); |
... | ... | @@ -313,6 +316,29 @@ export default abstract class LeafletMap { |
313 | 316 | } |
314 | 317 | } |
315 | 318 | |
319 | + updatePoints(pointsData: FormattedData[], getTooltip: (point: FormattedData, setTooltip?: boolean) => string) { | |
320 | + this.map$.subscribe(map => { | |
321 | + if (this.points) { | |
322 | + map.removeLayer(this.points); | |
323 | + } | |
324 | + this.points = new FeatureGroup(); | |
325 | + pointsData.filter(pdata => !!this.convertPosition(pdata)).forEach(data => { | |
326 | + const point = L.circleMarker(this.convertPosition(data), { | |
327 | + color: this.options.pointColor, | |
328 | + radius: this.options.pointSize | |
329 | + }); | |
330 | + if (!this.options.pointTooltipOnRightPanel) { | |
331 | + point.on('click', () => getTooltip(data)); | |
332 | + } | |
333 | + else { | |
334 | + createTooltip(point, this.options, pointsData, getTooltip(data, false)); | |
335 | + } | |
336 | + this.points.addLayer(point); | |
337 | + }); | |
338 | + map.addLayer(this.points); | |
339 | + }); | |
340 | + } | |
341 | + | |
316 | 342 | setImageAlias(alias: Observable<any>) { |
317 | 343 | } |
318 | 344 | |
... | ... | @@ -337,15 +363,17 @@ export default abstract class LeafletMap { |
337 | 363 | this.ready$.subscribe(() => { |
338 | 364 | const poly = new Polyline(this.map, |
339 | 365 | data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings); |
340 | - const bounds = this.bounds.extend(poly.leafletPoly.getBounds()); | |
341 | - this.fitBounds(bounds) | |
342 | - this.polylines.set(data[0].entityName, poly) | |
366 | + const bounds = poly.leafletPoly.getBounds(); | |
367 | + this.fitBounds(bounds); | |
368 | + this.polylines.set(data[0].entityName, poly); | |
343 | 369 | }); |
344 | 370 | } |
345 | 371 | |
346 | 372 | updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) { |
347 | 373 | this.ready$.subscribe(() => { |
348 | - this.polylines.get(key).updatePolyline(settings, data.map(el => this.convertPosition(el)), dataSources); | |
374 | + const poly = this.polylines.get(key); | |
375 | + poly.updatePolyline(settings, data.map(el => this.convertPosition(el)), dataSources); | |
376 | + const bounds = poly.leafletPoly.getBounds(); | |
349 | 377 | }); |
350 | 378 | } |
351 | 379 | |
... | ... | @@ -370,7 +398,7 @@ export default abstract class LeafletMap { |
370 | 398 | createPolygon(polyData: DatasourceData, dataSources: DatasourceData[], settings: PolygonSettings) { |
371 | 399 | this.ready$.subscribe(() => { |
372 | 400 | const polygon = new Polygon(this.map, polyData, dataSources, settings); |
373 | - const bounds = this.bounds.extend(polygon.leafletPoly.getBounds()); | |
401 | + const bounds = polygon.leafletPoly.getBounds(); | |
374 | 402 | this.fitBounds(bounds); |
375 | 403 | this.polygons.set(polyData.datasource.entityName, polygon); |
376 | 404 | }); |
... | ... | @@ -380,7 +408,6 @@ export default abstract class LeafletMap { |
380 | 408 | this.ready$.subscribe(() => { |
381 | 409 | const poly = this.polygons.get(polyData.datasource.entityName); |
382 | 410 | poly.updatePolygon(polyData.data, dataSources, settings); |
383 | - this.fitBounds(poly.leafletPoly.getBounds()); | |
384 | 411 | }); |
385 | 412 | } |
386 | 413 | } | ... | ... |
... | ... | @@ -15,13 +15,19 @@ |
15 | 15 | /// |
16 | 16 | |
17 | 17 | import { LatLngTuple } from 'leaflet'; |
18 | -import { Datasource } from '@app/shared/models/widget.models'; | |
18 | +import { Datasource, JsonSettingsSchema } from '@app/shared/models/widget.models'; | |
19 | +import { Type } from '@angular/core'; | |
20 | +import LeafletMap from './leaflet-map'; | |
21 | +import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers'; | |
22 | +import { | |
23 | + openstreetMapSettingsSchema, tencentMapSettingsSchema, | |
24 | + googleMapSettingsSchema, hereMapSettingsSchema, imageMapSettingsSchema | |
25 | +} from './schemes'; | |
19 | 26 | |
20 | 27 | export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; |
21 | 28 | export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; |
22 | 29 | |
23 | 30 | export type MapSettings = { |
24 | - polygonKeyName: any; | |
25 | 31 | draggableMarker: boolean; |
26 | 32 | initCallback?: () => any; |
27 | 33 | posFunction: (origXPos, origYPos) => { x, y }; |
... | ... | @@ -108,7 +114,8 @@ export interface FormattedData { |
108 | 114 | |
109 | 115 | export type PolygonSettings = { |
110 | 116 | showPolygon: boolean; |
111 | - showTooltip: any; | |
117 | + polygonKeyName: string; | |
118 | + polKeyName: string;// deprecated | |
112 | 119 | polygonStrokeOpacity: number; |
113 | 120 | polygonOpacity: number; |
114 | 121 | polygonStrokeWeight: number; |
... | ... | @@ -116,12 +123,13 @@ export type PolygonSettings = { |
116 | 123 | polygonColor: string; |
117 | 124 | showPolygonTooltip: boolean; |
118 | 125 | autocloseTooltip: boolean; |
119 | - tooltipFunction: GenericFunction; | |
120 | 126 | showTooltipAction: string; |
121 | 127 | tooltipAction: { [name: string]: actionsHandler }; |
122 | - tooltipPattern: string; | |
123 | - useTooltipFunction: boolean; | |
128 | + polygonTooltipPattern: string; | |
129 | + usePolygonTooltipFunction: boolean; | |
124 | 130 | polygonClick: { [name: string]: actionsHandler }; |
131 | + usePolygonColorFunction: boolean; | |
132 | + polygonTooltipFunction: GenericFunction; | |
125 | 133 | polygonColorFunction?: GenericFunction; |
126 | 134 | } |
127 | 135 | |
... | ... | @@ -154,6 +162,88 @@ export interface HistorySelectSettings { |
154 | 162 | buttonColor: string; |
155 | 163 | } |
156 | 164 | |
165 | +export type TripAnimationSttings = { | |
166 | + pointColor: string; | |
167 | + pointSize: number; | |
168 | + pointTooltipOnRightPanel: boolean; | |
169 | +} | |
170 | + | |
157 | 171 | export type actionsHandler = ($event: Event, datasource: Datasource) => void; |
158 | 172 | |
159 | -export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings; | |
173 | +export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings & TripAnimationSttings; | |
174 | + | |
175 | +interface IProvider { | |
176 | + MapClass: Type<LeafletMap>, | |
177 | + schema: JsonSettingsSchema, | |
178 | + name: string | |
179 | +} | |
180 | + | |
181 | +export const providerSets: { [key: string]: IProvider } = { | |
182 | + 'openstreet-map': { | |
183 | + MapClass: OpenStreetMap, | |
184 | + schema: openstreetMapSettingsSchema, | |
185 | + name: 'openstreet-map', | |
186 | + }, | |
187 | + 'tencent-map': { | |
188 | + MapClass: TencentMap, | |
189 | + schema: tencentMapSettingsSchema, | |
190 | + name: 'tencent-map' | |
191 | + }, | |
192 | + 'google-map': { | |
193 | + MapClass: GoogleMap, | |
194 | + schema: googleMapSettingsSchema, | |
195 | + name: 'google-map' | |
196 | + }, | |
197 | + here: { | |
198 | + MapClass: HEREMap, | |
199 | + schema: hereMapSettingsSchema, | |
200 | + name: 'here' | |
201 | + }, | |
202 | + 'image-map': { | |
203 | + MapClass: ImageMap, | |
204 | + schema: imageMapSettingsSchema, | |
205 | + name: 'image-map' | |
206 | + } | |
207 | +}; | |
208 | + | |
209 | +export const defaultSettings: any = { | |
210 | + xPosKeyName: 'xPos', | |
211 | + yPosKeyName: 'yPos', | |
212 | + markerOffsetX: 0.5, | |
213 | + markerOffsetY: 1, | |
214 | + latKeyName: 'latitude', | |
215 | + lngKeyName: 'longitude', | |
216 | + polygonKeyName: 'coordinates', | |
217 | + showLabel: false, | |
218 | + label: '${entityName}', | |
219 | + showTooltip: false, | |
220 | + useDefaultCenterPosition: false, | |
221 | + showTooltipAction: 'click', | |
222 | + autocloseTooltip: false, | |
223 | + showPolygon: false, | |
224 | + labelColor: '#000000', | |
225 | + color: '#FE7569', | |
226 | + polygonColor: '#0000ff', | |
227 | + polygonStrokeColor: '#fe0001', | |
228 | + polygonOpacity: 0.5, | |
229 | + polygonStrokeOpacity: 1, | |
230 | + polygonStrokeWeight: 1, | |
231 | + useLabelFunction: false, | |
232 | + markerImages: [], | |
233 | + strokeWeight: 2, | |
234 | + strokeOpacity: 1.0, | |
235 | + initCallback: () => { }, | |
236 | + defaultZoomLevel: 8, | |
237 | + disableScrollZooming: false, | |
238 | + minZoomLevel: 16, | |
239 | + credentials: '', | |
240 | + markerClusteringSetting: null, | |
241 | + draggableMarker: false, | |
242 | + fitMapBounds: true | |
243 | +}; | |
244 | + | |
245 | +export const hereProviders = [ | |
246 | + 'HERE.normalDay', | |
247 | + 'HERE.normalNight', | |
248 | + 'HERE.hybridDay', | |
249 | + 'HERE.terrainDay'] | ... | ... |
... | ... | @@ -26,7 +26,7 @@ export interface MapWidgetInterface { |
26 | 26 | |
27 | 27 | export interface MapWidgetStaticInterface { |
28 | 28 | settingsSchema(mapProvider?: MapProviders, drawRoutes?: boolean): JsonSettingsSchema; |
29 | - getProvidersSchema(mapProvider?: MapProviders): JsonSettingsSchema | |
29 | + getProvidersSchema(mapProvider?: MapProviders, ignoreImageMap?: boolean): JsonSettingsSchema | |
30 | 30 | dataKeySettingsSchema(): object; |
31 | 31 | actionSources(): object; |
32 | 32 | } | ... | ... |
... | ... | @@ -14,23 +14,17 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import { MapProviders, UnitedMapSettings } from './map-models'; | |
17 | +import { MapProviders, UnitedMapSettings, providerSets, hereProviders, defaultSettings } from './map-models'; | |
18 | 18 | import LeafletMap from './leaflet-map'; |
19 | 19 | import { |
20 | - openstreetMapSettingsSchema, | |
21 | - googleMapSettingsSchema, | |
22 | - imageMapSettingsSchema, | |
23 | - tencentMapSettingsSchema, | |
24 | 20 | commonMapSettingsSchema, |
25 | 21 | routeMapSettingsSchema, |
26 | 22 | markerClusteringSettingsSchema, |
27 | 23 | markerClusteringSettingsSchemaLeaflet, |
28 | - hereMapSettingsSchema, | |
29 | 24 | mapProviderSchema, |
30 | 25 | mapPolygonSchema |
31 | 26 | } from './schemes'; |
32 | 27 | import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface'; |
33 | -import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers'; | |
34 | 28 | import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils'; |
35 | 29 | import { of, Subject } from 'rxjs'; |
36 | 30 | import { WidgetContext } from '@app/modules/home/models/widget-component.models'; |
... | ... | @@ -39,7 +33,6 @@ import { JsonSettingsSchema, WidgetActionDescriptor, DatasourceType, widgetType, |
39 | 33 | import { EntityId } from '@shared/models/id/entity-id'; |
40 | 34 | import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; |
41 | 35 | import { AttributeService } from '@core/http/attribute.service'; |
42 | -import { Type } from '@angular/core'; | |
43 | 36 | import { TranslateService } from '@ngx-translate/core'; |
44 | 37 | import { UtilsService } from '@core/services/utils.service'; |
45 | 38 | |
... | ... | @@ -85,11 +78,19 @@ export class MapWidgetController implements MapWidgetInterface { |
85 | 78 | return {}; |
86 | 79 | } |
87 | 80 | |
88 | - public static getProvidersSchema(mapProvider: MapProviders) { | |
89 | - mapProviderSchema.schema.properties.provider.default = mapProvider; | |
90 | - return mergeSchemes([mapProviderSchema, | |
81 | + public static getProvidersSchema(mapProvider: MapProviders, ignoreImageMap = false) { | |
82 | + if (mapProvider) | |
83 | + mapProviderSchema.schema.properties.provider.default = mapProvider; | |
84 | + const providerSchema = mapProviderSchema; | |
85 | + if (ignoreImageMap) { | |
86 | + providerSchema.form[0].items = providerSchema.form[0]?.items.filter(item => item.value !== 'image-map'); | |
87 | + } | |
88 | + return mergeSchemes([providerSchema, | |
91 | 89 | ...Object.keys(providerSets)?.map( |
92 | - (key: string) => { const setting = providerSets[key]; return addCondition(setting?.schema, `model.provider === '${setting.name}'`) })]); | |
90 | + (key: string) => { | |
91 | + const setting = providerSets[key]; | |
92 | + return addCondition(setting?.schema, `model.provider === '${setting.name}'`); | |
93 | + })]); | |
93 | 94 | } |
94 | 95 | |
95 | 96 | public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema { |
... | ... | @@ -218,6 +219,7 @@ export class MapWidgetController implements MapWidgetInterface { |
218 | 219 | polygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams), |
219 | 220 | markerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']), |
220 | 221 | labelColor: this.ctx.widgetConfig.color, |
222 | + polygonKeyName: settings.polKeyName ? settings.polKeyName : settings.polygonKeyName, | |
221 | 223 | tooltipPattern: settings.tooltipPattern || |
222 | 224 | '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${' + |
223 | 225 | settings.latKeyName + ':7}<br/><b>Longitude:</b> ${' + settings.lngKeyName + ':7}', |
... | ... | @@ -295,78 +297,4 @@ export class MapWidgetController implements MapWidgetInterface { |
295 | 297 | |
296 | 298 | export let TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController; |
297 | 299 | |
298 | -interface IProvider { | |
299 | - MapClass: Type<LeafletMap>, | |
300 | - schema: JsonSettingsSchema, | |
301 | - name: string | |
302 | -} | |
303 | - | |
304 | -export const providerSets: { [key: string]: IProvider } = { | |
305 | - 'openstreet-map': { | |
306 | - MapClass: OpenStreetMap, | |
307 | - schema: openstreetMapSettingsSchema, | |
308 | - name: 'openstreet-map', | |
309 | - }, | |
310 | - 'tencent-map': { | |
311 | - MapClass: TencentMap, | |
312 | - schema: tencentMapSettingsSchema, | |
313 | - name: 'tencent-map' | |
314 | - }, | |
315 | - 'google-map': { | |
316 | - MapClass: GoogleMap, | |
317 | - schema: googleMapSettingsSchema, | |
318 | - name: 'google-map' | |
319 | - }, | |
320 | - here: { | |
321 | - MapClass: HEREMap, | |
322 | - schema: hereMapSettingsSchema, | |
323 | - name: 'here' | |
324 | - }, | |
325 | - 'image-map': { | |
326 | - MapClass: ImageMap, | |
327 | - schema: imageMapSettingsSchema, | |
328 | - name: 'image-map' | |
329 | - } | |
330 | -}; | |
331 | - | |
332 | -export const defaultSettings: any = { | |
333 | - xPosKeyName: 'xPos', | |
334 | - yPosKeyName: 'yPos', | |
335 | - markerOffsetX: 0.5, | |
336 | - markerOffsetY: 1, | |
337 | - latKeyName: 'latitude', | |
338 | - lngKeyName: 'longitude', | |
339 | - polygonKeyName: 'coordinates', | |
340 | - showLabel: false, | |
341 | - label: '${entityName}', | |
342 | - showTooltip: false, | |
343 | - useDefaultCenterPosition: false, | |
344 | - showTooltipAction: 'click', | |
345 | - autocloseTooltip: false, | |
346 | - showPolygon: false, | |
347 | - labelColor: '#000000', | |
348 | - color: '#FE7569', | |
349 | - polygonColor: '#0000ff', | |
350 | - polygonStrokeColor: '#fe0001', | |
351 | - polygonOpacity: 0.5, | |
352 | - polygonStrokeOpacity: 1, | |
353 | - polygonStrokeWeight: 1, | |
354 | - useLabelFunction: false, | |
355 | - markerImages: [], | |
356 | - strokeWeight: 2, | |
357 | - strokeOpacity: 1.0, | |
358 | - initCallback: () => { }, | |
359 | - defaultZoomLevel: 8, | |
360 | - disableScrollZooming: false, | |
361 | - minZoomLevel: 16, | |
362 | - credentials: '', | |
363 | - markerClusteringSetting: null, | |
364 | - draggableMarker: false, | |
365 | - fitMapBounds: true | |
366 | -}; | |
367 | 300 | |
368 | -export const hereProviders = [ | |
369 | - 'HERE.normalDay', | |
370 | - 'HERE.normalNight', | |
371 | - 'HERE.hybridDay', | |
372 | - 'HERE.terrainDay'] | ... | ... |
... | ... | @@ -120,7 +120,6 @@ export class Marker { |
120 | 120 | [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage; |
121 | 121 | const currentColor = tinycolor(this.settings.useColorFunction ? safeExecute(this.settings.colorFunction, |
122 | 122 | [this.data, this.dataSources, this.data.dsIndex]) : this.settings.color).toHex(); |
123 | - | |
124 | 123 | if (currentImage && currentImage.url) { |
125 | 124 | aspectCache(currentImage.url).subscribe( |
126 | 125 | (aspect) => { | ... | ... |
... | ... | @@ -53,8 +53,9 @@ export class Polygon { |
53 | 53 | } |
54 | 54 | |
55 | 55 | updateTooltip(data: DatasourceData) { |
56 | - const pattern = this.settings.useTooltipFunction ? | |
57 | - safeExecute(this.settings.tooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern; | |
56 | + const pattern = this.settings.usePolygonTooltipFunction ? | |
57 | + safeExecute(this.settings.polygonTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : | |
58 | + this.settings.polygonTooltipPattern; | |
58 | 59 | this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true)); |
59 | 60 | } |
60 | 61 | |
... | ... | @@ -71,10 +72,12 @@ export class Polygon { |
71 | 72 | this.map.removeLayer(this.leafletPoly); |
72 | 73 | } |
73 | 74 | |
74 | - updatePolygonColor(settings) { | |
75 | + updatePolygonColor(settings: PolygonSettings) { | |
76 | + const color = settings.usePolygonColorFunction ? | |
77 | + safeExecute(settings.polygonColorFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.polygonColor; | |
75 | 78 | const style: L.PathOptions = { |
76 | 79 | fill: true, |
77 | - fillColor: settings.polygonColor, | |
80 | + fillColor: color, | |
78 | 81 | color: settings.polygonStrokeColor, |
79 | 82 | weight: settings.polygonStrokeWeight, |
80 | 83 | fillOpacity: settings.polygonOpacity, | ... | ... |
... | ... | @@ -109,13 +109,13 @@ export class ImageMap extends LeafletMap { |
109 | 109 | lastCenterPos.y /= prevHeight; |
110 | 110 | this.updateBounds(updateImage, lastCenterPos); |
111 | 111 | this.map.invalidateSize(true); |
112 | - // TODO: need add update marker position | |
112 | + this.updateMarkers(this.markersData); | |
113 | 113 | } |
114 | 114 | } |
115 | 115 | } |
116 | 116 | } |
117 | 117 | |
118 | - fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { } | |
118 | + fitBounds(bounds: LatLngBounds, padding?: LatLngTuple) { } | |
119 | 119 | |
120 | 120 | initMap(updateImage?) { |
121 | 121 | if (!this.map && this.aspect > 0) { | ... | ... |
... | ... | @@ -546,6 +546,20 @@ export const mapPolygonSchema = |
546 | 546 | type: 'boolean', |
547 | 547 | default: false |
548 | 548 | }, |
549 | + polygonTooltipPattern: { | |
550 | + title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')', | |
551 | + type: 'string', | |
552 | + default: '<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}' | |
553 | + }, | |
554 | + usePolygonTooltipFunction: { | |
555 | + title: 'Use polygon tooltip function', | |
556 | + type: 'boolean', | |
557 | + default: false | |
558 | + }, | |
559 | + polygonTooltipFunction: { | |
560 | + title: 'Polygon tooltip function: f(data, dsData, dsIndex)', | |
561 | + type: 'string' | |
562 | + }, | |
549 | 563 | usePolygonColorFunction: { |
550 | 564 | title: 'Use polygon color function', |
551 | 565 | type: 'boolean', |
... | ... | @@ -570,7 +584,15 @@ export const mapPolygonSchema = |
570 | 584 | key: 'polygonStrokeColor', |
571 | 585 | type: 'color' |
572 | 586 | }, |
573 | - 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction', 'showPolygonTooltip', | |
587 | + 'polygonStrokeOpacity', 'polygonStrokeWeight', 'showPolygonTooltip', | |
588 | + { | |
589 | + key: 'polygonTooltipPattern', | |
590 | + type: 'textarea' | |
591 | + }, 'usePolygonTooltipFunction', { | |
592 | + key: 'polygonTooltipFunction', | |
593 | + type: 'javascript' | |
594 | + }, | |
595 | + 'usePolygonColorFunction', | |
574 | 596 | { |
575 | 597 | key: 'polygonColorFunction', |
576 | 598 | type: 'javascript' |
... | ... | @@ -710,6 +732,161 @@ export const imageMapSettingsSchema = |
710 | 732 | ] |
711 | 733 | }; |
712 | 734 | |
735 | +export const pathSchema = | |
736 | +{ | |
737 | + schema: { | |
738 | + title: 'Trip Animation Path Configuration', | |
739 | + type: 'object', | |
740 | + properties: { | |
741 | + color: { | |
742 | + title: 'Path color', | |
743 | + type: 'string' | |
744 | + }, | |
745 | + strokeWeight: { | |
746 | + title: 'Stroke weight', | |
747 | + type: 'number', | |
748 | + default: 2 | |
749 | + }, | |
750 | + strokeOpacity: { | |
751 | + title: 'Stroke opacity', | |
752 | + type: 'number', | |
753 | + default: 1 | |
754 | + }, | |
755 | + useColorFunction: { | |
756 | + title: 'Use path color function', | |
757 | + type: 'boolean', | |
758 | + default: false | |
759 | + }, | |
760 | + colorFunction: { | |
761 | + title: 'Path color function: f(data, dsData, dsIndex)', | |
762 | + type: 'string' | |
763 | + }, | |
764 | + usePolylineDecorator: { | |
765 | + title: 'Use path decorator', | |
766 | + type: 'boolean', | |
767 | + default: false | |
768 | + }, | |
769 | + decoratorSymbol: { | |
770 | + title: 'Decorator symbol', | |
771 | + type: 'string', | |
772 | + default: 'arrowHead' | |
773 | + }, | |
774 | + decoratorSymbolSize: { | |
775 | + title: 'Decorator symbol size (px)', | |
776 | + type: 'number', | |
777 | + default: 10 | |
778 | + }, | |
779 | + useDecoratorCustomColor: { | |
780 | + title: 'Use path decorator custom color', | |
781 | + type: 'boolean', | |
782 | + default: false | |
783 | + }, | |
784 | + decoratorCustomColor: { | |
785 | + title: 'Decorator custom color', | |
786 | + type: 'string', | |
787 | + default: '#000' | |
788 | + }, | |
789 | + decoratorOffset: { | |
790 | + title: 'Decorator offset', | |
791 | + type: 'string', | |
792 | + default: '20px' | |
793 | + }, | |
794 | + endDecoratorOffset: { | |
795 | + title: 'End decorator offset', | |
796 | + type: 'string', | |
797 | + default: '20px' | |
798 | + }, | |
799 | + decoratorRepeat: { | |
800 | + title: 'Decorator repeat', | |
801 | + type: 'string', | |
802 | + default: '20px' | |
803 | + } | |
804 | + }, | |
805 | + required: [] | |
806 | + }, | |
807 | + form: [ | |
808 | + { | |
809 | + key: 'color', | |
810 | + type: 'color' | |
811 | + }, 'useColorFunction', { | |
812 | + key: 'colorFunction', | |
813 | + type: 'javascript' | |
814 | + }, 'strokeWeight', 'strokeOpacity', | |
815 | + 'usePolylineDecorator', { | |
816 | + key: 'decoratorSymbol', | |
817 | + type: 'rc-select', | |
818 | + multiple: false, | |
819 | + items: [{ | |
820 | + value: 'arrowHead', | |
821 | + label: 'Arrow' | |
822 | + }, { | |
823 | + value: 'dash', | |
824 | + label: 'Dash' | |
825 | + }] | |
826 | + }, 'decoratorSymbolSize', 'useDecoratorCustomColor', { | |
827 | + key: 'decoratorCustomColor', | |
828 | + type: 'color' | |
829 | + }, { | |
830 | + key: 'decoratorOffset', | |
831 | + type: 'textarea' | |
832 | + }, { | |
833 | + key: 'endDecoratorOffset', | |
834 | + type: 'textarea' | |
835 | + }, { | |
836 | + key: 'decoratorRepeat', | |
837 | + type: 'textarea' | |
838 | + } | |
839 | + ] | |
840 | +}; | |
841 | + | |
842 | +export const pointSchema = | |
843 | +{ | |
844 | + schema: { | |
845 | + title: 'Trip Animation Path Configuration', | |
846 | + type: 'object', | |
847 | + properties: { | |
848 | + showPoints: { | |
849 | + title: 'Show points', | |
850 | + type: 'boolean', | |
851 | + default: false | |
852 | + }, | |
853 | + pointColor: { | |
854 | + title: 'Point color', | |
855 | + type: 'string' | |
856 | + }, | |
857 | + pointSize: { | |
858 | + title: 'Point size (px)', | |
859 | + type: 'number', | |
860 | + default: 10 | |
861 | + }, | |
862 | + usePointAsAnchor: { | |
863 | + title: 'Use point as anchor', | |
864 | + type: 'boolean', | |
865 | + default: false | |
866 | + }, | |
867 | + pointAsAnchorFunction: { | |
868 | + title: 'Point as anchor function: f(data, dsData, dsIndex)', | |
869 | + type: 'string' | |
870 | + }, | |
871 | + pointTooltipOnRightPanel: { | |
872 | + title: 'Independant point tooltip', | |
873 | + type: 'boolean', | |
874 | + default: true | |
875 | + }, | |
876 | + }, | |
877 | + required: [] | |
878 | + }, | |
879 | + form: [ | |
880 | + 'showPoints', { | |
881 | + key: 'pointColor', | |
882 | + type: 'color' | |
883 | + }, 'pointSize', 'usePointAsAnchor', { | |
884 | + key: 'pointAsAnchorFunction', | |
885 | + type: 'javascript' | |
886 | + }, 'pointTooltipOnRightPanel', | |
887 | + ] | |
888 | +}; | |
889 | + | |
713 | 890 | export const mapProviderSchema = |
714 | 891 | { |
715 | 892 | schema: { |
... | ... | @@ -755,7 +932,6 @@ export const mapProviderSchema = |
755 | 932 | ] |
756 | 933 | }; |
757 | 934 | |
758 | - | |
759 | 935 | export const tripAnimationSchema = { |
760 | 936 | schema: { |
761 | 937 | title: 'Openstreet Map Configuration', |
... | ... | @@ -776,11 +952,6 @@ export const tripAnimationSchema = { |
776 | 952 | type: 'string', |
777 | 953 | default: 'longitude' |
778 | 954 | }, |
779 | - polKeyName: { | |
780 | - title: 'Polygon key name', | |
781 | - type: 'string', | |
782 | - default: 'coordinates' | |
783 | - }, | |
784 | 955 | showLabel: { |
785 | 956 | title: 'Show label', |
786 | 957 | type: 'boolean', |
... | ... | @@ -834,148 +1005,6 @@ export const tripAnimationSchema = { |
834 | 1005 | title: 'Tooltip function: f(data, dsData, dsIndex)', |
835 | 1006 | type: 'string' |
836 | 1007 | }, |
837 | - color: { | |
838 | - title: 'Path color', | |
839 | - type: 'string' | |
840 | - }, | |
841 | - strokeWeight: { | |
842 | - title: 'Stroke weight', | |
843 | - type: 'number', | |
844 | - default: 2 | |
845 | - }, | |
846 | - strokeOpacity: { | |
847 | - title: 'Stroke opacity', | |
848 | - type: 'number', | |
849 | - default: 1 | |
850 | - }, | |
851 | - useColorFunction: { | |
852 | - title: 'Use path color function', | |
853 | - type: 'boolean', | |
854 | - default: false | |
855 | - }, | |
856 | - colorFunction: { | |
857 | - title: 'Path color function: f(data, dsData, dsIndex)', | |
858 | - type: 'string' | |
859 | - }, | |
860 | - usePolylineDecorator: { | |
861 | - title: 'Use path decorator', | |
862 | - type: 'boolean', | |
863 | - default: false | |
864 | - }, | |
865 | - decoratorSymbol: { | |
866 | - title: 'Decorator symbol', | |
867 | - type: 'string', | |
868 | - default: 'arrowHead' | |
869 | - }, | |
870 | - decoratorSymbolSize: { | |
871 | - title: 'Decorator symbol size (px)', | |
872 | - type: 'number', | |
873 | - default: 10 | |
874 | - }, | |
875 | - useDecoratorCustomColor: { | |
876 | - title: 'Use path decorator custom color', | |
877 | - type: 'boolean', | |
878 | - default: false | |
879 | - }, | |
880 | - decoratorCustomColor: { | |
881 | - title: 'Decorator custom color', | |
882 | - type: 'string', | |
883 | - default: '#000' | |
884 | - }, | |
885 | - decoratorOffset: { | |
886 | - title: 'Decorator offset', | |
887 | - type: 'string', | |
888 | - default: '20px' | |
889 | - }, | |
890 | - endDecoratorOffset: { | |
891 | - title: 'End decorator offset', | |
892 | - type: 'string', | |
893 | - default: '20px' | |
894 | - }, | |
895 | - decoratorRepeat: { | |
896 | - title: 'Decorator repeat', | |
897 | - type: 'string', | |
898 | - default: '20px' | |
899 | - }, | |
900 | - showPolygon: { | |
901 | - title: 'Show polygon', | |
902 | - type: 'boolean', | |
903 | - default: false | |
904 | - }, | |
905 | - polygonTooltipPattern: { | |
906 | - title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')', | |
907 | - type: 'string', | |
908 | - default: '<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}' | |
909 | - }, | |
910 | - usePolygonTooltipFunction: { | |
911 | - title: 'Use polygon tooltip function', | |
912 | - type: 'boolean', | |
913 | - default: false | |
914 | - }, | |
915 | - polygonTooltipFunction: { | |
916 | - title: 'Polygon tooltip function: f(data, dsData, dsIndex)', | |
917 | - type: 'string' | |
918 | - }, | |
919 | - polygonColor: { | |
920 | - title: 'Polygon color', | |
921 | - type: 'string' | |
922 | - }, | |
923 | - polygonOpacity: { | |
924 | - title: 'Polygon opacity', | |
925 | - type: 'number', | |
926 | - default: 0.5 | |
927 | - }, | |
928 | - polygonStrokeColor: { | |
929 | - title: 'Polygon border color', | |
930 | - type: 'string' | |
931 | - }, | |
932 | - polygonStrokeOpacity: { | |
933 | - title: 'Polygon border opacity', | |
934 | - type: 'number', | |
935 | - default: 1 | |
936 | - }, | |
937 | - polygonStrokeWeight: { | |
938 | - title: 'Polygon border weight', | |
939 | - type: 'number', | |
940 | - default: 1 | |
941 | - }, | |
942 | - usePolygonColorFunction: { | |
943 | - title: 'Use polygon color function', | |
944 | - type: 'boolean', | |
945 | - default: false | |
946 | - }, | |
947 | - polygonColorFunction: { | |
948 | - title: 'Polygon Color function: f(data, dsData, dsIndex)', | |
949 | - type: 'string' | |
950 | - }, | |
951 | - showPoints: { | |
952 | - title: 'Show points', | |
953 | - type: 'boolean', | |
954 | - default: false | |
955 | - }, | |
956 | - pointColor: { | |
957 | - title: 'Point color', | |
958 | - type: 'string' | |
959 | - }, | |
960 | - pointSize: { | |
961 | - title: 'Point size (px)', | |
962 | - type: 'number', | |
963 | - default: 10 | |
964 | - }, | |
965 | - usePointAsAnchor: { | |
966 | - title: 'Use point as anchor', | |
967 | - type: 'boolean', | |
968 | - default: false | |
969 | - }, | |
970 | - pointAsAnchorFunction: { | |
971 | - title: 'Point as anchor function: f(data, dsData, dsIndex)', | |
972 | - type: 'string' | |
973 | - }, | |
974 | - pointTooltipOnRightPanel: { | |
975 | - title: 'Independant point tooltip', | |
976 | - type: 'boolean', | |
977 | - default: true | |
978 | - }, | |
979 | 1008 | autocloseTooltip: { |
980 | 1009 | title: 'Auto-close point popup', |
981 | 1010 | type: 'boolean', |
... | ... | @@ -1015,111 +1044,35 @@ export const tripAnimationSchema = { |
1015 | 1044 | }, |
1016 | 1045 | required: [] |
1017 | 1046 | }, |
1018 | - form: [{ | |
1019 | - key: 'mapProvider', | |
1020 | - type: 'rc-select', | |
1021 | - multiple: false, | |
1022 | - items: [{ | |
1023 | - value: 'OpenStreetMap.Mapnik', | |
1024 | - label: 'OpenStreetMap.Mapnik (Default)' | |
1025 | - }, { | |
1026 | - value: 'OpenStreetMap.BlackAndWhite', | |
1027 | - label: 'OpenStreetMap.BlackAndWhite' | |
1028 | - }, { | |
1029 | - value: 'OpenStreetMap.HOT', | |
1030 | - label: 'OpenStreetMap.HOT' | |
1031 | - }, { | |
1032 | - value: 'Esri.WorldStreetMap', | |
1033 | - label: 'Esri.WorldStreetMap' | |
1034 | - }, { | |
1035 | - value: 'Esri.WorldTopoMap', | |
1036 | - label: 'Esri.WorldTopoMap' | |
1037 | - }, { | |
1038 | - value: 'CartoDB.Positron', | |
1039 | - label: 'CartoDB.Positron' | |
1040 | - }, { | |
1041 | - value: 'CartoDB.DarkMatter', | |
1042 | - label: 'CartoDB.DarkMatter' | |
1043 | - }] | |
1044 | - }, 'normalizationStep', 'latKeyName', 'lngKeyName', 'polKeyName', 'showLabel', 'label', 'useLabelFunction', { | |
1047 | + form: ['normalizationStep', 'latKeyName', 'lngKeyName', 'showLabel', 'label', 'useLabelFunction', { | |
1045 | 1048 | key: 'labelFunction', |
1046 | 1049 | type: 'javascript' |
1047 | 1050 | }, 'showTooltip', { |
1048 | - key: 'tooltipColor', | |
1049 | - type: 'color' | |
1050 | - }, { | |
1051 | - key: 'tooltipFontColor', | |
1052 | - type: 'color' | |
1053 | - }, 'tooltipOpacity', { | |
1054 | - key: 'tooltipPattern', | |
1055 | - type: 'textarea' | |
1056 | - }, 'useTooltipFunction', { | |
1057 | - key: 'tooltipFunction', | |
1058 | - type: 'javascript' | |
1059 | - }, { | |
1060 | - key: 'color', | |
1061 | - type: 'color' | |
1062 | - }, 'useColorFunction', { | |
1063 | - key: 'colorFunction', | |
1064 | - type: 'javascript' | |
1065 | - }, 'usePolylineDecorator', { | |
1066 | - key: 'decoratorSymbol', | |
1067 | - type: 'rc-select', | |
1068 | - multiple: false, | |
1069 | - items: [{ | |
1070 | - value: 'arrowHead', | |
1071 | - label: 'Arrow' | |
1051 | + key: 'tooltipColor', | |
1052 | + type: 'color' | |
1053 | + }, { | |
1054 | + key: 'tooltipFontColor', | |
1055 | + type: 'color' | |
1056 | + }, 'tooltipOpacity', { | |
1057 | + key: 'tooltipPattern', | |
1058 | + type: 'textarea' | |
1059 | + }, 'useTooltipFunction', { | |
1060 | + key: 'tooltipFunction', | |
1061 | + type: 'javascript' | |
1062 | + }, 'autocloseTooltip', { | |
1063 | + key: 'markerImage', | |
1064 | + type: 'image' | |
1065 | + }, 'markerImageSize', 'rotationAngle', 'useMarkerImageFunction', | |
1066 | + { | |
1067 | + key: 'markerImageFunction', | |
1068 | + type: 'javascript' | |
1072 | 1069 | }, { |
1073 | - value: 'dash', | |
1074 | - label: 'Dash' | |
1070 | + key: 'markerImages', | |
1071 | + items: [ | |
1072 | + { | |
1073 | + key: 'markerImages[]', | |
1074 | + type: 'image' | |
1075 | + } | |
1076 | + ] | |
1075 | 1077 | }] |
1076 | - }, 'decoratorSymbolSize', 'useDecoratorCustomColor', { | |
1077 | - key: 'decoratorCustomColor', | |
1078 | - type: 'color' | |
1079 | - }, { | |
1080 | - key: 'decoratorOffset', | |
1081 | - type: 'textarea' | |
1082 | - }, { | |
1083 | - key: 'endDecoratorOffset', | |
1084 | - type: 'textarea' | |
1085 | - }, { | |
1086 | - key: 'decoratorRepeat', | |
1087 | - type: 'textarea' | |
1088 | - }, 'strokeWeight', 'strokeOpacity', 'showPolygon', { | |
1089 | - key: 'polygonTooltipPattern', | |
1090 | - type: 'textarea' | |
1091 | - }, 'usePolygonTooltipFunction', { | |
1092 | - key: 'polygonTooltipFunction', | |
1093 | - type: 'javascript' | |
1094 | - }, { | |
1095 | - key: 'polygonColor', | |
1096 | - type: 'color' | |
1097 | - }, 'polygonOpacity', { | |
1098 | - key: 'polygonStrokeColor', | |
1099 | - type: 'color' | |
1100 | - }, 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction', { | |
1101 | - key: 'polygonColorFunction', | |
1102 | - type: 'javascript' | |
1103 | - }, 'showPoints', { | |
1104 | - key: 'pointColor', | |
1105 | - type: 'color' | |
1106 | - }, 'pointSize', 'usePointAsAnchor', { | |
1107 | - key: 'pointAsAnchorFunction', | |
1108 | - type: 'javascript' | |
1109 | - }, 'pointTooltipOnRightPanel', 'autocloseTooltip', { | |
1110 | - key: 'markerImage', | |
1111 | - type: 'image' | |
1112 | - }, 'markerImageSize', 'rotationAngle', 'useMarkerImageFunction', | |
1113 | - { | |
1114 | - key: 'markerImageFunction', | |
1115 | - type: 'javascript' | |
1116 | - }, { | |
1117 | - key: 'markerImages', | |
1118 | - items: [ | |
1119 | - { | |
1120 | - key: 'markerImages[]', | |
1121 | - type: 'image' | |
1122 | - } | |
1123 | - ] | |
1124 | - }] | |
1125 | 1078 | } | ... | ... |
... | ... | @@ -32,6 +32,6 @@ |
32 | 32 | [ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}"> |
33 | 33 | </div> |
34 | 34 | </div> |
35 | - <tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals" | |
35 | + <tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals" [anchors]="anchors" [useAnchors]="useAnchors" | |
36 | 36 | (timeUpdated)="timeUpdated($event)"></tb-history-selector> |
37 | 37 | </div> |
\ No newline at end of file | ... | ... |
... | ... | @@ -21,9 +21,9 @@ import { interpolateOnPointSegment } from 'leaflet-geometryutil'; |
21 | 21 | |
22 | 22 | import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core'; |
23 | 23 | import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2'; |
24 | -import { MapProviders } from '../lib/maps/map-models'; | |
25 | -import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils'; | |
26 | -import { tripAnimationSchema } from '../lib/maps/schemes'; | |
24 | +import { MapProviders, FormattedData } from '../lib/maps/map-models'; | |
25 | +import { initSchema, addToSchema, addGroupInfo, addCondition } from '@app/core/schema-utils'; | |
26 | +import { tripAnimationSchema, mapPolygonSchema, pathSchema, pointSchema } from '../lib/maps/schemes'; | |
27 | 27 | import { DomSanitizer } from '@angular/platform-browser'; |
28 | 28 | import { WidgetContext } from '@app/modules/home/models/widget-component.models'; |
29 | 29 | import { findAngle, getRatio, parseArray, parseWithTranslation, safeExecute } from '../lib/maps/maps-utils'; |
... | ... | @@ -58,13 +58,21 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { |
58 | 58 | label; |
59 | 59 | minTime; |
60 | 60 | maxTime; |
61 | + anchors = []; | |
62 | + useAnchors = false; | |
61 | 63 | |
62 | 64 | static getSettingsSchema(): JsonSettingsSchema { |
63 | 65 | const schema = initSchema(); |
64 | - addToSchema(schema, TbMapWidgetV2.getProvidersSchema()); | |
66 | + addToSchema(schema, TbMapWidgetV2.getProvidersSchema(null, true)); | |
65 | 67 | addGroupInfo(schema, 'Map Provider Settings'); |
66 | 68 | addToSchema(schema, tripAnimationSchema); |
67 | 69 | addGroupInfo(schema, 'Trip Animation Settings'); |
70 | + addToSchema(schema, pathSchema); | |
71 | + addGroupInfo(schema, 'Path Settings'); | |
72 | + addToSchema(schema, addCondition(pointSchema, 'model.showPoints === true', ['showPoints'])); | |
73 | + addGroupInfo(schema, 'Path Points Settings'); | |
74 | + addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon'])); | |
75 | + addGroupInfo(schema, 'Polygon Settings'); | |
68 | 76 | return schema; |
69 | 77 | } |
70 | 78 | |
... | ... | @@ -78,14 +86,15 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { |
78 | 86 | rotationAngle: 0 |
79 | 87 | } |
80 | 88 | this.settings = { ...settings, ...this.ctx.settings }; |
89 | + this.useAnchors = this.settings.usePointAsAnchor && this.settings.showPoints; | |
90 | + this.settings.fitMapBounds = true; | |
91 | + this.normalizationStep = this.settings.normalizationStep; | |
81 | 92 | const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]]; |
82 | - if (subscription) subscription.callbacks.onDataUpdated = (updated) => { | |
93 | + if (subscription) subscription.callbacks.onDataUpdated = () => { | |
83 | 94 | this.historicalData = parseArray(this.ctx.data); |
84 | 95 | this.activeTrip = this.historicalData[0][0]; |
85 | 96 | this.calculateIntervals(); |
86 | 97 | this.timeUpdated(this.intervals[0]); |
87 | - this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds))); | |
88 | - | |
89 | 98 | this.mapWidget.map.map?.invalidateSize(); |
90 | 99 | this.cd.detectChanges(); |
91 | 100 | } |
... | ... | @@ -104,9 +113,17 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { |
104 | 113 | this.calcLabel(); |
105 | 114 | this.calcTooltip(); |
106 | 115 | if (this.mapWidget) { |
116 | + this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds))); | |
107 | 117 | if (this.settings.showPolygon) { |
108 | 118 | this.mapWidget.map.updatePolygons(this.interpolatedData); |
109 | 119 | } |
120 | + if (this.settings.showPoints) { | |
121 | + this.mapWidget.map.updatePoints(this.historicalData[0], this.calcTooltip); | |
122 | + this.anchors = this.historicalData[0] | |
123 | + .filter(data => | |
124 | + this.settings.usePointAsAnchor || | |
125 | + safeExecute(this.settings.pointAsAnchorFunction, [this.historicalData, data, data.dsIndex])).map(data => data.time); | |
126 | + } | |
110 | 127 | this.mapWidget.map.updateMarkers(currentPosition); |
111 | 128 | } |
112 | 129 | } |
... | ... | @@ -117,23 +134,29 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { |
117 | 134 | calculateIntervals() { |
118 | 135 | this.historicalData.forEach((dataSource, index) => { |
119 | 136 | this.intervals = []; |
120 | - | |
121 | 137 | for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) { |
122 | 138 | this.intervals.push(time); |
123 | 139 | } |
124 | - | |
125 | 140 | this.intervals.push(dataSource[dataSource.length - 1]?.time); |
126 | 141 | this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals); |
127 | 142 | }); |
128 | 143 | |
129 | 144 | } |
130 | 145 | |
131 | - calcTooltip() { | |
132 | - const data = { ...this.activeTrip, maxTime: this.maxTime, minTime: this.minTime } | |
133 | - const tooltipText: string = this.settings.useTooltipFunction ? | |
146 | + calcTooltip = (point?: FormattedData, setTooltip = true) => { | |
147 | + if (!point) { | |
148 | + point = this.activeTrip; | |
149 | + } | |
150 | + const data = { ...point, maxTime: this.maxTime, minTime: this.minTime } | |
151 | + const tooltipPattern: string = this.settings.useTooltipFunction ? | |
134 | 152 | safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, 0]) : this.settings.tooltipPattern; |
135 | - this.mainTooltip = this.sanitizer.sanitize( | |
136 | - SecurityContext.HTML, (parseWithTranslation.parseTemplate(tooltipText, data, true))); | |
153 | + const tooltipText = parseWithTranslation.parseTemplate(tooltipPattern, data, true); | |
154 | + if (setTooltip) { | |
155 | + this.mainTooltip = this.sanitizer.sanitize( | |
156 | + SecurityContext.HTML, tooltipText); | |
157 | + this.cd.detectChanges(); | |
158 | + } | |
159 | + return tooltipText; | |
137 | 160 | } |
138 | 161 | |
139 | 162 | calcLabel() { | ... | ... |
... | ... | @@ -28,6 +28,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges { |
28 | 28 | |
29 | 29 | @Input() settings: HistorySelectSettings |
30 | 30 | @Input() intervals = []; |
31 | + @Input() anchors = []; | |
32 | + @Input() useAnchors = false; | |
31 | 33 | |
32 | 34 | @Output() timeUpdated: EventEmitter<number> = new EventEmitter(); |
33 | 35 | |
... | ... | @@ -56,7 +58,7 @@ export class HistorySelectorComponent implements OnInit, OnChanges { |
56 | 58 | this.interval = interval(1000 / this.speed) |
57 | 59 | .pipe( |
58 | 60 | filter(() => this.playing)).subscribe(() => { |
59 | - this.index++; | |
61 | + this.index++; | |
60 | 62 | if (this.index < this.maxTimeIndex) { |
61 | 63 | this.cd.detectChanges(); |
62 | 64 | this.timeUpdated.emit(this.intervals[this.index]); |
... | ... | @@ -91,14 +93,24 @@ export class HistorySelectorComponent implements OnInit, OnChanges { |
91 | 93 | |
92 | 94 | moveNext() { |
93 | 95 | if (this.index < this.maxTimeIndex) { |
94 | - this.index++; | |
96 | + if (this.useAnchors) { | |
97 | + const anchorIndex = this.findIndex(this.intervals[this.index], this.anchors)+1; | |
98 | + this.index = this.findIndex(this.anchors[anchorIndex], this.intervals); | |
99 | + } | |
100 | + else | |
101 | + this.index++; | |
95 | 102 | } |
96 | 103 | this.pause(); |
97 | 104 | } |
98 | 105 | |
99 | 106 | movePrev() { |
100 | 107 | if (this.index > this.minTimeIndex) { |
101 | - this.index++; | |
108 | + if (this.useAnchors) { | |
109 | + const anchorIndex = this.findIndex(this.intervals[this.index], this.anchors) - 1; | |
110 | + this.index = this.findIndex(this.anchors[anchorIndex], this.intervals); | |
111 | + } | |
112 | + else | |
113 | + this.index--; | |
102 | 114 | } |
103 | 115 | this.pause(); |
104 | 116 | } |
... | ... | @@ -113,6 +125,14 @@ export class HistorySelectorComponent implements OnInit, OnChanges { |
113 | 125 | this.pause(); |
114 | 126 | } |
115 | 127 | |
128 | + findIndex(value, array: any[]) { | |
129 | + let i = 0; | |
130 | + while (array[i] < value) { | |
131 | + i++; | |
132 | + }; | |
133 | + return i; | |
134 | + } | |
135 | + | |
116 | 136 | changeIndex() { |
117 | 137 | this.timeUpdated.emit(this.intervals[this.index]); |
118 | 138 | } | ... | ... |