Skip to content

Commit

Permalink
feat(*): Support for Bicycling Layer (#1678)
Browse files Browse the repository at this point in the history
- Added a single service to manage all map layers like transit and bicycling layers
  • Loading branch information
bmamey authored and doom777 committed Jul 15, 2019
1 parent be2c5af commit a9b2a9c
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 188 deletions.
3 changes: 2 additions & 1 deletion packages/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {MapsAPILoader} from './services/maps-api-loader/maps-api-loader';
import {BROWSER_GLOBALS_PROVIDERS} from './utils/browser-globals';
import {AgmFitBounds} from './directives/fit-bounds';
import { AgmPolylineIcon } from './directives/polyline-icon';
import { AgmBicyclingLayer } from './directives/bicycling-layer';
import { AgmTransitLayer } from './directives/transit-layer';

/**
Expand All @@ -24,7 +25,7 @@ export function coreDirectives() {
return [
AgmMap, AgmMarker, AgmInfoWindow, AgmCircle, AgmRectangle,
AgmPolygon, AgmPolyline, AgmPolylinePoint, AgmKmlLayer,
AgmDataLayer, AgmFitBounds, AgmPolylineIcon, AgmTransitLayer
AgmDataLayer, AgmFitBounds, AgmPolylineIcon, AgmTransitLayer, AgmBicyclingLayer
];
}

Expand Down
1 change: 1 addition & 0 deletions packages/core/directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export {AgmInfoWindow} from './directives/info-window';
export {AgmKmlLayer} from './directives/kml-layer';
export {AgmDataLayer} from './directives/data-layer';
export {AgmTransitLayer} from './directives/transit-layer';
export {AgmBicyclingLayer} from './directives/bicycling-layer';
export {AgmMarker} from './directives/marker';
export {AgmPolygon} from './directives/polygon';
export {AgmPolyline} from './directives/polyline';
Expand Down
53 changes: 53 additions & 0 deletions packages/core/directives/bicycling-layer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Directive, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { LayerManager } from '../services/managers/layer-manager';

let layerId = 0;

/*
* This directive adds a bicycling layer to a google map instance
* <agm-bicycling-layer [visible]="true|false"> <agm-bicycling-layer>
* */
@Directive({
selector: 'agm-bicycling-layer'
})

export class AgmBicyclingLayer implements OnInit, OnChanges, OnDestroy{
private _addedToManager: boolean = false;
private _id: string = (layerId++).toString();

/**
* Hide/show bicycling layer
*/
@Input() visible: boolean = true;

constructor( private _manager: LayerManager ) {}

ngOnInit() {
if (this._addedToManager) {
return;
}
this._manager.addBicyclingLayer(this, {visible: this.visible});
this._addedToManager = true;
}

ngOnChanges(changes: SimpleChanges) {
if (!this._addedToManager) {
return;
}
if (changes['visible'] != null) {
this._manager.toggleLayerVisibility(this, {visible: changes['visible'].currentValue});
}
}

/** @internal */
id(): string { return this._id; }

/** @internal */
toString(): string { return `AgmBicyclingLayer-${this._id.toString()}`; }

/** @internal */
ngOnDestroy() {
this._manager.deleteLayer(this);
}

}
4 changes: 2 additions & 2 deletions packages/core/directives/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { PolygonManager } from '../services/managers/polygon-manager';
import { PolylineManager } from '../services/managers/polyline-manager';
import { KmlLayerManager } from './../services/managers/kml-layer-manager';
import { DataLayerManager } from './../services/managers/data-layer-manager';
import { TransitLayerManager } from '../services/managers/transit-layer-manager';
import { LayerManager } from '../services/managers/layer-manager';
import { FitBoundsService } from '../services/fit-bounds';

declare var google: any;
Expand Down Expand Up @@ -48,7 +48,7 @@ declare var google: any;
providers: [
GoogleMapsAPIWrapper, MarkerManager, InfoWindowManager, CircleManager, RectangleManager,
PolylineManager, PolygonManager, KmlLayerManager, DataLayerManager, DataLayerManager,
TransitLayerManager, FitBoundsService
LayerManager, FitBoundsService
],
host: {
// todo: deprecated - we will remove it with the next version
Expand Down
21 changes: 5 additions & 16 deletions packages/core/directives/transit-layer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Directive, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { TransitLayerManager } from '../services/managers/transit-layer-manager';
import { LayerManager } from '../services/managers/layer-manager';

let layerId = 0;

Expand All @@ -14,14 +14,13 @@ let layerId = 0;
export class AgmTransitLayer implements OnInit, OnChanges, OnDestroy{
private _addedToManager: boolean = false;
private _id: string = (layerId++).toString();
private static _transitLayerOptions: string[] = [ 'visible'];

/**
* Hide/show transit layer
*/
@Input() visible: boolean = true;

constructor( private _manager: TransitLayerManager ) {}
constructor( private _manager: LayerManager ) {}

ngOnInit() {
if (this._addedToManager) {
Expand All @@ -35,18 +34,8 @@ export class AgmTransitLayer implements OnInit, OnChanges, OnDestroy{
if (!this._addedToManager) {
return;
}
this._updateTransitLayerOptions(changes);
}

private _updateTransitLayerOptions(changes: SimpleChanges) {
const options = Object.keys(changes)
.filter(k => AgmTransitLayer._transitLayerOptions.indexOf(k) !== -1)
.reduce((obj: any, k: string) => {
obj[k] = changes[k].currentValue;
return obj;
}, {});
if (Object.keys(options).length > 0) {
this._manager.setOptions(this, options);
if (changes['visible'] != null) {
this._manager.toggleLayerVisibility(this, {visible: changes['visible'].currentValue});
}
}

Expand All @@ -58,7 +47,7 @@ export class AgmTransitLayer implements OnInit, OnChanges, OnDestroy{

/** @internal */
ngOnDestroy() {
this._manager.deleteTransitLayer(this);
this._manager.deleteLayer(this);
}

}
2 changes: 1 addition & 1 deletion packages/core/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ export {GoogleMapsScriptProtocol, LAZY_MAPS_API_CONFIG, LazyMapsAPILoader, LazyM
export {MapsAPILoader} from './services/maps-api-loader/maps-api-loader';
export {NoOpMapsAPILoader} from './services/maps-api-loader/noop-maps-api-loader';
export {FitBoundsAccessor, FitBoundsDetails} from './services/fit-bounds';
export {TransitLayerManager} from './services/managers/transit-layer-manager';
export {LayerManager} from './services/managers/layer-manager';
23 changes: 18 additions & 5 deletions packages/core/services/google-maps-api-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,28 @@ export class GoogleMapsAPIWrapper {
}

/**
* Creates a Google Map transit layer instance add it to map
* @param {TransitLayerOptions} options - TransitLayerOptions options
* Creates a TransitLayer instance for a map
* @param {TransitLayerOptions} options - used for setting layer options
* @returns {Promise<TransitLayer>} a new transit layer object
*/
createTransitLayer(options: mapTypes.TransitLayerOptions): Promise<mapTypes.TransitLayer>{
return this._map.then((map: mapTypes.GoogleMap) => {
let transitLayer: mapTypes.TransitLayer = new google.maps.TransitLayer();
transitLayer.setMap(options.visible ? map : null);
return transitLayer;
let newLayer: mapTypes.TransitLayer = new google.maps.TransitLayer();
newLayer.setMap(options.visible ? map : null);
return newLayer;
});
}

/**
* Creates a BicyclingLayer instance for a map
* @param {BicyclingLayerOptions} options - used for setting layer options
* @returns {Promise<BicyclingLayer>} a new bicycling layer object
*/
createBicyclingLayer(options: mapTypes.BicyclingLayerOptions): Promise<mapTypes.BicyclingLayer>{
return this._map.then((map: mapTypes.GoogleMap) => {
let newLayer: mapTypes.BicyclingLayer = new google.maps.BicyclingLayer();
newLayer.setMap(options.visible ? map : null);
return newLayer;
});
}

Expand Down
10 changes: 9 additions & 1 deletion packages/core/services/google-maps-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,13 +438,21 @@ export interface KmlMouseEvent extends MouseEvent {
export interface TransitLayer extends MVCObject {
getMap(): GoogleMap;
setMap(map: GoogleMap): void;
setOptions(options: TransitLayerOptions): void;
}

export interface TransitLayerOptions {
visible: boolean;
}

export interface BicyclingLayer extends MVCObject {
getMap(): GoogleMap;
setMap(map: GoogleMap): void;
}

export interface BicyclingLayerOptions {
visible: boolean;
}

export interface Data extends MVCObject {
features: Feature[];
addGeoJson(geoJson: Object, options?: GeoJsonOptions): Feature[];
Expand Down
116 changes: 116 additions & 0 deletions packages/core/services/managers/layer-manager.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import {NgZone} from '@angular/core';
import {TestBed, inject, fakeAsync} from '@angular/core/testing';
import {AgmTransitLayer} from '../../directives/transit-layer';
import {GoogleMapsAPIWrapper} from '../../services/google-maps-api-wrapper';
import {LayerManager} from './layer-manager';
import {AgmBicyclingLayer} from '../../directives/bicycling-layer';

describe('LayerManager', () => {
beforeAll(() => {
(<any>window).google = {
maps: {
TransitLayer: class TransitLayer {
setMap = jest.fn();
getMap = jest.fn();
constructor() {

}
},

BicyclingLayer: class BicyclingLayer {
setMap = jest.fn();
getMap = jest.fn();
constructor() {

}
},
}
};
});

beforeEach(() => {

TestBed.configureTestingModule({
providers: [
{provide: NgZone, useFactory: () => new NgZone({enableLongStackTrace: true})},
{
provide: GoogleMapsAPIWrapper,
useValue: {
getNativeMap: () => Promise.resolve(),
createTransitLayer: jest.fn(),
createBicyclingLayer: jest.fn()

}
},
LayerManager,

]
});
}); // end beforeEach

describe('Create a new transit layer', () => {

it('should call mapsApiWrapper when creating a new transit layer',
fakeAsync(inject(
[LayerManager, GoogleMapsAPIWrapper],
(layerManager: LayerManager, apiWrapper: GoogleMapsAPIWrapper) => {
const transitLayer = new AgmTransitLayer(layerManager);
const opt = {visible: false};
layerManager.addTransitLayer(transitLayer, opt);
expect(apiWrapper.createTransitLayer).toHaveBeenCalledWith(opt);
})
)
);
});

describe('Create a new bicycling layer', () => {

it('should call mapsApiWrapper when creating a new bicycling layer',
fakeAsync(inject(
[LayerManager, GoogleMapsAPIWrapper],
(layerManager: LayerManager, apiWrapper: GoogleMapsAPIWrapper) => {
const bicyclingLayer = new AgmBicyclingLayer(layerManager);
const opt = {visible: true};
layerManager.addBicyclingLayer(bicyclingLayer, opt);
expect(apiWrapper.createBicyclingLayer).toHaveBeenCalledWith(opt);

})
)
);
});

describe('Toggling visibility of a MapLayer', () => {

it('should update the visibility of a map layer when the visibility option changes', fakeAsync(

inject(
[LayerManager, GoogleMapsAPIWrapper],
(layerManager: LayerManager,
apiWrapper: GoogleMapsAPIWrapper) => {
const newLayer = new AgmTransitLayer(layerManager);
newLayer.visible = true;

const transitLayerInstance: any = {
setMap: jest.fn(),
getMap: jest.fn(),
};

(<jest.Mock>apiWrapper.createTransitLayer).mockReturnValue(
Promise.resolve(transitLayerInstance)
);

layerManager.addTransitLayer(newLayer, {visible: true});
expect(apiWrapper.createTransitLayer).toHaveBeenCalledWith({visible: true});

newLayer.visible = false;
layerManager.toggleLayerVisibility(newLayer, {visible: false}).then(() => {
expect(transitLayerInstance.setMap).toHaveBeenCalledWith(null);
});
}
)

));

});

});
Loading

0 comments on commit a9b2a9c

Please sign in to comment.