Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(*): Support for Bicycling Layer #1678

Merged
5 commits merged into from
Jul 15, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
54 changes: 54 additions & 0 deletions packages/core/directives/bicycling-layer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Directive, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { MapLayerManager } from '../services/managers/map-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 = false;

constructor( private _manager: MapLayerManager ) {}

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

ngOnChanges(changes: SimpleChanges) {
if (!this._addedToManager) {
return;
}
this._manager.setOptions(this, changes);
}

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

/** @internal */
name(): string { return 'BicyclingLayer'; }

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

/** @internal */
ngOnDestroy() {
this._manager.deleteMapLayer(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 { MapLayerManager } from '../services/managers/map-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
MapLayerManager, FitBoundsService
],
host: {
// todo: deprecated - we will remove it with the next version
Expand Down
28 changes: 10 additions & 18 deletions packages/core/directives/transit-layer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Directive, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { TransitLayerManager } from '../services/managers/transit-layer-manager';
import { MapLayerManager } from '../services/managers/map-layer-manager';
import { MapLayerType } from '../services/google-maps-types';

let layerId = 0;

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be static and final?


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

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

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

ngOnChanges(changes: SimpleChanges) {
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);
}
this._manager.setOptions(this, changes);
}

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

/** @internal */
name(): MapLayerType { return this._name; }

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

/** @internal */
ngOnDestroy() {
this._manager.deleteTransitLayer(this);
this._manager.deleteMapLayer(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 {MapLayerManager} from './services/managers/map-layer-manager';
15 changes: 8 additions & 7 deletions packages/core/services/google-maps-api-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,16 @@ export class GoogleMapsAPIWrapper {
}

/**
* Creates a Google Map transit layer instance add it to map
* @param {TransitLayerOptions} options - TransitLayerOptions options
* @returns {Promise<TransitLayer>} a new transit layer object
* Creates a MapLayer instance for a map
* @param {MapLayerOptions} options - used for setting layer options
* @param {string} name - the type of map layer to create
* @returns {Promise<MapLayer>} a new map layer object
*/
createTransitLayer(options: mapTypes.TransitLayerOptions): Promise<mapTypes.TransitLayer>{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really think that the google-maps-api-wrapper class should have separate methods for createTransitLayer, createBicycleLayer, createTrafficLayer since there is nothing in common between these, and it's bad practice to separate code execution by a passed in string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. I decided to go with one layerManager because Bicycling Layer and and Transit Layer has the exact methods. There is nothing different apart from the class names. I thought it would be an overkill to have 3 services with the same methods and properties, but I will separate them since that's how google does it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree to have one uniting manager. but I think there should be different methods in ApiWrapper and in google map types

createMapLayer(options: mapTypes.MapLayerOptions, name: string): Promise<mapTypes.MapLayer>{
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.MapLayer = new google.maps[`${name}`]();
newLayer.setMap(options.visible ? map : null);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra tab before newLayer

return newLayer;
});
}

Expand Down
8 changes: 5 additions & 3 deletions packages/core/services/google-maps-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,16 +435,18 @@ export interface KmlMouseEvent extends MouseEvent {
pixelOffset: Size;
}

export interface TransitLayer extends MVCObject {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Google maps has TransitLayer and BicycleLayer classes, but not MapLayer class.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you just split out the classes

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keeping one LayerManager, though, is a good idea, I just think that at the level of google apis, it should be separate since google keeps it separate

export interface MapLayer extends MVCObject {
getMap(): GoogleMap;
setMap(map: GoogleMap): void;
setOptions(options: TransitLayerOptions): void;
setOptions(options: MapLayerOptions): void;
}

export interface TransitLayerOptions {
export interface MapLayerOptions {
visible: boolean;
}

export type MapLayerType = 'TransitLayer' | 'BicyclingLayer' | 'TrafficLayer';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not a google map type, it doesn't belong here


export interface Data extends MVCObject {
features: Feature[];
addGeoJson(geoJson: Object, options?: GeoJsonOptions): Feature[];
Expand Down
111 changes: 111 additions & 0 deletions packages/core/services/managers/map-layer-manager.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
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 {MapLayerManager} from './map-layer-manager';
import {AgmBicyclingLayer} from '../../directives/bicycling-layer';

describe('MapLayerManager', () => {
beforeAll(() => {
(<any>window).google = {
maps: {
MapLayer: class MapLayer {
setMap = jest.fn();
getMap = jest.fn();
setOptions = jest.fn();

constructor() {

}
},
}
};
});

beforeEach(() => {

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

]
});
}); // end beforeEach

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

it('should call mapsApiWrapper when creating a new transit layer',
fakeAsync(inject(
[MapLayerManager, GoogleMapsAPIWrapper],
(mapLayerManager: MapLayerManager, apiWrapper: GoogleMapsAPIWrapper) => {
const transitLayer = new AgmTransitLayer(mapLayerManager);
const opt = {visible: false};
mapLayerManager.addMapLayer(transitLayer, opt);
expect(apiWrapper.createMapLayer).toHaveBeenCalledWith(opt, 'TransitLayer');

})
)
);
});

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

it('should call mapsApiWrapper when creating a new bicycling layer',
fakeAsync(inject(
[MapLayerManager, GoogleMapsAPIWrapper],
(mapLayerManager: MapLayerManager, apiWrapper: GoogleMapsAPIWrapper) => {
const bicyclingLayer = new AgmBicyclingLayer(mapLayerManager);
const opt = {visible: true};
mapLayerManager.addMapLayer(bicyclingLayer, opt);
expect(apiWrapper.createMapLayer).toHaveBeenCalledWith(opt, 'BicyclingLayer');

})
)
);
});

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

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

inject(
[MapLayerManager, GoogleMapsAPIWrapper],
(mapLayerManager: MapLayerManager,
apiWrapper: GoogleMapsAPIWrapper) => {
const newMapLayer = new AgmTransitLayer(mapLayerManager);
newMapLayer.visible = true;

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

};

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

mapLayerManager.addMapLayer(newMapLayer, {visible: true});
expect(apiWrapper.createMapLayer).toHaveBeenCalledWith({visible: true}, 'TransitLayer');

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

));

});

});
Loading