proximiio-js-library 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -120,7 +120,17 @@ const map = new Proximiio.Map({
120
120
  considerVisibilityParam: false, // optional, default: true, if enabled all pois with visibility property defined as 'hidden' will not be visible as default, will be possible to toggle them with toggleHiddenPois() method
121
121
  fitBoundsPadding: 200, // optional, default 250, number | PaddingOptions, the amount of padding in pixels to add to the given bounds for found route, https://docs.mapbox.com/mapbox-gl-js/api/properties/#paddingoptions
122
122
  showLevelDirectionIcon: false // optional, default: false, if enabled arrow icon will be shown at the levelchanger indicating direction of level change along the found route,
123
- showRasterFloorplans: false // optional, default: false, if enabled raster floorplans will be visible
123
+ showRasterFloorplans: false // optional, default: false, if enabled raster floorplans will be visible,
124
+ animatedRoute: false // optional, default: false, EXPERIMENTAL, if enabled animated dot will be displayed along the route,
125
+ useRasterTiles: false, // optional, default: false, this will add raster tile source and layer with defined options from rasterTilesOptions
126
+ rasterTilesOptions: {
127
+ tilesUrl: string[], mandatory
128
+ tileSize: number, optional, default: 256,
129
+ minZoom: number, optional, default: 15,
130
+ maxZoom: number, optional, default: 22,
131
+ beforeLayer: string, optional, default: 'proximiio-shop',
132
+ attribution: string, optional
133
+ },
124
134
  });
125
135
  ```
126
136
  #### Required Data for 3D Polygons
@@ -289,6 +299,36 @@ map.getMapReadyListener().subscribe(ready => {
289
299
  });
290
300
  ```
291
301
 
302
+ ###### by coords
303
+ This method will generate route based on attached coords.
304
+ ```
305
+ // @param latTo {number} finish latitude coordinate
306
+ // @param lngTo {number} finish longitude coordinate
307
+ // @param levelTo {number} finish level
308
+ // @param latFrom {number} start latitude coordinate, optional for kiosk
309
+ // @param lngFrom {number} start longitude coordinate, optional for kiosk
310
+ // @param levelFrom {number} start level, optional for kiosk
311
+ // @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
312
+
313
+ map.getMapReadyListener().subscribe(ready => {
314
+ console.log('map ready', ready);
315
+ map.findRouteByCoords(48.606703739771774, 17.833092384506614, 0, 48.60684545080579, 17.833450676669543, 0);
316
+ });
317
+ ```
318
+
319
+ ###### by nearest amenity feature
320
+ This method will generate route based on nearest amenity feature.
321
+ ```
322
+ // @param amenityId {string} amenity id of a nearest feature to look for
323
+ // @param idFrom {string} start feature id, optional for kiosk
324
+ // @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
325
+
326
+ map.getMapReadyListener().subscribe(ready => {
327
+ console.log('map ready', ready);
328
+ map.findRouteToNearestFeature('amenityId');
329
+ });
330
+ ```
331
+
292
332
  ##### Cancel Route
293
333
  Use this method to cancel generated route.
294
334
  ```
@@ -45,6 +45,16 @@ interface Options {
45
45
  fitBoundsPadding?: number | PaddingOptions;
46
46
  showLevelDirectionIcon?: boolean;
47
47
  showRasterFloorplans?: boolean;
48
+ animatedRoute?: boolean;
49
+ useRasterTiles?: boolean;
50
+ rasterTilesOptions?: {
51
+ tilesUrl: string[];
52
+ tileSize?: number;
53
+ minZoom?: number;
54
+ maxZoom?: number;
55
+ beforeLayer?: string;
56
+ attribution?: string;
57
+ };
48
58
  }
49
59
  interface PaddingOptions {
50
60
  bottom: number;
@@ -92,6 +102,8 @@ export declare class Map {
92
102
  private initKiosk;
93
103
  private onSetKiosk;
94
104
  private initDirectionIcon;
105
+ private initAnimatedRoute;
106
+ private initRasterTiles;
95
107
  private initPolygons;
96
108
  private onShopClick;
97
109
  handlePolygonSelection(poi?: Feature): void;
@@ -132,6 +144,14 @@ export declare class Map {
132
144
  private updateImages;
133
145
  private getUpcomingFloorNumber;
134
146
  private addDirectionFeatures;
147
+ private counter;
148
+ private arc;
149
+ private steps;
150
+ private animationInstances;
151
+ private addAnimatedRouteFeatures;
152
+ private animate;
153
+ private cancelAnimation;
154
+ private getClosestFeature;
135
155
  /**
136
156
  * @memberof Map
137
157
  * @name getMapboxInstance
@@ -281,6 +301,21 @@ export declare class Map {
281
301
  * });
282
302
  */
283
303
  findRouteByCoords(latTo: number, lngTo: number, levelTo: number, latFrom?: number, lngFrom?: number, levelFrom?: number, accessibleRoute?: boolean): void;
304
+ /**
305
+ * This method will generate route to nearest amenity feature
306
+ * @memberof Map
307
+ * @name findRouteToNearestFeature
308
+ * @param amenityId {string} amenity id of a nearest feature to look for
309
+ * @param idFrom {string} start feature id, optional for kiosk
310
+ * @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
311
+ * @example
312
+ * const map = new Proximiio.Map();
313
+ * map.getMapReadyListener().subscribe(ready => {
314
+ * console.log('map ready', ready);
315
+ * map.findRouteToNearestFeature('amenityId');
316
+ * });
317
+ */
318
+ findRouteToNearestFeature(amenityId: string, idFrom?: string, accessibleRoute?: boolean): void;
284
319
  /**
285
320
  * This method will cancel generated route
286
321
  * @memberof Map
@@ -128,12 +128,23 @@ var Map = /** @class */ (function () {
128
128
  fitBoundsPadding: 250,
129
129
  showLevelDirectionIcon: false,
130
130
  showRasterFloorplans: false,
131
+ animatedRoute: false,
132
+ useRasterTiles: false,
131
133
  };
132
134
  this.showStartPoint = false;
133
135
  this.amenityIds = [];
134
136
  this.filteredAmenities = [];
135
137
  this.amenityFilters = [];
136
138
  this.amenityCategories = {};
139
+ // Used to increment the value of the point measurement against the route.
140
+ this.counter = 0;
141
+ // store route part animation points
142
+ this.arc = {};
143
+ // Number of steps to use in the arc and animation, more steps means
144
+ // a smoother arc and animation, but too many steps will result in a
145
+ // low frame rate
146
+ this.steps = 300;
147
+ this.animationInstances = [];
137
148
  // fix centering in case of kiosk with defined pitch/bearing/etc. in mapbox options
138
149
  if (options.isKiosk && options.mapboxOptions && options.kioskSettings && !options.mapboxOptions.center) {
139
150
  options.mapboxOptions.center = options.kioskSettings.coordinates;
@@ -297,6 +308,12 @@ var Map = /** @class */ (function () {
297
308
  if (this.defaultOptions.showLevelDirectionIcon) {
298
309
  this.initDirectionIcon();
299
310
  }
311
+ if (this.defaultOptions.animatedRoute) {
312
+ this.initAnimatedRoute();
313
+ }
314
+ if (this.defaultOptions.useRasterTiles) {
315
+ this.initRasterTiles();
316
+ }
300
317
  this.initPersonsMap();
301
318
  this.map.on('click', 'proximiio-pois-icons', function (ev) {
302
319
  _this.onShopClick(ev);
@@ -386,6 +403,54 @@ var Map = /** @class */ (function () {
386
403
  this.map.setStyle(this.state.style);
387
404
  }
388
405
  };
406
+ Map.prototype.initAnimatedRoute = function () {
407
+ if (this.map) {
408
+ this.state.style.addSource('route-point', {
409
+ type: 'geojson',
410
+ data: {
411
+ type: 'FeatureCollection',
412
+ features: [],
413
+ },
414
+ });
415
+ this.state.style.addLayer({
416
+ id: 'route-point',
417
+ type: 'circle',
418
+ source: 'route-point',
419
+ minzoom: 17,
420
+ maxzoom: 24,
421
+ paint: {
422
+ 'circle-color': '#1d8a9f',
423
+ 'circle-radius': 10,
424
+ },
425
+ filter: ['all', ['==', ['to-number', ['get', 'level']], this.state.floor.level]],
426
+ });
427
+ this.map.setStyle(this.state.style);
428
+ }
429
+ };
430
+ Map.prototype.initRasterTiles = function () {
431
+ if (this.map) {
432
+ this.state.style.addSource('raster-tiles', {
433
+ type: 'raster',
434
+ tiles: this.defaultOptions.rasterTilesOptions.tilesUrl,
435
+ tileSize: this.defaultOptions.rasterTilesOptions.tileSize
436
+ ? this.defaultOptions.rasterTilesOptions.tileSize
437
+ : 256,
438
+ attribution: this.defaultOptions.rasterTilesOptions.attribution
439
+ ? this.defaultOptions.rasterTilesOptions.attribution
440
+ : '',
441
+ });
442
+ this.state.style.addLayer({
443
+ id: 'raster-tiles',
444
+ type: 'raster',
445
+ source: 'raster-tiles',
446
+ minzoom: this.defaultOptions.rasterTilesOptions.minZoom ? this.defaultOptions.rasterTilesOptions.minZoom : 15,
447
+ maxzoom: this.defaultOptions.rasterTilesOptions.maxZoom ? this.defaultOptions.rasterTilesOptions.maxZoom : 22,
448
+ }, this.defaultOptions.rasterTilesOptions.maxZoom
449
+ ? this.defaultOptions.rasterTilesOptions.beforeLayer
450
+ : 'proximiio-shop');
451
+ this.map.setStyle(this.state.style);
452
+ }
453
+ };
389
454
  Map.prototype.initPolygons = function () {
390
455
  var _this = this;
391
456
  if (this.map) {
@@ -444,19 +509,21 @@ var Map = /** @class */ (function () {
444
509
  }
445
510
  if (connectedPolygonId) {
446
511
  this.selectedPolygon = this.state.allFeatures.features.find(function (i) { return i.properties.id === connectedPolygonId; });
447
- this.map.setFeatureState({
448
- source: 'main',
449
- id: this.selectedPolygon.id,
450
- }, {
451
- selected: true,
452
- });
453
- if (this.selectedPolygon.properties.label_id) {
512
+ if (this.selectedPolygon) {
454
513
  this.map.setFeatureState({
455
514
  source: 'main',
456
- id: this.selectedPolygon.properties.label_id,
515
+ id: this.selectedPolygon.id,
457
516
  }, {
458
517
  selected: true,
459
518
  });
519
+ if (this.selectedPolygon.properties.label_id) {
520
+ this.map.setFeatureState({
521
+ source: 'main',
522
+ id: this.selectedPolygon.properties.label_id,
523
+ }, {
524
+ selected: true,
525
+ });
526
+ }
460
527
  }
461
528
  }
462
529
  };
@@ -910,11 +977,14 @@ var Map = /** @class */ (function () {
910
977
  if (this.routingSource.route) {
911
978
  var routeStart = this.routingSource.lines[0];
912
979
  var textNavigation = this.routeFactory.generateRoute(JSON.stringify(this.routingSource.points), JSON.stringify(this.endPoint));
913
- this.centerOnRoute(routeStart);
980
+ this.state = __assign(__assign({}, this.state), { loadingRoute: false, textNavigation: textNavigation });
914
981
  if (this.defaultOptions.showLevelDirectionIcon) {
915
982
  this.addDirectionFeatures();
916
983
  }
917
- this.state = __assign(__assign({}, this.state), { loadingRoute: false, textNavigation: textNavigation });
984
+ if (this.defaultOptions.animatedRoute) {
985
+ this.addAnimatedRouteFeatures();
986
+ }
987
+ this.centerOnRoute(routeStart);
918
988
  this.onRouteFoundListener.next({
919
989
  route: this.routingSource.route,
920
990
  TBTNav: this.defaultOptions.enableTBTNavigation ? textNavigation : null,
@@ -1097,13 +1167,20 @@ var Map = /** @class */ (function () {
1097
1167
  });
1098
1168
  if (route) {
1099
1169
  var routePoints = helpers_1.lineString(this.routingSource.levelPoints[floor.level].map(function (i) { return i.geometry.coordinates; }));
1170
+ var lengthInMeters = turf.length(routePoints, { units: 'kilometers' }) * 1000;
1100
1171
  var bbox = turf.bbox(routePoints);
1101
- // @ts-ignore;
1102
- map.fitBounds(bbox, {
1103
- padding: this.defaultOptions.fitBoundsPadding,
1104
- bearing: this.map.getBearing(),
1105
- pitch: this.map.getPitch(),
1106
- });
1172
+ if (lengthInMeters > 15) {
1173
+ // @ts-ignore;
1174
+ map.fitBounds(bbox, {
1175
+ padding: lengthInMeters < 80 ? 400 : this.defaultOptions.fitBoundsPadding,
1176
+ bearing: this.map.getBearing(),
1177
+ pitch: this.map.getPitch(),
1178
+ });
1179
+ }
1180
+ else {
1181
+ // @ts-ignore
1182
+ this.map.flyTo({ center: turf.center(routePoints).geometry.coordinates });
1183
+ }
1107
1184
  }
1108
1185
  if (this.defaultOptions.isKiosk && map.getLayer('my-location-layer')) {
1109
1186
  var filter = ['all', ['==', ['to-number', ['get', 'level']], floor.level]];
@@ -1120,6 +1197,11 @@ var Map = /** @class */ (function () {
1120
1197
  map.setFilter('direction-icon-layer', filter);
1121
1198
  this.state.style.getLayer('direction-icon-layer').filter = filter;
1122
1199
  }
1200
+ if (this.defaultOptions.animatedRoute && map.getLayer('route-point')) {
1201
+ var filter = ['all', ['==', ['to-number', ['get', 'level']], floor.level]];
1202
+ map.setFilter('route-point', filter);
1203
+ this.state.style.getLayer('route-point').filter = filter;
1204
+ }
1123
1205
  }
1124
1206
  this.state = __assign(__assign({}, this.state), { floor: floor, style: this.state.style });
1125
1207
  this.updateCluster();
@@ -1132,6 +1214,9 @@ var Map = /** @class */ (function () {
1132
1214
  if (finish && this.defaultOptions.initPolygons) {
1133
1215
  this.handlePolygonSelection(finish);
1134
1216
  }
1217
+ if (finish && this.defaultOptions.animatedRoute) {
1218
+ this.cancelAnimation();
1219
+ }
1135
1220
  this.routingSource.update(start, finish);
1136
1221
  }
1137
1222
  catch (e) {
@@ -1144,6 +1229,10 @@ var Map = /** @class */ (function () {
1144
1229
  if (this.defaultOptions.initPolygons) {
1145
1230
  this.handlePolygonSelection();
1146
1231
  }
1232
+ if (this.defaultOptions.animatedRoute) {
1233
+ this.cancelAnimation();
1234
+ }
1235
+ // TODO: remove direction icons
1147
1236
  this.routingSource.cancel();
1148
1237
  this.onRouteCancelListener.next('route cancelled');
1149
1238
  };
@@ -1166,13 +1255,20 @@ var Map = /** @class */ (function () {
1166
1255
  }
1167
1256
  if (this.map) {
1168
1257
  var routePoints = helpers_1.lineString(this.routingSource.levelPoints[this.state.floor.level].map(function (i) { return i.geometry.coordinates; }));
1258
+ var lengthInMeters = turf.length(routePoints, { units: 'kilometers' }) * 1000;
1169
1259
  var bbox = turf.bbox(routePoints);
1170
- // @ts-ignore
1171
- this.map.fitBounds(bbox, {
1172
- padding: this.defaultOptions.fitBoundsPadding,
1173
- bearing: this.map.getBearing(),
1174
- pitch: this.map.getPitch(),
1175
- });
1260
+ if (lengthInMeters > 15) {
1261
+ // @ts-ignore;
1262
+ this.map.fitBounds(bbox, {
1263
+ padding: lengthInMeters < 80 ? 400 : this.defaultOptions.fitBoundsPadding,
1264
+ bearing: this.map.getBearing(),
1265
+ pitch: this.map.getPitch(),
1266
+ });
1267
+ }
1268
+ else {
1269
+ // @ts-ignore
1270
+ this.map.flyTo({ center: turf.center(routePoints).geometry.coordinates });
1271
+ }
1176
1272
  }
1177
1273
  }
1178
1274
  };
@@ -1235,6 +1331,80 @@ var Map = /** @class */ (function () {
1235
1331
  };
1236
1332
  this.map.setStyle(this.state.style);
1237
1333
  };
1334
+ Map.prototype.addAnimatedRouteFeatures = function () {
1335
+ this.counter = 0;
1336
+ this.arc = {};
1337
+ var pointsArray = [];
1338
+ for (var _i = 0, _a = this.routingSource.lines; _i < _a.length; _i++) {
1339
+ var routePart = _a[_i];
1340
+ // Calculate the distance in kilometers between route start/end point.
1341
+ var lineDistance = turf.length(routePart);
1342
+ this.arc[routePart.id] = [];
1343
+ // Draw an arc between the `origin` & `destination` of the two points
1344
+ for (var i = 0; i < lineDistance; i += lineDistance / this.steps) {
1345
+ var segment = turf.along(turf.lineString(routePart.geometry.coordinates), i);
1346
+ this.arc[routePart.id].push(segment.geometry.coordinates);
1347
+ }
1348
+ var point = turf.point(routePart.geometry.coordinates[0], __assign(__assign({}, routePart.properties), { routePartId: routePart.id }));
1349
+ pointsArray.push(point);
1350
+ }
1351
+ this.state.style.sources['route-point'].data = turf.featureCollection(pointsArray);
1352
+ this.map.setStyle(this.state.style);
1353
+ this.animate();
1354
+ };
1355
+ Map.prototype.animate = function () {
1356
+ var _this = this;
1357
+ if (Object.keys(this.arc).length > 0) {
1358
+ var pointsArray = [];
1359
+ for (var _i = 0, _a = Object.entries(this.arc); _i < _a.length; _i++) {
1360
+ var _b = _a[_i], routeKey = _b[0], routePositions = _b[1];
1361
+ if (routePositions[this.counter]) {
1362
+ var point = turf.point(routePositions[this.counter], __assign(__assign({}, this.routingSource.route[routeKey].properties), { routePartId: routeKey }));
1363
+ pointsArray.push(point);
1364
+ }
1365
+ }
1366
+ // Update the source with this new data
1367
+ this.state.style.sources['route-point'].data = turf.featureCollection(pointsArray);
1368
+ this.map.setStyle(this.state.style);
1369
+ // Request the next frame of animation as long as the end has not been reached
1370
+ if (this.counter < this.steps) {
1371
+ var animationInstance = window.requestAnimationFrame(this.animate.bind(this));
1372
+ this.animationInstances.push(animationInstance);
1373
+ }
1374
+ if (this.counter === this.steps) {
1375
+ setTimeout(function () {
1376
+ // Reset the counter
1377
+ _this.counter = 0;
1378
+ // cancel animation
1379
+ // this.cancelAnimation();
1380
+ // Restart the animation
1381
+ _this.animate();
1382
+ }, 2000);
1383
+ }
1384
+ this.counter++;
1385
+ }
1386
+ };
1387
+ Map.prototype.cancelAnimation = function () {
1388
+ for (var _i = 0, _a = this.animationInstances; _i < _a.length; _i++) {
1389
+ var instance = _a[_i];
1390
+ window.cancelAnimationFrame(instance);
1391
+ }
1392
+ };
1393
+ Map.prototype.getClosestFeature = function (amenityId, fromFeature) {
1394
+ var sameLevelfeatures = this.state.allFeatures.features.filter(function (i) {
1395
+ return i.properties.amenity === amenityId &&
1396
+ i.geometry.type === 'Point' &&
1397
+ i.properties.level === fromFeature.properties.level;
1398
+ });
1399
+ var features = this.state.allFeatures.features.filter(function (i) { return i.properties.amenity === amenityId && i.geometry.type === 'Point'; });
1400
+ var targetPoint = turf.point(fromFeature.geometry.coordinates);
1401
+ if (sameLevelfeatures.length > 0 || features.length > 0) {
1402
+ return turf.nearestPoint(targetPoint, turf.featureCollection(sameLevelfeatures.length > 0 ? sameLevelfeatures : features));
1403
+ }
1404
+ else {
1405
+ return false;
1406
+ }
1407
+ };
1238
1408
  /**
1239
1409
  * @memberof Map
1240
1410
  * @name getMapboxInstance
@@ -1458,6 +1628,33 @@ var Map = /** @class */ (function () {
1458
1628
  this.routingSource.toggleAccessible(accessibleRoute);
1459
1629
  this.onRouteUpdate(this.defaultOptions.isKiosk ? this.startPoint : fromFeature, toFeature);
1460
1630
  };
1631
+ /**
1632
+ * This method will generate route to nearest amenity feature
1633
+ * @memberof Map
1634
+ * @name findRouteToNearestFeature
1635
+ * @param amenityId {string} amenity id of a nearest feature to look for
1636
+ * @param idFrom {string} start feature id, optional for kiosk
1637
+ * @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
1638
+ * @example
1639
+ * const map = new Proximiio.Map();
1640
+ * map.getMapReadyListener().subscribe(ready => {
1641
+ * console.log('map ready', ready);
1642
+ * map.findRouteToNearestFeature('amenityId');
1643
+ * });
1644
+ */
1645
+ Map.prototype.findRouteToNearestFeature = function (amenityId, idFrom, accessibleRoute) {
1646
+ var fromFeature = this.defaultOptions.isKiosk
1647
+ ? this.startPoint
1648
+ : this.state.allFeatures.features.find(function (f) { return f.id === idFrom || f.properties.id === idFrom; });
1649
+ var toFeature = this.getClosestFeature(amenityId, fromFeature);
1650
+ if (toFeature) {
1651
+ this.routingSource.toggleAccessible(accessibleRoute);
1652
+ this.onRouteUpdate(this.defaultOptions.isKiosk ? this.startPoint : fromFeature, toFeature);
1653
+ }
1654
+ else {
1655
+ throw new Error("Feature not found");
1656
+ }
1657
+ };
1461
1658
  /**
1462
1659
  * This method will cancel generated route
1463
1660
  * @memberof Map