Skip to content

Commit

Permalink
feat(google-maps): Add Layer components. (#19604)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
mbehrlich committed Aug 21, 2020
1 parent 7d511ba commit 3345a9a
Show file tree
Hide file tree
Showing 13 changed files with 602 additions and 2 deletions.
24 changes: 24 additions & 0 deletions src/dev-app/google-map/google-map-demo.html
Expand Up @@ -26,6 +26,9 @@
[bounds]="groundOverlayBounds"></map-ground-overlay>
<map-kml-layer *ngIf="isKmlLayerDisplayed"
[url]="demoKml"></map-kml-layer>
<map-traffic-layer *ngIf="isTrafficLayerDisplayed"></map-traffic-layer>
<map-transit-layer *ngIf="isTransitLayerDisplayed"></map-transit-layer>
<map-bicycling-layer *ngIf="isBicyclingLayerDisplayed"></map-bicycling-layer>
</google-map>

<p><label>Latitude:</label> {{display?.lat}}</p>
Expand Down Expand Up @@ -125,4 +128,25 @@
</label>
</div>

<div>
<label for="traffic-layer-checkbox">
Toggle Traffic Layer
<input type="checkbox" (click)="toggleTrafficLayerDisplay()">
</label>
</div>

<div>
<label for="transit-layer-checkbox">
Toggle Transit Layer
<input type="checkbox" (click)="toggleTransitLayerDisplay()">
</label>
</div>

<div>
<label for="bicycling-layer-checkbox">
Toggle Bicycling Layer
<input type="checkbox" (click)="toggleBicyclingLayerDisplay()">
</label>
</div>

</div>
15 changes: 15 additions & 0 deletions src/dev-app/google-map/google-map-demo.ts
Expand Up @@ -83,6 +83,9 @@ export class GoogleMapDemo {
isKmlLayerDisplayed = false;
demoKml =
'https://developers.google.com/maps/documentation/javascript/examples/kml/westcampus.kml';
isTrafficLayerDisplayed = false;
isTransitLayerDisplayed = false;
isBicyclingLayerDisplayed = false;

mapTypeId: google.maps.MapTypeId;
mapTypeIds = [
Expand Down Expand Up @@ -172,4 +175,16 @@ export class GoogleMapDemo {
toggleKmlLayerDisplay() {
this.isKmlLayerDisplayed = !this.isKmlLayerDisplayed;
}

toggleTrafficLayerDisplay() {
this.isTrafficLayerDisplayed = !this.isTrafficLayerDisplayed;
}

toggleTransitLayerDisplay() {
this.isTransitLayerDisplayed = !this.isTransitLayerDisplayed;
}

toggleBicyclingLayerDisplay() {
this.isBicyclingLayerDisplayed = !this.isBicyclingLayerDisplayed;
}
}
8 changes: 8 additions & 0 deletions src/google-maps/google-maps-module.ts
Expand Up @@ -9,6 +9,8 @@
import {NgModule} from '@angular/core';

import {GoogleMap} from './google-map/google-map';
import {MapBaseLayer} from './map-base-layer';
import {MapBicyclingLayer} from './map-bicycling-layer/map-bicycling-layer';
import {MapCircle} from './map-circle/map-circle';
import {MapGroundOverlay} from './map-ground-overlay/map-ground-overlay';
import {MapInfoWindow} from './map-info-window/map-info-window';
Expand All @@ -17,9 +19,13 @@ import {MapMarker} from './map-marker/map-marker';
import {MapPolygon} from './map-polygon/map-polygon';
import {MapPolyline} from './map-polyline/map-polyline';
import {MapRectangle} from './map-rectangle/map-rectangle';
import {MapTrafficLayer} from './map-traffic-layer/map-traffic-layer';
import {MapTransitLayer} from './map-transit-layer/map-transit-layer';

const COMPONENTS = [
GoogleMap,
MapBaseLayer,
MapBicyclingLayer,
MapCircle,
MapGroundOverlay,
MapInfoWindow,
Expand All @@ -28,6 +34,8 @@ const COMPONENTS = [
MapPolygon,
MapPolyline,
MapRectangle,
MapTrafficLayer,
MapTransitLayer,
];

@NgModule({
Expand Down
48 changes: 48 additions & 0 deletions src/google-maps/map-base-layer.ts
@@ -0,0 +1,48 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
/// <reference types="googlemaps" />

import {Directive, NgZone, OnDestroy, OnInit} from '@angular/core';

import {GoogleMap} from './google-map/google-map';

@Directive({
selector: 'map-base-layer',
exportAs: 'mapBaseLayer',
})
export class MapBaseLayer implements OnInit, OnDestroy {
constructor(protected readonly _map: GoogleMap, protected readonly _ngZone: NgZone) {}

ngOnInit() {
if (this._map._isBrowser) {
this._ngZone.runOutsideAngular(() => {
this._initializeObject();
});
this._assertInitialized();
this._setMap();
}
}

ngOnDestroy() {
this._unsetMap();
}

private _assertInitialized() {
if (!this._map.googleMap) {
throw Error(
'Cannot access Google Map information before the API has been initialized. ' +
'Please wait for the API to load before trying to interact with it.');
}
}

protected _initializeObject() {}
protected _setMap() {}
protected _unsetMap() {}
}
54 changes: 54 additions & 0 deletions src/google-maps/map-bicycling-layer/map-bicycling-layer.spec.ts
@@ -0,0 +1,54 @@
import {Component} from '@angular/core';
import {async, TestBed} from '@angular/core/testing';

import {DEFAULT_OPTIONS} from '../google-map/google-map';
import {GoogleMapsModule} from '../google-maps-module';
import {
createBicyclingLayerConstructorSpy,
createBicyclingLayerSpy,
createMapConstructorSpy,
createMapSpy,
} from '../testing/fake-google-map-utils';

describe('MapBicyclingLayer', () => {
let mapSpy: jasmine.SpyObj<google.maps.Map>;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [GoogleMapsModule],
declarations: [TestApp],
});
}));

beforeEach(() => {
TestBed.compileComponents();

mapSpy = createMapSpy(DEFAULT_OPTIONS);
createMapConstructorSpy(mapSpy).and.callThrough();
});

afterEach(() => {
delete window.google;
});

it('initializes a Google Map Bicycling Layer', () => {
const bicyclingLayerSpy = createBicyclingLayerSpy();
const bicyclingLayerConstructorSpy =
createBicyclingLayerConstructorSpy(bicyclingLayerSpy).and.callThrough();

const fixture = TestBed.createComponent(TestApp);
fixture.detectChanges();

expect(bicyclingLayerConstructorSpy).toHaveBeenCalled();
expect(bicyclingLayerSpy.setMap).toHaveBeenCalledWith(mapSpy);
});
});

@Component({
selector: 'test-app',
template: `<google-map>
<map-bicycling-layer></map-bicycling-layer>
</google-map>`,
})
class TestApp {
}
55 changes: 55 additions & 0 deletions src/google-maps/map-bicycling-layer/map-bicycling-layer.ts
@@ -0,0 +1,55 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
/// <reference types="googlemaps" />

import {Directive} from '@angular/core';

import {MapBaseLayer} from '../map-base-layer';

/**
* Angular component that renders a Google Maps Bicycling Layer via the Google Maps JavaScript API.
*
* See developers.google.com/maps/documentation/javascript/reference/map#BicyclingLayer
*/
@Directive({
selector: 'map-bicycling-layer',
exportAs: 'mapBicyclingLayer',
})
export class MapBicyclingLayer extends MapBaseLayer {
/**
* The underlying google.maps.BicyclingLayer object.
*
* See developers.google.com/maps/documentation/javascript/reference/map#BicyclingLayer
*/
bicyclingLayer?: google.maps.BicyclingLayer;

protected _initializeObject() {
this.bicyclingLayer = new google.maps.BicyclingLayer();
}

protected _setMap() {
this._assertLayerInitialized();
this.bicyclingLayer.setMap(this._map.googleMap!);
}

protected _unsetMap() {
if (this.bicyclingLayer) {
this.bicyclingLayer.setMap(null);
}
}

private _assertLayerInitialized(): asserts this is {bicyclingLayer: google.maps.BicyclingLayer} {
if (!this.bicyclingLayer) {
throw Error(
'Cannot interact with a Google Map Bicycling Layer before it has been initialized. ' +
'Please wait for the Transit Layer to load before trying to interact with it.');
}
}
}
58 changes: 58 additions & 0 deletions src/google-maps/map-traffic-layer/map-traffic-layer.spec.ts
@@ -0,0 +1,58 @@
import {Component} from '@angular/core';
import {async, TestBed} from '@angular/core/testing';

import {DEFAULT_OPTIONS} from '../google-map/google-map';
import {GoogleMapsModule} from '../google-maps-module';
import {
createMapConstructorSpy,
createMapSpy,
createTrafficLayerConstructorSpy,
createTrafficLayerSpy,
} from '../testing/fake-google-map-utils';

describe('MapTrafficLayer', () => {
let mapSpy: jasmine.SpyObj<google.maps.Map>;
const trafficLayerOptions: google.maps.TrafficLayerOptions = {autoRefresh: false};

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [GoogleMapsModule],
declarations: [TestApp],
});
}));

beforeEach(() => {
TestBed.compileComponents();

mapSpy = createMapSpy(DEFAULT_OPTIONS);
createMapConstructorSpy(mapSpy).and.callThrough();
});

afterEach(() => {
delete window.google;
});

it('initializes a Google Map Traffic Layer', () => {
const trafficLayerSpy = createTrafficLayerSpy(trafficLayerOptions);
const trafficLayerConstructorSpy =
createTrafficLayerConstructorSpy(trafficLayerSpy).and.callThrough();

const fixture = TestBed.createComponent(TestApp);
fixture.componentInstance.autoRefresh = false;
fixture.detectChanges();

expect(trafficLayerConstructorSpy).toHaveBeenCalledWith(trafficLayerOptions);
expect(trafficLayerSpy.setMap).toHaveBeenCalledWith(mapSpy);
});
});

@Component({
selector: 'test-app',
template: `<google-map>
<map-traffic-layer [autoRefresh]="autoRefresh">
</map-traffic-layer>
</google-map>`,
})
class TestApp {
autoRefresh?: boolean;
}

0 comments on commit 3345a9a

Please sign in to comment.