Skip to content

Commit 3345a9a

Browse files
authored
feat(google-maps): Add Layer components. (#19604)
* feat(google-maps): Add Layer components. Implement components to easily add a traffic, transit or bicycling layer to an Angular Google Map. * feat(google-maps): Add Layer components. Fix comment for autoRefresh property of Traffic Layer. * feat(google-maps): Add layer components Add a base class for the transit and bicycling layers. * feat(google-maps): Add Layer components. Fix circleci error with MapBaseLayer not being part of a module. * feat(google-maps): Add Layer components Update public api with base layer class.
1 parent 7d511ba commit 3345a9a

File tree

13 files changed

+602
-2
lines changed

13 files changed

+602
-2
lines changed

src/dev-app/google-map/google-map-demo.html

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
[bounds]="groundOverlayBounds"></map-ground-overlay>
2727
<map-kml-layer *ngIf="isKmlLayerDisplayed"
2828
[url]="demoKml"></map-kml-layer>
29+
<map-traffic-layer *ngIf="isTrafficLayerDisplayed"></map-traffic-layer>
30+
<map-transit-layer *ngIf="isTransitLayerDisplayed"></map-transit-layer>
31+
<map-bicycling-layer *ngIf="isBicyclingLayerDisplayed"></map-bicycling-layer>
2932
</google-map>
3033

3134
<p><label>Latitude:</label> {{display?.lat}}</p>
@@ -125,4 +128,25 @@
125128
</label>
126129
</div>
127130

131+
<div>
132+
<label for="traffic-layer-checkbox">
133+
Toggle Traffic Layer
134+
<input type="checkbox" (click)="toggleTrafficLayerDisplay()">
135+
</label>
136+
</div>
137+
138+
<div>
139+
<label for="transit-layer-checkbox">
140+
Toggle Transit Layer
141+
<input type="checkbox" (click)="toggleTransitLayerDisplay()">
142+
</label>
143+
</div>
144+
145+
<div>
146+
<label for="bicycling-layer-checkbox">
147+
Toggle Bicycling Layer
148+
<input type="checkbox" (click)="toggleBicyclingLayerDisplay()">
149+
</label>
150+
</div>
151+
128152
</div>

src/dev-app/google-map/google-map-demo.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ export class GoogleMapDemo {
8383
isKmlLayerDisplayed = false;
8484
demoKml =
8585
'https://developers.google.com/maps/documentation/javascript/examples/kml/westcampus.kml';
86+
isTrafficLayerDisplayed = false;
87+
isTransitLayerDisplayed = false;
88+
isBicyclingLayerDisplayed = false;
8689

8790
mapTypeId: google.maps.MapTypeId;
8891
mapTypeIds = [
@@ -172,4 +175,16 @@ export class GoogleMapDemo {
172175
toggleKmlLayerDisplay() {
173176
this.isKmlLayerDisplayed = !this.isKmlLayerDisplayed;
174177
}
178+
179+
toggleTrafficLayerDisplay() {
180+
this.isTrafficLayerDisplayed = !this.isTrafficLayerDisplayed;
181+
}
182+
183+
toggleTransitLayerDisplay() {
184+
this.isTransitLayerDisplayed = !this.isTransitLayerDisplayed;
185+
}
186+
187+
toggleBicyclingLayerDisplay() {
188+
this.isBicyclingLayerDisplayed = !this.isBicyclingLayerDisplayed;
189+
}
175190
}

src/google-maps/google-maps-module.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import {NgModule} from '@angular/core';
1010

1111
import {GoogleMap} from './google-map/google-map';
12+
import {MapBaseLayer} from './map-base-layer';
13+
import {MapBicyclingLayer} from './map-bicycling-layer/map-bicycling-layer';
1214
import {MapCircle} from './map-circle/map-circle';
1315
import {MapGroundOverlay} from './map-ground-overlay/map-ground-overlay';
1416
import {MapInfoWindow} from './map-info-window/map-info-window';
@@ -17,9 +19,13 @@ import {MapMarker} from './map-marker/map-marker';
1719
import {MapPolygon} from './map-polygon/map-polygon';
1820
import {MapPolyline} from './map-polyline/map-polyline';
1921
import {MapRectangle} from './map-rectangle/map-rectangle';
22+
import {MapTrafficLayer} from './map-traffic-layer/map-traffic-layer';
23+
import {MapTransitLayer} from './map-transit-layer/map-transit-layer';
2024

2125
const COMPONENTS = [
2226
GoogleMap,
27+
MapBaseLayer,
28+
MapBicyclingLayer,
2329
MapCircle,
2430
MapGroundOverlay,
2531
MapInfoWindow,
@@ -28,6 +34,8 @@ const COMPONENTS = [
2834
MapPolygon,
2935
MapPolyline,
3036
MapRectangle,
37+
MapTrafficLayer,
38+
MapTransitLayer,
3139
];
3240

3341
@NgModule({

src/google-maps/map-base-layer.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
10+
/// <reference types="googlemaps" />
11+
12+
import {Directive, NgZone, OnDestroy, OnInit} from '@angular/core';
13+
14+
import {GoogleMap} from './google-map/google-map';
15+
16+
@Directive({
17+
selector: 'map-base-layer',
18+
exportAs: 'mapBaseLayer',
19+
})
20+
export class MapBaseLayer implements OnInit, OnDestroy {
21+
constructor(protected readonly _map: GoogleMap, protected readonly _ngZone: NgZone) {}
22+
23+
ngOnInit() {
24+
if (this._map._isBrowser) {
25+
this._ngZone.runOutsideAngular(() => {
26+
this._initializeObject();
27+
});
28+
this._assertInitialized();
29+
this._setMap();
30+
}
31+
}
32+
33+
ngOnDestroy() {
34+
this._unsetMap();
35+
}
36+
37+
private _assertInitialized() {
38+
if (!this._map.googleMap) {
39+
throw Error(
40+
'Cannot access Google Map information before the API has been initialized. ' +
41+
'Please wait for the API to load before trying to interact with it.');
42+
}
43+
}
44+
45+
protected _initializeObject() {}
46+
protected _setMap() {}
47+
protected _unsetMap() {}
48+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {Component} from '@angular/core';
2+
import {async, TestBed} from '@angular/core/testing';
3+
4+
import {DEFAULT_OPTIONS} from '../google-map/google-map';
5+
import {GoogleMapsModule} from '../google-maps-module';
6+
import {
7+
createBicyclingLayerConstructorSpy,
8+
createBicyclingLayerSpy,
9+
createMapConstructorSpy,
10+
createMapSpy,
11+
} from '../testing/fake-google-map-utils';
12+
13+
describe('MapBicyclingLayer', () => {
14+
let mapSpy: jasmine.SpyObj<google.maps.Map>;
15+
16+
beforeEach(async(() => {
17+
TestBed.configureTestingModule({
18+
imports: [GoogleMapsModule],
19+
declarations: [TestApp],
20+
});
21+
}));
22+
23+
beforeEach(() => {
24+
TestBed.compileComponents();
25+
26+
mapSpy = createMapSpy(DEFAULT_OPTIONS);
27+
createMapConstructorSpy(mapSpy).and.callThrough();
28+
});
29+
30+
afterEach(() => {
31+
delete window.google;
32+
});
33+
34+
it('initializes a Google Map Bicycling Layer', () => {
35+
const bicyclingLayerSpy = createBicyclingLayerSpy();
36+
const bicyclingLayerConstructorSpy =
37+
createBicyclingLayerConstructorSpy(bicyclingLayerSpy).and.callThrough();
38+
39+
const fixture = TestBed.createComponent(TestApp);
40+
fixture.detectChanges();
41+
42+
expect(bicyclingLayerConstructorSpy).toHaveBeenCalled();
43+
expect(bicyclingLayerSpy.setMap).toHaveBeenCalledWith(mapSpy);
44+
});
45+
});
46+
47+
@Component({
48+
selector: 'test-app',
49+
template: `<google-map>
50+
<map-bicycling-layer></map-bicycling-layer>
51+
</google-map>`,
52+
})
53+
class TestApp {
54+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
10+
/// <reference types="googlemaps" />
11+
12+
import {Directive} from '@angular/core';
13+
14+
import {MapBaseLayer} from '../map-base-layer';
15+
16+
/**
17+
* Angular component that renders a Google Maps Bicycling Layer via the Google Maps JavaScript API.
18+
*
19+
* See developers.google.com/maps/documentation/javascript/reference/map#BicyclingLayer
20+
*/
21+
@Directive({
22+
selector: 'map-bicycling-layer',
23+
exportAs: 'mapBicyclingLayer',
24+
})
25+
export class MapBicyclingLayer extends MapBaseLayer {
26+
/**
27+
* The underlying google.maps.BicyclingLayer object.
28+
*
29+
* See developers.google.com/maps/documentation/javascript/reference/map#BicyclingLayer
30+
*/
31+
bicyclingLayer?: google.maps.BicyclingLayer;
32+
33+
protected _initializeObject() {
34+
this.bicyclingLayer = new google.maps.BicyclingLayer();
35+
}
36+
37+
protected _setMap() {
38+
this._assertLayerInitialized();
39+
this.bicyclingLayer.setMap(this._map.googleMap!);
40+
}
41+
42+
protected _unsetMap() {
43+
if (this.bicyclingLayer) {
44+
this.bicyclingLayer.setMap(null);
45+
}
46+
}
47+
48+
private _assertLayerInitialized(): asserts this is {bicyclingLayer: google.maps.BicyclingLayer} {
49+
if (!this.bicyclingLayer) {
50+
throw Error(
51+
'Cannot interact with a Google Map Bicycling Layer before it has been initialized. ' +
52+
'Please wait for the Transit Layer to load before trying to interact with it.');
53+
}
54+
}
55+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {Component} from '@angular/core';
2+
import {async, TestBed} from '@angular/core/testing';
3+
4+
import {DEFAULT_OPTIONS} from '../google-map/google-map';
5+
import {GoogleMapsModule} from '../google-maps-module';
6+
import {
7+
createMapConstructorSpy,
8+
createMapSpy,
9+
createTrafficLayerConstructorSpy,
10+
createTrafficLayerSpy,
11+
} from '../testing/fake-google-map-utils';
12+
13+
describe('MapTrafficLayer', () => {
14+
let mapSpy: jasmine.SpyObj<google.maps.Map>;
15+
const trafficLayerOptions: google.maps.TrafficLayerOptions = {autoRefresh: false};
16+
17+
beforeEach(async(() => {
18+
TestBed.configureTestingModule({
19+
imports: [GoogleMapsModule],
20+
declarations: [TestApp],
21+
});
22+
}));
23+
24+
beforeEach(() => {
25+
TestBed.compileComponents();
26+
27+
mapSpy = createMapSpy(DEFAULT_OPTIONS);
28+
createMapConstructorSpy(mapSpy).and.callThrough();
29+
});
30+
31+
afterEach(() => {
32+
delete window.google;
33+
});
34+
35+
it('initializes a Google Map Traffic Layer', () => {
36+
const trafficLayerSpy = createTrafficLayerSpy(trafficLayerOptions);
37+
const trafficLayerConstructorSpy =
38+
createTrafficLayerConstructorSpy(trafficLayerSpy).and.callThrough();
39+
40+
const fixture = TestBed.createComponent(TestApp);
41+
fixture.componentInstance.autoRefresh = false;
42+
fixture.detectChanges();
43+
44+
expect(trafficLayerConstructorSpy).toHaveBeenCalledWith(trafficLayerOptions);
45+
expect(trafficLayerSpy.setMap).toHaveBeenCalledWith(mapSpy);
46+
});
47+
});
48+
49+
@Component({
50+
selector: 'test-app',
51+
template: `<google-map>
52+
<map-traffic-layer [autoRefresh]="autoRefresh">
53+
</map-traffic-layer>
54+
</google-map>`,
55+
})
56+
class TestApp {
57+
autoRefresh?: boolean;
58+
}

0 commit comments

Comments
 (0)