@aggdirect/coolmap-services 0.0.1

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 ADDED
@@ -0,0 +1,24 @@
1
+ # CoolmapServices
2
+
3
+ This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.0.
4
+
5
+ ## Code scaffolding
6
+
7
+ Run `ng generate component component-name --project coolmap-services` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project coolmap-services`.
8
+ > Note: Don't forget to add `--project coolmap-services` or else it will be added to the default project in your `angular.json` file.
9
+
10
+ ## Build
11
+
12
+ Run `ng build coolmap-services` to build the project. The build artifacts will be stored in the `dist/` directory.
13
+
14
+ ## Publishing
15
+
16
+ After building your library with `ng build coolmap-services`, go to the dist folder `cd dist/coolmap-services` and run `npm publish`.
17
+
18
+ ## Running unit tests
19
+
20
+ Run `ng test coolmap-services` to execute the unit tests via [Karma](https://karma-runner.github.io).
21
+
22
+ ## Further help
23
+
24
+ To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
package/karma.conf.js ADDED
@@ -0,0 +1,44 @@
1
+ // Karma configuration file, see link for more information
2
+ // https://karma-runner.github.io/1.0/config/configuration-file.html
3
+
4
+ module.exports = function (config) {
5
+ config.set({
6
+ basePath: '',
7
+ frameworks: ['jasmine', '@angular-devkit/build-angular'],
8
+ plugins: [
9
+ require('karma-jasmine'),
10
+ require('karma-chrome-launcher'),
11
+ require('karma-jasmine-html-reporter'),
12
+ require('karma-coverage'),
13
+ require('@angular-devkit/build-angular/plugins/karma')
14
+ ],
15
+ client: {
16
+ jasmine: {
17
+ // you can add configuration options for Jasmine here
18
+ // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19
+ // for example, you can disable the random execution with `random: false`
20
+ // or set a specific seed with `seed: 4321`
21
+ },
22
+ clearContext: false // leave Jasmine Spec Runner output visible in browser
23
+ },
24
+ jasmineHtmlReporter: {
25
+ suppressAll: true // removes the duplicated traces
26
+ },
27
+ coverageReporter: {
28
+ dir: require('path').join(__dirname, '../../coverage/coolmap-services'),
29
+ subdir: '.',
30
+ reporters: [
31
+ { type: 'html' },
32
+ { type: 'text-summary' }
33
+ ]
34
+ },
35
+ reporters: ['progress', 'kjhtml'],
36
+ port: 9876,
37
+ colors: true,
38
+ logLevel: config.LOG_INFO,
39
+ autoWatch: true,
40
+ browsers: ['Chrome'],
41
+ singleRun: false,
42
+ restartOnFileChange: true
43
+ });
44
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/coolmap-services",
4
+ "lib": {
5
+ "entryFile": "src/public-api.ts"
6
+ }
7
+ }
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@aggdirect/coolmap-services",
3
+ "version": "0.0.1",
4
+ "peerDependencies": {
5
+ "@angular/common": "^14.2.0",
6
+ "@angular/core": "^14.2.0"
7
+ },
8
+ "dependencies": {
9
+ "tslib": "^2.3.0"
10
+ },
11
+ "allowedNonPeerDependencies": {
12
+ "mapbox-gl": "^2.10.0",
13
+ "@types/mapbox-gl": "^2.7.5",
14
+ "@turf/turf": "^6.5.0",
15
+ "deck.gl": "^8.4.16"
16
+ },
17
+ "description": "This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.0.",
18
+ "main": "karma.conf.js",
19
+ "scripts": {
20
+ "test": "echo \"Error: no test specified\" && exit 1"
21
+ },
22
+ "author": "Navin Vishwakarma <navin@ogmaconceptions.com>",
23
+ "contributors": [
24
+ "Santanu Das <santanu.das@ogmaconceptions.com>"
25
+ ],
26
+ "license": "ISC"
27
+ }
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { CoolmapServicesComponent } from './coolmap-services.component';
4
+
5
+ describe('CoolmapServicesComponent', () => {
6
+ let component: CoolmapServicesComponent;
7
+ let fixture: ComponentFixture<CoolmapServicesComponent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ declarations: [ CoolmapServicesComponent ]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(CoolmapServicesComponent);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
@@ -0,0 +1,20 @@
1
+ import { Component, OnInit } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'lib-coolmap-services',
5
+ template: `
6
+ <p>
7
+ coolmap-services works!
8
+ </p>
9
+ `,
10
+ styles: [
11
+ ]
12
+ })
13
+ export class CoolmapServicesComponent implements OnInit {
14
+
15
+ constructor() { }
16
+
17
+ ngOnInit(): void {
18
+ }
19
+
20
+ }
@@ -0,0 +1,16 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { CoolmapServicesComponent } from './coolmap-services.component';
3
+
4
+
5
+
6
+ @NgModule({
7
+ declarations: [
8
+ CoolmapServicesComponent
9
+ ],
10
+ imports: [
11
+ ],
12
+ exports: [
13
+ CoolmapServicesComponent
14
+ ]
15
+ })
16
+ export class CoolmapServicesModule { }
@@ -0,0 +1,16 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+
3
+ import { CoolmapServicesService } from './coolmap-services.service';
4
+
5
+ describe('CoolmapServicesService', () => {
6
+ let service: CoolmapServicesService;
7
+
8
+ beforeEach(() => {
9
+ TestBed.configureTestingModule({});
10
+ service = TestBed.inject(CoolmapServicesService);
11
+ });
12
+
13
+ it('should be created', () => {
14
+ expect(service).toBeTruthy();
15
+ });
16
+ });
@@ -0,0 +1,9 @@
1
+ import { Injectable } from '@angular/core';
2
+
3
+ @Injectable({
4
+ providedIn: 'root'
5
+ })
6
+ export class CoolmapServicesService {
7
+
8
+ constructor() { }
9
+ }
@@ -0,0 +1,445 @@
1
+ import { Injectable } from '@angular/core';
2
+ import * as mapboxgl from 'mapbox-gl';
3
+ import * as turf from '@turf/turf';
4
+ import { BehaviorSubject } from 'rxjs';
5
+ import { PopupData, Route } from './data-model';
6
+ import { UtilsService } from './utils.service';
7
+ import { EventManager } from '@angular/platform-browser';
8
+ import { MapboxLayer } from "@deck.gl/mapbox";
9
+ import { ArcLayer } from '@deck.gl/layers';
10
+
11
+ declare const Threebox: any;
12
+
13
+ @Injectable({ providedIn: 'root' })
14
+ export class CoolmapService {
15
+ map!: mapboxgl.Map
16
+ markerOriginList: mapboxgl.Marker[] = [];
17
+ markerDestinationList: mapboxgl.Marker[] = [];
18
+ public initiatecoolmap = new BehaviorSubject(true);
19
+ reintiatecoolmap = this.initiatecoolmap.asObservable();
20
+ bounds = new mapboxgl.LngLatBounds();
21
+ originDestinationCordinates: any = [];
22
+ padding!: any;
23
+ windowActualHeightWidth!: any;
24
+ popup: mapboxgl.Popup;
25
+
26
+ constructor(private utils: UtilsService, private eventManager: EventManager) {
27
+ this.eventManager.addGlobalEventListener('window', 'resize', this.onResize.bind(this)); this.windowActualHeightWidth = { availHeight: 0 };
28
+ }
29
+
30
+ initiateMapForAddRoute(el: HTMLElement) {
31
+ return new Promise((resolve, reject) => {
32
+ this.map = new mapboxgl.Map({
33
+ accessToken: 'pk.eyJ1Ijoic2FudGFudS1vZ21hIiwiYSI6ImNsOGQ0cDVrNjAweTMzb3RmdG9ieTU5ZDkifQ.I6ZS7mViZYSvBo4zz-mGQQ',
34
+ container: el,
35
+ style: 'mapbox://styles/santanu-ogma/cl8ep33uz003414n1twlosz9d',
36
+ center: [-77.036873, 38.907192],
37
+ zoom: 10, bearing: 0, pitch: 65, interactive: true,
38
+ });
39
+ this.map.once('load', (res) => {
40
+ resolve(res);
41
+ });
42
+ });
43
+ }
44
+ // Below method Load route with animation
45
+ loadMapProperty(pinRouteGeojson: any, index: number, unit: any, route?: any, bottom?: number) {
46
+ return new Promise((resolve, reject)=>{
47
+ let origin = pinRouteGeojson.features[0].geometry.coordinates[0];
48
+ const linecolor = (unit === 'Ton') ? '#ff7272' : (unit === 'Load') ? '#a3c52e' : '#ae23d1';
49
+ let destination = pinRouteGeojson.features[0].geometry.coordinates[pinRouteGeojson.features[0].geometry.coordinates.length - 1];
50
+ this.extendBound(pinRouteGeojson.features[0].geometry.coordinates, true).then((res: any) => {
51
+ const point: any = {
52
+ 'type': 'FeatureCollection',
53
+ 'features': [
54
+ { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'Point', 'coordinates': origin } }
55
+ ]
56
+ }
57
+ const lineDistance = turf.length(pinRouteGeojson.features[0]);
58
+ const arc = [];
59
+ const steps = 10 * pinRouteGeojson.features[0].geometry.coordinates.length;
60
+ for (let i = 0; i < lineDistance; i += lineDistance / steps) {
61
+ const segment = turf.along(pinRouteGeojson.features[0], i);
62
+ arc.push(segment.geometry.coordinates);
63
+ }
64
+ pinRouteGeojson.features[0].geometry.coordinates = arc;
65
+ const pinRoute = pinRouteGeojson.features[0].geometry.coordinates;
66
+ const marker = new mapboxgl.Marker(document.createElement('div'))
67
+ .setLngLat(pinRoute[0]).addTo(this.map).togglePopup();
68
+
69
+ if (this.map.getSource(`line${index}`)) {
70
+ this.removeRouteAndMarker(index).then(() => { })
71
+ }
72
+
73
+ this.map.addSource(`line${index}`, { type: 'geojson', lineMetrics: true, data: pinRouteGeojson });
74
+ this.map.addLayer({
75
+ type: 'line',
76
+ source: `line${index}`,
77
+ id: `line${index}`,
78
+ paint: {
79
+ 'line-width': 2,
80
+ 'line-gradient': [
81
+ 'interpolate',
82
+ ['linear'],
83
+ ['line-progress'],
84
+ 0, unit === 'Ton' ? '#d7f7e4' : unit === 'Load' ? '#c9d8f5' : '#f5dcc1',
85
+ 1, unit === 'Ton' ? '#ff7272' : unit === 'Load' ? '#a3c52e' : '#ae23d1',
86
+ ]
87
+ },
88
+ layout: { 'line-cap': 'round', 'line-join': 'round' }
89
+ });
90
+
91
+ const dataSetForMap = {
92
+ counter: 0, pinRouteGeojson, steps,
93
+ point, pointId: `point${index}`,
94
+ marker, pinRoute, lineId: `line${index}`, index,
95
+ origin, destination, lineDistance,
96
+ linecolor, route, isViewRoute: true
97
+ }
98
+ this.createMarker(dataSetForMap);
99
+ this.map.on('mouseenter', `line${index}`, (e) => {
100
+ this.map.setPaintProperty(`line${index}`, 'line-width', 5);
101
+ this.map.setPaintProperty(`line${index}`, 'line-opacity', 1);
102
+ const datasetForPopup: PopupData = {
103
+ coordinate: [e.lngLat.lng, e.lngLat.lat],
104
+ pickup: route.pickup_location ? route.pickup_location : '',
105
+ drop: route.delivery_location ? route.delivery_location : '',
106
+ routeType: route.project ? 'Project' : 'Route',
107
+ title: route.project ? route.project : route.route_name ? route.route_name : '',
108
+ material: route.material ? route.material : '',
109
+ type: route.unit ? route.unit : ''
110
+ }
111
+ this.createPopup(datasetForPopup);
112
+ });
113
+ this.map.on('mouseleave', `line${index}`, (e) => {
114
+ this.map.setPaintProperty(`line${index}`, 'line-width', 2);
115
+ if (this.popup) {
116
+ this.popup.remove();
117
+ }
118
+ });
119
+ })
120
+
121
+ this.map.once('idle', (res) => {
122
+ resolve(true)
123
+ });
124
+ })
125
+ }
126
+ // Below method Load route without animation
127
+ drawLine(cordinates?: any, index?: number | any, route?: any, enablefitbound?: boolean, routeType?: string) {
128
+ let linecolor: any;
129
+ let origin = cordinates[0]; let destination = cordinates[cordinates.length - 1]
130
+ if (origin[0] && origin[1] && destination && destination[0] && destination[1]) {
131
+ linecolor = this.provideLineColor(route['unit'], routeType);
132
+ if (enablefitbound) {
133
+ const padding = {
134
+ top: this.padding.top, bottom: this.padding.bottom + (this.windowActualHeightWidth.availHeight - (window.innerHeight - 65)),
135
+ left: this.padding.left, right: this.padding.right
136
+ }
137
+ this.map.fitBounds([origin, destination], { padding }, { fitboundCompleteJob: true })
138
+ }
139
+ if (this.map.getSource(`route-source-for-job-code${index}`)) {
140
+ this.removeRouteAndMarker(index).then(() => { })
141
+ }
142
+ this.map.addSource(`route-source-for-job-code${index}`, {
143
+ 'type': 'geojson',
144
+ 'data': { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'LineString', 'coordinates': cordinates } }
145
+ });
146
+ this.map.addLayer({
147
+ 'id': `route-for-job-code${index}`, 'type': 'line', 'source': `route-source-for-job-code${index}`,
148
+ paint: { 'line-color': linecolor, 'line-width': 2 },
149
+ layout: { 'line-cap': 'round', 'line-join': 'round' }
150
+ });
151
+ const dataSetForMap = {
152
+ origin, destination, index, linecolor, route
153
+ }
154
+ this.createMarker(dataSetForMap);
155
+ this.map.on('mouseenter', `route-for-job-code${index}`, (e) => {
156
+ if (this.popup) {
157
+ this.popup.remove();
158
+ }
159
+ this.map.setPaintProperty(`route-for-job-code${index}`, 'line-width', 5);
160
+ this.map.setPaintProperty(`route-for-job-code${index}`, 'line-opacity', 1);
161
+ const datasetForPopup: PopupData = {
162
+ coordinate: [e.lngLat.lng, e.lngLat.lat],
163
+ pickup: route.pickup_location ? route.pickup_location : '',
164
+ drop: route.delivery_location ? route.delivery_location : '',
165
+ jobCode: route.project ? route.order_number : null,
166
+ customer: route.project ? route.customer_name : null,
167
+ routeType: route.project ? 'Project' : 'Route',
168
+ title: route.project ? route.project : route.route_name ? route.route_name : '',
169
+ material: route.material ? route.material : '',
170
+ type: route.unit ? route.unit : ''
171
+ }
172
+ this.createPopup(datasetForPopup);
173
+ });
174
+ this.map.on('mouseleave', `route-for-job-code${index}`, (e) => {
175
+ this.map.setPaintProperty(`route-for-job-code${index}`, 'line-width', 2);
176
+ if (this.popup) {
177
+ this.popup.remove();
178
+ }
179
+ });
180
+ }
181
+ }
182
+
183
+ provideLineColor(unitType: string, type?: string | undefined) {
184
+ let checkType = (type && !['jobrouteList', 'addroute'].includes(type)) ? true : false;
185
+ let color;
186
+ switch (unitType) {
187
+ case "Ton":
188
+ color = checkType ? '#39c471' : '#ff7272';
189
+ break;
190
+ case "Load":
191
+ color = checkType ? '#326ad3' : '#a3c52e';
192
+ break;
193
+ case "Hourly":
194
+ color = checkType ? '#ffad56' : '#ae23d1';
195
+ break;
196
+ }
197
+ return color;
198
+ }
199
+
200
+ showRoutePopup(arcDetails, event, isViewRoute?): void {
201
+ if (this.popup) {
202
+ this.popup.remove();
203
+ this.map.setPaintProperty(`${isViewRoute ? 'line' : 'route-for-job-code'}${arcDetails.layer.props.data.index}`, 'line-width', 2);
204
+ }
205
+ if (arcDetails.color && this.map.getLayoutProperty(arcDetails.layer.id, 'visibility') !== 'none') {
206
+ this.map.setPaintProperty(`${isViewRoute ? 'line' : 'route-for-job-code'}${arcDetails.layer.props.data.index}`, 'line-width', 5);
207
+ this.map.setPaintProperty(`${isViewRoute ? 'line' : 'route-for-job-code'}${arcDetails.layer.props.data.index}`, 'line-opacity', 1);
208
+
209
+ const datasetForPopup: PopupData = {
210
+ coordinate: arcDetails.coordinate,
211
+ pickup: arcDetails.layer.props.data.route.pickup_location ? arcDetails.layer.props.data.route.pickup_location : '',
212
+ drop: arcDetails.layer.props.data.route.delivery_location ? arcDetails.layer.props.data.route.delivery_location : '',
213
+ jobCode: arcDetails.layer.props.data.route.project ? arcDetails.layer.props.data.route.order_number : '',
214
+ customer: arcDetails.layer.props.data.route.project ? arcDetails.layer.props.data.route.customer_name : '',
215
+ routeType: arcDetails.layer.props.data.route.project ? 'Project' : 'Route',
216
+ title: arcDetails.layer.props.data.route.project ? arcDetails.layer.props.data.route.project : arcDetails.layer.props.data.route.route_name ? arcDetails.layer.props.data.route.route_name : '',
217
+ material: arcDetails.layer.props.data.route.material ? arcDetails.layer.props.data.route.material : '',
218
+ type: arcDetails.layer.props.data.route.unit ? arcDetails.layer.props.data.route.unit : ''
219
+ }
220
+ this.createPopup(datasetForPopup);
221
+ }
222
+ }
223
+
224
+ createPopup(datasetForPopup: PopupData): void {
225
+ this.popup = new mapboxgl.Popup({
226
+ closeButton: false,
227
+ closeOnClick: false,
228
+ closeOnMove: true,
229
+ anchor: 'bottom-left'
230
+ });
231
+
232
+ this.popup.setLngLat(datasetForPopup.coordinate)
233
+ .setHTML(`
234
+ <div class="destination">
235
+ <div class="duration">
236
+ <p class="pickprt"><b>Pickup Location:</b> ${datasetForPopup.pickup}</p>
237
+ <p class="dropprt"><b>Drop Location:</b> ${datasetForPopup.drop}</p>
238
+ </div>
239
+ ${datasetForPopup.jobCode ? '<span><b>Job Code:</b> ' + datasetForPopup.jobCode + '</span>' : ''}
240
+ ${datasetForPopup.customer ? '<span><b>Customer:</b> ' + datasetForPopup.customer + '</span>' : ''}
241
+ <span><b>${datasetForPopup.routeType} Name:</b> ${datasetForPopup.title}</span>
242
+ <span><b>Material:</b> ${datasetForPopup.material}</span>
243
+ <span><b>Type:</b> ${datasetForPopup.type}</span>
244
+ </div>
245
+ `)
246
+ .addTo(this.map);
247
+ }
248
+
249
+ hexToRGB(hex: any): [number, number, number] {
250
+ return hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i
251
+ , (m, r, g, b) => '#' + r + r + g + g + b + b)
252
+ .substring(1).match(/.{2}/g)
253
+ .map(x => parseInt(x, 16))
254
+ }
255
+
256
+ createMarker(routeDetails) {
257
+ if (routeDetails.origin[0] && routeDetails.origin[1] && routeDetails.destination[0] && routeDetails.destination[1]) {
258
+
259
+ const popup = new mapboxgl.Popup({ closeButton: false }).setHTML('<b>Pickup: </b>' + routeDetails.route?.pickup_location);
260
+ const popupForDestination = new mapboxgl.Popup({ closeButton: false })
261
+ .setHTML('<b>Delivery: </b>' + routeDetails.route?.delivery_location);
262
+
263
+ const el = document.createElement('div');
264
+ el.className = 'marker';
265
+ el.innerHTML = `<span class='markerPointer' style='background:${routeDetails.linecolor}'><b>P</b><span class='markerSpan' style='border-top: 10px solid ${routeDetails.linecolor}'></span></span>`
266
+
267
+ const originMarker = new mapboxgl.Marker(el)
268
+ .setPopup(popup).setLngLat(routeDetails.origin).addTo(this.map);
269
+
270
+ originMarker.getElement().addEventListener('mouseenter', () => originMarker.togglePopup());
271
+ originMarker.getElement().addEventListener('mouseleave', () => originMarker.togglePopup());
272
+
273
+ const elementForDestination = document.createElement('div');
274
+ elementForDestination.className = 'marker';
275
+ elementForDestination.innerHTML = `<span class='markerPointer' style='background:${routeDetails.linecolor}'><b>D</b><span class='markerSpan' style='border-top: 10px solid ${routeDetails.linecolor}'></span></span>`
276
+
277
+ const destinationMarker = new mapboxgl.Marker(elementForDestination).setPopup(popupForDestination).setLngLat(routeDetails.destination).addTo(this.map);
278
+ destinationMarker.getElement().addEventListener('mouseenter', () => destinationMarker.togglePopup());
279
+ destinationMarker.getElement().addEventListener('mouseleave', () => destinationMarker.togglePopup());
280
+ this.markerOriginList[routeDetails.index] = originMarker;
281
+ this.markerDestinationList[routeDetails.index] = destinationMarker;
282
+
283
+ const colorArray = this.hexToRGB(routeDetails.linecolor)
284
+ const arcLayer = new MapboxLayer({
285
+ id: 'arc-layer' + routeDetails.index,
286
+ type: ArcLayer,
287
+ pickable: true,
288
+ data: { route: routeDetails.route, index: routeDetails.index },
289
+ getWidth: 1,
290
+ getSourcePosition: routeDetails.origin,
291
+ getTargetPosition: routeDetails.destination,
292
+ getTargetColor: [255, 255, 255],
293
+ getSourceColor: [colorArray[0], colorArray[1], colorArray[2]],
294
+ onHover: (info, event) => this.showRoutePopup(info, event, routeDetails.isViewRoute),
295
+ });
296
+ this.map.addLayer(arcLayer);
297
+ }
298
+ }
299
+
300
+ async removeRouteAndMarker(index: number | string | any) {
301
+ if (this.map) {
302
+ this.map.getLayer(`arc-layer${index}`) ? this.map.removeLayer(`arc-layer${index}`) : '';
303
+ this.map.getLayer(`line${index}`) ? this.map.removeLayer(`line${index}`) : '';
304
+ this.map.getLayer(`custom_layer${index}`) ? this.map.removeLayer(`custom_layer${index}`) : '';
305
+ this.map.getSource(`line${index}`) ? this.map.removeSource(`line${index}`) : '';
306
+ this.map.getLayer(`route-for-job-code${index}`) ? this.map.removeLayer(`route-for-job-code${index}`) : '';
307
+ this.map.getSource(`route-source-for-job-code${index}`) ? this.map.removeSource(`route-source-for-job-code${index}`) : '';
308
+ this.findMarkerBound(index);
309
+ this.markerOriginList[index] ? this.markerOriginList[index].remove() : '';
310
+ this.markerDestinationList[index] ? this.markerDestinationList[index].remove() : '';
311
+ await true;
312
+ }
313
+ }
314
+ findMarkerBound(index: any) {
315
+ const indexOfCordinates = this.originDestinationCordinates.findIndex((x: any) =>
316
+ (x[0][0].toFixed(6) == this.markerOriginList[index]?.getLngLat()?.lng.toFixed(6)) && (x[0][1].toFixed(6) == this.markerOriginList[index]?.getLngLat()?.lat.toFixed(6)));
317
+ if (indexOfCordinates >= 0) { this.originDestinationCordinates.splice(indexOfCordinates, 1) }
318
+ }
319
+ async filterRoute(ID: any, visibility: string, showAllFitbound?: boolean) {
320
+ if (ID) {
321
+ if (this.map.getLayer(`route-for-job-code${ID}`)) {
322
+ this.map.setLayoutProperty(`route-for-job-code${ID}`, 'visibility', visibility);
323
+ const originM = this.markerOriginList[ID].getElement();
324
+ originM.style.display = ((visibility === 'visible') ? 'block' : visibility);
325
+ const destinationM = this.markerDestinationList[ID].getElement();
326
+ destinationM.style.display = ((visibility === 'visible') ? 'block' : visibility);
327
+ if (visibility === 'none' && showAllFitbound) {
328
+ this.findMarkerBound(ID);
329
+ this.extendReBound();
330
+ }
331
+ }
332
+ if (this.map.getLayer(`arc-layer${ID}`)) {
333
+ this.map.setLayoutProperty(`arc-layer${ID}`, 'visibility', visibility);
334
+ }
335
+ }
336
+ await true;
337
+ }
338
+ extendBound(route: any, showAllFitbound: boolean | undefined) {
339
+ return new Promise((resolve, reject) => {
340
+ if (route) {
341
+ if (typeof route === 'string') {
342
+ let path: any = route.split(';');
343
+ path = path.map((ele: string | any) => { return ele = this.formateLatLong(ele) });
344
+ path.forEach((ele: any, index: number) => { if (ele.length === 1) path.splice(index, 1) });
345
+ route = path
346
+ }
347
+ if (route[0][0] && route[0][1] && route[route.length - 1][0] && route[route.length - 1][1]) {
348
+ this.originDestinationCordinates.push(route);
349
+ route.map((item: any) => {
350
+ this.bounds.extend(item);
351
+ })
352
+ }
353
+ }
354
+ if (showAllFitbound) {
355
+ const padding = { top: this.padding.top, bottom: (this.padding.bottom + (this.windowActualHeightWidth.availHeight - (window.innerHeight - 65))), left: this.padding.left, right: this.padding.right }
356
+ setTimeout(() => {
357
+ if (showAllFitbound && (Object.keys(this.bounds).length > 0)) this.map.fitBounds(this.bounds, { padding }, { fitboundComplete: true })
358
+ }, 100);
359
+ this.map.once('moveend', (event: any) => { if (event.fitboundComplete) { resolve(true) } });
360
+ }
361
+ });
362
+ }
363
+
364
+ extendReBound(bottom?: number) {
365
+ return new Promise((resolve, reject) => {
366
+ this.bounds = new mapboxgl.LngLatBounds();
367
+
368
+ if (this.originDestinationCordinates.length >= 0) {
369
+ this.originDestinationCordinates.map((item: any, index: number) => {
370
+ item.map((route: any) => {
371
+ this.bounds.extend(route);
372
+ })
373
+ if (index === (this.originDestinationCordinates.length - 1)) {
374
+ const padding = { top: this.padding.top, bottom: (this.padding.bottom + (this.windowActualHeightWidth.availHeight - (window.innerHeight - 65))), left: this.padding.left, right: this.padding.right }
375
+ setTimeout(() => {
376
+ if (this.originDestinationCordinates.length > 0) this.map.fitBounds(this.bounds, { padding });
377
+ }, 500);
378
+ resolve(true);
379
+ }
380
+ })
381
+ } else {
382
+ resolve(true);
383
+ }
384
+ })
385
+ }
386
+ plotRoute(route: any, i: number | any, type: string, enablefitbound?: boolean, showAllFitbound?: boolean) {
387
+ return new Promise((resolve, reject) => {
388
+ let param: any = {};
389
+ if (['jobcode'].includes(type)) {
390
+ param['job'] = route['job_id']
391
+ this.utils.postDataWithRestUrl('schedule/job/path', param).subscribe((res: any) => {
392
+ if (res['data']['route']) {
393
+ let path = res['data']['route'].split(';');
394
+ path = path.map((ele: string | any) => { return ele = this.formateLatLong(ele) });
395
+ path.forEach((ele: any, index: number) => { if (ele.length === 1) path.splice(index, 1) });
396
+ route['path'] = path;
397
+ this.extendBound(route['path'], showAllFitbound);
398
+ if (route['path'] && route['path'].length > 0) this.drawLine(route['path'], i, route, enablefitbound, type);
399
+ route['index'] = i;
400
+ } else {
401
+ this.extendBound(null, showAllFitbound);
402
+ }
403
+ resolve(true);
404
+ }, (err) => { if (err) { reject(false) } });
405
+ } else if (['jobrouteList', 'addroute'].includes(type)) {
406
+ if (route['path'] && route['path'].length > 0) {
407
+ let path = route['path'].split(';');
408
+ path = path.map((ele: string | any) => { return ele = this.formateLatLong(ele) });
409
+ path.forEach((ele: any, index: number) => { if (ele.length === 1) path.splice(index, 1) });
410
+ this.extendBound(path, showAllFitbound);
411
+ this.drawLine(path, i, route, enablefitbound, type)
412
+ };
413
+ }
414
+ })
415
+ }
416
+ clearBound() { this.bounds = new mapboxgl.LngLatBounds(); this.originDestinationCordinates = []; this.clearPadding(); }
417
+ formateLatLong(latlong: string) { return latlong ? latlong.split(',').map(x => +x).reverse() : null; }
418
+ clearBoundWithCordinates() {
419
+ this.bounds = new mapboxgl.LngLatBounds(); this.originDestinationCordinates = [];
420
+ }
421
+
422
+ onResize(event: any) {
423
+ if (!this.bounds.isEmpty()) {
424
+ this.windowActualHeightWidth.availHeight = (window.innerHeight > window.screen.availHeight) ? window.innerHeight : window.screen.availHeight;
425
+ setTimeout(() => {
426
+ this.map.fitBounds(this.bounds, { padding: { top: this.padding.top, bottom: this.padding.bottom + (this.windowActualHeightWidth.availHeight - (event.target.innerHeight - 65)), left: this.padding.left, right: this.padding.right } });
427
+ }, 500);
428
+ }
429
+ }
430
+
431
+ setWindowHeight(screen: any) {
432
+ this.windowActualHeightWidth.availHeight = screen;
433
+ }
434
+ setPadding(padding: any) { this.padding = padding }
435
+ clearPadding() { this.padding = null }
436
+ removeJobFromMap(data: Route[]) {
437
+ data.map((ele: any, index: number) => {
438
+ const id = (ele['job_id'] ? ele['job_id'] : ele['route_id']);
439
+ this.removeRouteAndMarker(id);
440
+ if (index === (data.length - 1)) {
441
+ this.extendReBound();
442
+ }
443
+ })
444
+ }
445
+ }
@@ -0,0 +1,80 @@
1
+
2
+ export class Route {
3
+ index: number = 0;
4
+ type?: string | null;
5
+ // Job Code
6
+ customer_contact?: string | null;
7
+ customer_name?: string | null;
8
+ delivery_contact?: string | null;
9
+ delivery_lat?: number | null;
10
+ delivery_location?: string | null | undefined;
11
+ delivery_lon?: number | null;
12
+ driver_list?: Array<any>;
13
+ material?: string | null;
14
+ order_number?: string | null;
15
+ pickup_lat?: number | null;
16
+ pickup_location?: string | null | undefined;
17
+ pickup_lon?: number | null;
18
+ project?: string | null;
19
+ total_count?: number;
20
+ unit?: string | null;
21
+ values: any;
22
+ job_id?: string;
23
+ isSelected?: boolean;
24
+ date?: string;
25
+ // Add Route
26
+ created_at?: string | null;
27
+ created_by_name?: string | null;
28
+ customer_id?: string | null;
29
+ delivery_lat_lng?: string | null;
30
+ estimated_distance?: string | null;
31
+ estimated_time?: string | null;
32
+ materials_id?: string | null;
33
+ path?: [number, number];
34
+ pickup_lat_lng?: string | null;
35
+ route_id?: string | null;
36
+ route_name?: string | null;
37
+ unit_id?: string | null;
38
+ note?: string | null
39
+ isActive?: boolean;
40
+ prevent?: boolean;
41
+ }
42
+
43
+ export const EstinationData = ['estimated_distance', 'estimated_time']
44
+ export enum EstinationEnum {
45
+ estimated_distance = "miles",
46
+ estimated_time = "time"
47
+ }
48
+
49
+ export const JobCodeOverviewData = ['customer_contact', 'delivery_contact', 'pickup_location', 'delivery_location', 'project', 'unit']
50
+ export enum JobCodeOverviewEnum {
51
+ customer_contact = 'Customer Contact',
52
+ delivery_contact = 'Delivery Contact',
53
+ pickup_location = 'Pickup',
54
+ delivery_location = 'Delivery',
55
+ project = 'Project Name',
56
+ unit = 'Job Type',
57
+ }
58
+
59
+ export const DriversmsCardKey = ['order_number', 'date', 'values', 'material', 'unit', 'pickup_location', 'delivery_location']
60
+ export enum DriverSmsCardEnum {
61
+ order_number = 'Jobcode',
62
+ date = 'Date',
63
+ values = 'Total tasks',
64
+ material = 'Material',
65
+ unit = 'Unit',
66
+ pickup_location = 'Pickup Address',
67
+ delivery_location = 'Delivery Address'
68
+ }
69
+
70
+ export class PopupData {
71
+ coordinate: [number, number];
72
+ pickup: string | null;
73
+ jobCode?: string | null;
74
+ customer?: string | null;
75
+ drop: string | null;
76
+ routeType: string | null;
77
+ title: string | null;
78
+ material: string | null;
79
+ type: string | null;
80
+ }
@@ -0,0 +1,198 @@
1
+ import { HttpClient } from '@angular/common/http';
2
+ import { Injectable } from '@angular/core';
3
+ import { MatSnackBar } from '@angular/material/snack-bar';
4
+ import { BehaviorSubject } from 'rxjs';
5
+ import { Route } from './data-model';
6
+
7
+ @Injectable({ providedIn: 'root' })
8
+ export class UtilsService {
9
+ analyticsRESTURL = '';
10
+ RESTURLPrefix = '';
11
+ pickupOptions: any[] = [];
12
+ destOptions: any[] = [];
13
+ ownerOptions: any[] = [];
14
+ customerOptions: any[] = [];
15
+ unitOptions: any[] = [];
16
+ materialOptions: any[] = [];
17
+ jcodeOptions: any[] = [];
18
+ driverOption: any[] = [];
19
+ truckingCompanayOption: any[] = [];
20
+ routeNameOptions: any[] = [];
21
+ public preventnavChange = new BehaviorSubject(false);
22
+ navChangeObserve = this.preventnavChange.asObservable();
23
+ clearViewRouteforJobCode = new BehaviorSubject(false);
24
+ clearViewRouteforJobCodeObserve = this.clearViewRouteforJobCode.asObservable();
25
+ preVentJobdetailclose = new BehaviorSubject(false);
26
+ getpreVentJobdetailclose = this.preVentJobdetailclose.asObservable();
27
+ routeDetailsUtility = new BehaviorSubject({});
28
+ getrouteDetailsUtility = this.routeDetailsUtility.asObservable();
29
+ removeMapEntity = new BehaviorSubject({});
30
+ removeMapEntityUtility = this.removeMapEntity.asObservable();
31
+ dict = new Map<string, string>();
32
+ constructor(private http: HttpClient,private snackBar: MatSnackBar) {
33
+ this.analyticsRESTURL = 'https://test-analytics.aggdirect.com/REST/';
34
+ this.RESTURLPrefix = 'https://test-server.aggdirect.com/REST/';
35
+ }
36
+ getDateFormat(strVal: Date | null, seprater?: string) {
37
+ seprater = seprater ? seprater : '-';
38
+ const mydate = strVal;
39
+ return mydate?.getFullYear() + seprater + ((mydate ? mydate.getMonth() : 0) + 1) + seprater + mydate?.getDate();
40
+ }
41
+ getData(path: string) { return this.http.get(`${this.analyticsRESTURL}${path}`) }
42
+ postdata(path: string, data: any) { return this.http.post(`${this.analyticsRESTURL}${path}`, data) }
43
+ postDataWithRestUrl(path: string, data: any) { return this.http.post(`${this.RESTURLPrefix}${path}`, data) }
44
+ fetchAutoCompleteLocations(keyword: string) {
45
+ return this.http.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/${keyword}.json?access_token=pk.eyJ1Ijoic2FudGFudS1vZ21hIiwiYSI6ImNsOGQ0cDVrNjAweTMzb3RmdG9ieTU5ZDkifQ.I6ZS7mViZYSvBo4zz-mGQQ&country=US`)
46
+ }
47
+ openSnackBar(message: string, className?: string) {
48
+ this.snackBar.open(message, '', {
49
+ duration: 5000, verticalPosition: 'top', horizontalPosition: 'center', panelClass: [className ? className : 'default']
50
+ });
51
+ }
52
+
53
+ makeOptions(item: Route) {
54
+ if (item.order_number) {
55
+ (this.jcodeOptions.findIndex((elem) => elem.job_id === item.job_id) === -1) ? this.jcodeOptions.push({ job_code: item.order_number, job_id: item.job_id }) : null;
56
+ if (item.driver_list && item.driver_list?.length > 0) {
57
+ item.driver_list.forEach((driver) => {
58
+ this.driverOption.findIndex(elem => elem === driver['driver_name']) === -1 ? this.driverOption.push(driver['driver_name']) : null;
59
+ this.truckingCompanayOption.findIndex(elem => elem === driver['trucking_company']) === -1 ? this.truckingCompanayOption.push(driver['trucking_company']) : null;
60
+ });
61
+ }
62
+ }
63
+ ((this.pickupOptions.findIndex((elem) => elem === item.pickup_location)) === -1) ? this.pickupOptions.push(item.pickup_location) : null;
64
+ ((this.destOptions.findIndex((elem) => elem === item.delivery_location)) === -1) ? this.destOptions.push(item.delivery_location) : null;
65
+ (this.customerOptions.findIndex((customer) => customer === item.customer_name) === -1) ? this.customerOptions.push(item.customer_name) : null;
66
+ (this.unitOptions.findIndex((elem) => elem === item.unit) === -1) ? this.unitOptions.push(item.unit) : null;
67
+ (this.materialOptions.findIndex((elem) => elem === item.material) === -1) ? this.materialOptions.push(item.material) : null;
68
+ if (item.route_name) this.routeNameOptions.findIndex(elem => elem === item.route_name) === -1 ? this.routeNameOptions.push(item.route_name) : null;
69
+ }
70
+
71
+ filter(value: string, filters: any) {
72
+ if (typeof value !== 'string') { return [] }
73
+ const filterValue = value.toLowerCase();
74
+ if (filterValue === '') { return [] }
75
+ const searchResults: any[] = [];
76
+ this.unitOptions.map(unit => {
77
+ if (unit.toLowerCase().includes(filterValue)) {
78
+ searchResults.push({ "type": "unit", "label": unit, "value": unit })
79
+ }
80
+ });
81
+ this.customerOptions.map(unit => {
82
+ if (unit.toLowerCase().includes(filterValue)) {
83
+ searchResults.push({ "type": "customer", "label": unit, "value": unit })
84
+ }
85
+ });
86
+ this.materialOptions.map(unit => {
87
+ if (unit.toLowerCase().includes(filterValue)) {
88
+ searchResults.push({ "type": "material", "label": unit, "value": unit })
89
+ }
90
+ });
91
+ this.pickupOptions.map(unit => {
92
+ if (unit.toLowerCase().includes(filterValue)) {
93
+ searchResults.push({ "type": "pickup location", "label": unit, "value": unit })
94
+ }
95
+ });
96
+ this.destOptions.map(unit => {
97
+ if (unit.toLowerCase().includes(filterValue)) {
98
+ searchResults.push({ "type": "destination location", "label": unit, "value": unit })
99
+ }
100
+ });
101
+ this.jcodeOptions.map(unit => {
102
+ if (unit.job_code.toLowerCase().includes(filterValue)) {
103
+ searchResults.push({ "type": "job", "label": unit['job_code'], "value": unit })
104
+ }
105
+ });
106
+ this.driverOption.map(unit => {
107
+ if (unit.toLowerCase().includes(filterValue)) {
108
+ searchResults.push({ "type": "Driver", "label": unit, "value": unit })
109
+ }
110
+ });
111
+
112
+ this.truckingCompanayOption.map(unit => {
113
+ if (unit.toLowerCase().includes(filterValue)) {
114
+ searchResults.push({ "type": "Trucking Company", "label": unit, "value": unit })
115
+ }
116
+ });
117
+ this.routeNameOptions.map(unit => {
118
+ if (unit.toLowerCase().includes(filterValue)) {
119
+ searchResults.push({ "type": "Route name", "label": unit, "value": unit })
120
+ }
121
+ });
122
+ const searchDict: any = {}
123
+ filters.map((filter: { [x: string]: any; }) => {
124
+ searchDict[filter['name'] + filter['type']] = filter;
125
+ })
126
+ const furtherFilter: any[] = [];
127
+ searchResults.map(search => {
128
+ if ((search['label'] + search['type']) in searchDict) { }
129
+ else { furtherFilter.push(search) }
130
+ });
131
+ return furtherFilter;
132
+ }
133
+
134
+ getSearchResults(list: Route[], filterval: any): any {
135
+ return list.filter(element => {
136
+ const result_list_boolean = []
137
+ if (filterval.length > 0) {
138
+ if (filterval[0]['type'] === 'unit') {
139
+ result_list_boolean.push(filterval[0]['name'] === element[filterval[0]['type'] as keyof Route]);
140
+ }
141
+ if (filterval[0]['type'] === 'customer') {
142
+ result_list_boolean.push(filterval[0]['name'] === element['customer_name']);
143
+ }
144
+ if (filterval[0]['type'] === 'material') {
145
+ result_list_boolean.push(filterval[0]['name'] === element[filterval[0]['type'] as keyof Route]);
146
+ }
147
+ if (filterval[0]['type'] === 'pickup location') {
148
+ result_list_boolean.push(filterval[0]['name'] === element['pickup_location']);
149
+ }
150
+ if (filterval[0]['type'] === 'destination location') {
151
+ result_list_boolean.push(filterval[0]['name'] === element['delivery_location']);
152
+ }
153
+ if (filterval[0]['type'] === 'job') {
154
+ result_list_boolean.push(filterval[0]['name'] === element['order_number']);
155
+ }
156
+ if (filterval[0]['type'] === 'Route name') {
157
+ result_list_boolean.push(filterval[0]['name'] === element['route_name']);
158
+ }
159
+ if (filterval[0]['type'] === 'Driver') {
160
+ const index = element.driver_list?.findIndex(ele => { return filterval[0]['name'] === ele['driver_name'] });
161
+ if (index !== -1) result_list_boolean.push(true);
162
+ }
163
+ if (filterval[0]['type'] === 'Trucking Company') {
164
+ const index = element.driver_list?.findIndex(ele => { return filterval[0]['name'] === ele['trucking_company'] });
165
+ if (index !== -1) result_list_boolean.push(true);
166
+ }
167
+ }
168
+ if (result_list_boolean.length > 0) {
169
+ return result_list_boolean.reduce((prev, curr) => prev && curr);
170
+ }
171
+ return false;
172
+ });
173
+ }
174
+
175
+ clearOptions() {
176
+ this.pickupOptions = [];
177
+ this.destOptions = [];
178
+ this.ownerOptions = [];
179
+ this.customerOptions = [];
180
+ this.unitOptions = [];
181
+ this.materialOptions = [];
182
+ this.jcodeOptions = [];
183
+ this.routeNameOptions = [];
184
+ }
185
+ setdictValue(key: string, value: string) {
186
+ this.dict.set(key, value);
187
+ }
188
+ getdictValue(key: string) {
189
+ return JSON.parse(this.dict.get(key));
190
+ }
191
+ removedictValue(key: string) {
192
+ this.dict.delete(key);
193
+ }
194
+ conveySearchIcon(value: any): boolean{
195
+ if(value && typeof(value) !== 'object') return true;
196
+ return false
197
+ }
198
+ }
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Public API Surface of coolmap-services
3
+ */
4
+
5
+ export * from './lib/coolmap-services.service';
6
+ export * from './lib/coolmap-services.component';
7
+ export * from './lib/coolmap-services.module';
8
+ export * from './lib/service/coolmap.service';
9
+ export * from './lib/service/utils.service';
10
+ export * from './lib/service/data-model';
11
+
12
+
13
+
package/src/test.ts ADDED
@@ -0,0 +1,27 @@
1
+ // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2
+
3
+ import 'zone.js';
4
+ import 'zone.js/testing';
5
+ import { getTestBed } from '@angular/core/testing';
6
+ import {
7
+ BrowserDynamicTestingModule,
8
+ platformBrowserDynamicTesting
9
+ } from '@angular/platform-browser-dynamic/testing';
10
+
11
+ declare const require: {
12
+ context(path: string, deep?: boolean, filter?: RegExp): {
13
+ <T>(id: string): T;
14
+ keys(): string[];
15
+ };
16
+ };
17
+
18
+ // First, initialize the Angular testing environment.
19
+ getTestBed().initTestEnvironment(
20
+ BrowserDynamicTestingModule,
21
+ platformBrowserDynamicTesting(),
22
+ );
23
+
24
+ // Then we find all the tests.
25
+ const context = require.context('./', true, /\.spec\.ts$/);
26
+ // And load the modules.
27
+ context.keys().forEach(context);
@@ -0,0 +1,15 @@
1
+ /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
+ {
3
+ "extends": "../../tsconfig.json",
4
+ "compilerOptions": {
5
+ "outDir": "../../out-tsc/lib",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "inlineSources": true,
9
+ "types": []
10
+ },
11
+ "exclude": [
12
+ "src/test.ts",
13
+ "**/*.spec.ts"
14
+ ]
15
+ }
@@ -0,0 +1,10 @@
1
+ /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
+ {
3
+ "extends": "./tsconfig.lib.json",
4
+ "compilerOptions": {
5
+ "declarationMap": false
6
+ },
7
+ "angularCompilerOptions": {
8
+ "compilationMode": "partial"
9
+ }
10
+ }
@@ -0,0 +1,17 @@
1
+ /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
+ {
3
+ "extends": "../../tsconfig.json",
4
+ "compilerOptions": {
5
+ "outDir": "../../out-tsc/spec",
6
+ "types": [
7
+ "jasmine"
8
+ ]
9
+ },
10
+ "files": [
11
+ "src/test.ts"
12
+ ],
13
+ "include": [
14
+ "**/*.spec.ts",
15
+ "**/*.d.ts"
16
+ ]
17
+ }