Skip to content
This repository was archived by the owner on Jun 23, 2025. It is now read-only.

Commit 0f71222

Browse files
bmameydoom777
authored andcommitted
feat(*): Support for Transit Layer
- Added new Directive called <agm-transit-layer> - Added test fixes: 1674
1 parent 46a39b7 commit 0f71222

9 files changed

Lines changed: 255 additions & 2 deletions

File tree

packages/core/core.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {MapsAPILoader} from './services/maps-api-loader/maps-api-loader';
1515
import {BROWSER_GLOBALS_PROVIDERS} from './utils/browser-globals';
1616
import {AgmFitBounds} from './directives/fit-bounds';
1717
import { AgmPolylineIcon } from './directives/polyline-icon';
18+
import { AgmTransitLayer } from './directives/transit-layer';
1819

1920
/**
2021
* @internal
@@ -23,7 +24,7 @@ export function coreDirectives() {
2324
return [
2425
AgmMap, AgmMarker, AgmInfoWindow, AgmCircle, AgmRectangle,
2526
AgmPolygon, AgmPolyline, AgmPolylinePoint, AgmKmlLayer,
26-
AgmDataLayer, AgmFitBounds, AgmPolylineIcon
27+
AgmDataLayer, AgmFitBounds, AgmPolylineIcon, AgmTransitLayer
2728
];
2829
}
2930

packages/core/directives.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export {AgmRectangle} from './directives/rectangle';
44
export {AgmInfoWindow} from './directives/info-window';
55
export {AgmKmlLayer} from './directives/kml-layer';
66
export {AgmDataLayer} from './directives/data-layer';
7+
export {AgmTransitLayer} from './directives/transit-layer';
78
export {AgmMarker} from './directives/marker';
89
export {AgmPolygon} from './directives/polygon';
910
export {AgmPolyline} from './directives/polyline';

packages/core/directives/map.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { PolygonManager } from '../services/managers/polygon-manager';
1515
import { PolylineManager } from '../services/managers/polyline-manager';
1616
import { KmlLayerManager } from './../services/managers/kml-layer-manager';
1717
import { DataLayerManager } from './../services/managers/data-layer-manager';
18+
import { TransitLayerManager } from '../services/managers/transit-layer-manager';
1819
import { FitBoundsService } from '../services/fit-bounds';
1920

2021
declare var google: any;
@@ -47,7 +48,7 @@ declare var google: any;
4748
providers: [
4849
GoogleMapsAPIWrapper, MarkerManager, InfoWindowManager, CircleManager, RectangleManager,
4950
PolylineManager, PolygonManager, KmlLayerManager, DataLayerManager, DataLayerManager,
50-
FitBoundsService
51+
TransitLayerManager, FitBoundsService
5152
],
5253
host: {
5354
// todo: deprecated - we will remove it with the next version
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Directive, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
2+
import { TransitLayerManager } from '../services/managers/transit-layer-manager';
3+
4+
let layerId = 0;
5+
6+
/*
7+
* This directive adds a transit layer to a google map instance
8+
* <agm-transit-layer [visible]="true|false"> <agm-transit-layer>
9+
* */
10+
@Directive({
11+
selector: 'agm-transit-layer'
12+
})
13+
14+
export class AgmTransitLayer implements OnInit, OnChanges, OnDestroy{
15+
private _addedToManager: boolean = false;
16+
private _id: string = (layerId++).toString();
17+
private static _transitLayerOptions: string[] = [ 'visible'];
18+
19+
/**
20+
* Hide/show transit layer
21+
*/
22+
@Input() visible: boolean = true;
23+
24+
constructor( private _manager: TransitLayerManager ) {}
25+
26+
ngOnInit() {
27+
if (this._addedToManager) {
28+
return;
29+
}
30+
this._manager.addTransitLayer(this, {visible: this.visible});
31+
this._addedToManager = true;
32+
}
33+
34+
ngOnChanges(changes: SimpleChanges) {
35+
if (!this._addedToManager) {
36+
return;
37+
}
38+
this._updateTransitLayerOptions(changes);
39+
}
40+
41+
private _updateTransitLayerOptions(changes: SimpleChanges) {
42+
const options = Object.keys(changes)
43+
.filter(k => AgmTransitLayer._transitLayerOptions.indexOf(k) !== -1)
44+
.reduce((obj: any, k: string) => {
45+
obj[k] = changes[k].currentValue;
46+
return obj;
47+
}, {});
48+
if (Object.keys(options).length > 0) {
49+
this._manager.setOptions(this, options);
50+
}
51+
}
52+
53+
/** @internal */
54+
id(): string { return this._id; }
55+
56+
/** @internal */
57+
toString(): string { return `AgmTransitLayer-${this._id.toString()}`; }
58+
59+
/** @internal */
60+
ngOnDestroy() {
61+
this._manager.deleteTransitLayer(this);
62+
}
63+
64+
}

packages/core/services.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ export {GoogleMapsScriptProtocol, LAZY_MAPS_API_CONFIG, LazyMapsAPILoader, LazyM
1111
export {MapsAPILoader} from './services/maps-api-loader/maps-api-loader';
1212
export {NoOpMapsAPILoader} from './services/maps-api-loader/noop-maps-api-loader';
1313
export {FitBoundsAccessor, FitBoundsDetails} from './services/fit-bounds';
14+
export {TransitLayerManager} from './services/managers/transit-layer-manager';

packages/core/services/google-maps-api-wrapper.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,19 @@ export class GoogleMapsAPIWrapper {
104104
});
105105
}
106106

107+
/**
108+
* Creates a Google Map transit layer instance add it to map
109+
* @param {TransitLayerOptions} options - TransitLayerOptions options
110+
* @returns {Promise<TransitLayer>} a new transit layer object
111+
*/
112+
createTransitLayer(options: mapTypes.TransitLayerOptions): Promise<mapTypes.TransitLayer>{
113+
return this._map.then((map: mapTypes.GoogleMap) => {
114+
let transitLayer: mapTypes.TransitLayer = new google.maps.TransitLayer();
115+
transitLayer.setMap(options.visible ? map : null);
116+
return transitLayer;
117+
});
118+
}
119+
107120
/**
108121
* Determines if given coordinates are insite a Polygon path.
109122
*/

packages/core/services/google-maps-types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,16 @@ export interface KmlMouseEvent extends MouseEvent {
435435
pixelOffset: Size;
436436
}
437437

438+
export interface TransitLayer extends MVCObject {
439+
getMap(): GoogleMap;
440+
setMap(map: GoogleMap): void;
441+
setOptions(options: TransitLayerOptions): void;
442+
}
443+
444+
export interface TransitLayerOptions {
445+
visible: boolean;
446+
}
447+
438448
export interface Data extends MVCObject {
439449
features: Feature[];
440450
addGeoJson(geoJson: Object, options?: GeoJsonOptions): Feature[];
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import {NgZone} from '@angular/core';
2+
import {TestBed, inject, fakeAsync} from '@angular/core/testing';
3+
import {AgmTransitLayer} from '../../directives/transit-layer';
4+
import {GoogleMapsAPIWrapper} from '../../services/google-maps-api-wrapper';
5+
import {TransitLayerManager} from '../../services/managers/transit-layer-manager';
6+
7+
describe('TransitLayerManager', () => {
8+
beforeAll(() => {
9+
(<any>window).google = {
10+
maps: {
11+
TransitLayer: class TransitLayer {
12+
setMap = jest.fn();
13+
14+
constructor() {
15+
16+
}
17+
},
18+
}
19+
};
20+
});
21+
22+
beforeEach(() => {
23+
24+
TestBed.configureTestingModule({
25+
providers: [
26+
{provide: NgZone, useFactory: () => new NgZone({enableLongStackTrace: true})},
27+
{
28+
provide: GoogleMapsAPIWrapper,
29+
useValue: {
30+
getNativeMap: () => Promise.resolve(),
31+
createTransitLayer: jest.fn()
32+
}
33+
},
34+
TransitLayerManager,
35+
36+
]
37+
});
38+
}); // end beforeEach
39+
40+
describe('Create a new TransitLayer', () => {
41+
42+
it('should call mapsApiWrapper when creating a new transit',
43+
fakeAsync(inject(
44+
[TransitLayerManager, GoogleMapsAPIWrapper],
45+
(transitLayerManager: TransitLayerManager, apiWrapper: GoogleMapsAPIWrapper) => {
46+
const newTransitLayer = new AgmTransitLayer(transitLayerManager);
47+
const opt = {visible: false};
48+
transitLayerManager.addTransitLayer(newTransitLayer, opt);
49+
expect(apiWrapper.createTransitLayer).toHaveBeenCalled();
50+
51+
})
52+
)
53+
);
54+
});
55+
56+
describe('Toggling visibility of TransitLayer', () => {
57+
58+
it('should update that transit layer via setOptions method when the options changes', fakeAsync(
59+
60+
inject(
61+
[TransitLayerManager, GoogleMapsAPIWrapper],
62+
(transitLayerManager: TransitLayerManager,
63+
apiWrapper: GoogleMapsAPIWrapper) => {
64+
const newTransitLayer = new AgmTransitLayer(transitLayerManager);
65+
newTransitLayer.visible = true;
66+
67+
const transitLayerInstance: any = {
68+
setMap: jest.fn(),
69+
setOptions: jest.fn()
70+
71+
};
72+
73+
(<jest.Mock>apiWrapper.createTransitLayer).mockReturnValue(
74+
Promise.resolve(transitLayerInstance)
75+
);
76+
77+
transitLayerManager.addTransitLayer(newTransitLayer, {visible: true});
78+
expect(apiWrapper.createTransitLayer).toHaveBeenCalledWith({
79+
visible: true
80+
});
81+
82+
newTransitLayer.visible = false;
83+
transitLayerManager.setOptions(newTransitLayer, {visible: false}).then(() => {
84+
expect(transitLayerInstance.setMap).toHaveBeenCalledWith(null);
85+
});
86+
87+
}
88+
)
89+
90+
));
91+
92+
});
93+
94+
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import {Injectable} from '@angular/core';
2+
import {AgmTransitLayer} from '../../directives/transit-layer';
3+
import {GoogleMapsAPIWrapper} from '../google-maps-api-wrapper';
4+
import {TransitLayer, TransitLayerOptions, GoogleMap} from '../google-maps-types';
5+
6+
/**
7+
* This class manages a Transit Layer for a Google Map instance.
8+
*/
9+
10+
@Injectable()
11+
export class TransitLayerManager {
12+
private _layers: Map<AgmTransitLayer, Promise<TransitLayer>> =
13+
new Map<AgmTransitLayer, Promise<TransitLayer>>();
14+
15+
constructor(private _wrapper: GoogleMapsAPIWrapper) {}
16+
17+
/**
18+
* Adds a transit layer to a map and local layer manager
19+
* @param {AgmTransitLayer} layer - a transitLayer object
20+
* @param {TransitLayerOptions} options - TransitLayerOptions options
21+
* @returns void
22+
*/
23+
addTransitLayer(layer: AgmTransitLayer, options: TransitLayerOptions): void {
24+
const newLayer = this._wrapper.createTransitLayer(options);
25+
this._layers.set(layer, newLayer);
26+
}
27+
28+
/**
29+
* Sets layer options
30+
* @param {AgmTransitLayer} transitLayer object
31+
* @param {options} TransitLayerOptions
32+
* @returns Promise<void>
33+
*/
34+
setOptions(layer: AgmTransitLayer, options: TransitLayerOptions): Promise<void> {
35+
return this.toggleTransitLayerVisibility(layer, options);
36+
}
37+
38+
/**
39+
* Deletes a transit layer
40+
* @param {AgmTransitLayer} layer - the transit layer to delete
41+
* @returns Promise<void>
42+
*/
43+
deleteTransitLayer(layer: AgmTransitLayer): Promise<void> {
44+
return this._layers.get(layer).then(currentLayer => {
45+
currentLayer.setMap(null);
46+
this._layers.delete(layer);
47+
});
48+
}
49+
50+
/**
51+
* Hide/Show a Google Map transit layer
52+
* @param {AgmTransitLayer} transitLayer object
53+
* @param {options} TransitLayerOptions
54+
* @returns Promise<void>
55+
*/
56+
toggleTransitLayerVisibility(layer: AgmTransitLayer, options: TransitLayerOptions): Promise<void> {
57+
return this._layers.get(layer).then(currentLayer => {
58+
if (!options.visible) {
59+
currentLayer.setMap(null);
60+
return Promise.resolve();
61+
} else {
62+
return this._wrapper.getNativeMap().then( (map: GoogleMap) => {
63+
currentLayer.setMap(map);
64+
});
65+
}
66+
});
67+
}
68+
}

0 commit comments

Comments
 (0)