Skip to content

Commit

Permalink
feat(google-maps): Add KML Layer component to Google Maps (#19226)
Browse files Browse the repository at this point in the history
* feat(google-maps): Add KML Layer component to Google Maps

Implement KML layers in @angular/google-maps. This allows users to
specify a .kml file that configures many options that display on a
Google Map. See
https://developers.google.com/maps/documentation/javascript/reference/kml

* feat(google-maps): Add KML Layer component

Remove non-null assertions from KML Layer component.

* feat(google-maps): Add KML Layer component

Add exportAs to the directive to match the other components.

* feat(google-maps): Add KML Layer Component

Fix test failure caused by merge.
  • Loading branch information
mbehrlich committed Jul 27, 2020
1 parent ef8fc4f commit 2b00725
Show file tree
Hide file tree
Showing 8 changed files with 437 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/dev-app/google-map/google-map-demo.html
Expand Up @@ -24,6 +24,8 @@
<map-ground-overlay *ngIf="isGroundOverlayDisplayed"
[url]="groundOverlayUrl"
[bounds]="groundOverlayBounds"></map-ground-overlay>
<map-kml-layer *ngIf="isKmlLayerDisplayed"
[url]="demoKml"></map-kml-layer>
</google-map>

<p><label>Latitude:</label> {{display?.lat}}</p>
Expand Down Expand Up @@ -116,4 +118,11 @@
</label>
</div>

<div>
<label for="kml-layer-checkbox">
Toggle KML Layer
<input type="checkbox" (click)="toggleKmlLayerDisplay()">
</label>
</div>

</div>
9 changes: 9 additions & 0 deletions src/dev-app/google-map/google-map-demo.ts
Expand Up @@ -56,6 +56,7 @@ export class GoogleMapDemo {
isPolylineDisplayed = false;
polylineOptions:
google.maps.PolylineOptions = {path: POLYLINE_PATH, strokeColor: 'grey', strokeOpacity: 0.8};

isPolygonDisplayed = false;
polygonOptions:
google.maps.PolygonOptions = {paths: POLYGON_PATH, strokeColor: 'grey', strokeOpacity: 0.8};
Expand All @@ -65,6 +66,7 @@ export class GoogleMapDemo {
isCircleDisplayed = false;
circleOptions: google.maps.CircleOptions =
{center: CIRCLE_CENTER, radius: CIRCLE_RADIUS, strokeColor: 'grey', strokeOpacity: 0.8};

isGroundOverlayDisplayed = false;
groundOverlayImages = [
{
Expand All @@ -78,6 +80,9 @@ export class GoogleMapDemo {
];
groundOverlayUrl = this.groundOverlayImages[0].url;
groundOverlayBounds = RECTANGLE_BOUNDS;
isKmlLayerDisplayed = false;
demoKml =
'https://developers.google.com/maps/documentation/javascript/examples/kml/westcampus.kml';

mapTypeId: google.maps.MapTypeId;
mapTypeIds = [
Expand Down Expand Up @@ -163,4 +168,8 @@ export class GoogleMapDemo {
groundOverlayUrlChanged(event: Event) {
this.groundOverlayUrl = (event.target as HTMLSelectElement).value;
}

toggleKmlLayerDisplay() {
this.isKmlLayerDisplayed = !this.isKmlLayerDisplayed;
}
}
2 changes: 2 additions & 0 deletions src/google-maps/google-maps-module.ts
Expand Up @@ -12,6 +12,7 @@ import {GoogleMap} from './google-map/google-map';
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';
import {MapKmlLayer} from './map-kml-layer/map-kml-layer';
import {MapMarker} from './map-marker/map-marker';
import {MapPolygon} from './map-polygon/map-polygon';
import {MapPolyline} from './map-polyline/map-polyline';
Expand All @@ -22,6 +23,7 @@ const COMPONENTS = [
MapCircle,
MapGroundOverlay,
MapInfoWindow,
MapKmlLayer,
MapMarker,
MapPolygon,
MapPolyline,
Expand Down
167 changes: 167 additions & 0 deletions src/google-maps/map-kml-layer/map-kml-layer.spec.ts
@@ -0,0 +1,167 @@
import {Component, ViewChild} from '@angular/core';
import {async, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';

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

import {MapKmlLayer} from './map-kml-layer';

const DEMO_URL = 'www.test.kml';
const DEFAULT_KML_OPTIONS: google.maps.KmlLayerOptions = {
clickable: true,
preserveViewport: true,
url: DEMO_URL,
};

describe('MapKmlLayer', () => {
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 Kml Layer', () => {
const kmlLayerSpy = createKmlLayerSpy({});
const kmlLayerConstructorSpy = createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();

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

expect(kmlLayerConstructorSpy).toHaveBeenCalledWith({url: undefined});
expect(kmlLayerSpy.setMap).toHaveBeenCalledWith(mapSpy);
});

it('sets url from input', () => {
const options: google.maps.KmlLayerOptions = {url: DEMO_URL};
const kmlLayerSpy = createKmlLayerSpy(options);
const kmlLayerConstructorSpy = createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();

const fixture = TestBed.createComponent(TestApp);
fixture.componentInstance.url = DEMO_URL;
fixture.detectChanges();

expect(kmlLayerConstructorSpy).toHaveBeenCalledWith(options);
});

it('gives precedence to url input over options', () => {
const expectedUrl = 'www.realurl.kml';
const expectedOptions: google.maps.KmlLayerOptions = {...DEFAULT_KML_OPTIONS, url: expectedUrl};
const kmlLayerSpy = createKmlLayerSpy(expectedOptions);
const kmlLayerConstructorSpy = createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();

const fixture = TestBed.createComponent(TestApp);
fixture.componentInstance.options = DEFAULT_KML_OPTIONS;
fixture.componentInstance.url = expectedUrl;
fixture.detectChanges();

expect(kmlLayerConstructorSpy).toHaveBeenCalledWith(expectedOptions);
});

it('exposes methods that provide information about the KmlLayer', () => {
const kmlLayerSpy = createKmlLayerSpy(DEFAULT_KML_OPTIONS);
createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();

const fixture = TestBed.createComponent(TestApp);
const kmlLayerComponent = fixture.debugElement.query(By.directive(
MapKmlLayer))!.injector.get<MapKmlLayer>(MapKmlLayer);
fixture.detectChanges();

kmlLayerComponent.getDefaultViewport();
expect(kmlLayerSpy.getDefaultViewport).toHaveBeenCalled();

const metadata: google.maps.KmlLayerMetadata = {
author: {
email: 'test@test.com',
name: 'author',
uri: 'www.author.com',
},
description: 'test',
hasScreenOverlays: true,
name: 'metadata',
snippet: '...',
};
kmlLayerSpy.getMetadata.and.returnValue(metadata);
expect(kmlLayerComponent.getMetadata()).toBe(metadata);

kmlLayerComponent.getStatus();
expect(kmlLayerSpy.getStatus).toHaveBeenCalled();

kmlLayerSpy.getUrl.and.returnValue(DEMO_URL);
expect(kmlLayerComponent.getUrl()).toBe(DEMO_URL);

kmlLayerSpy.getZIndex.and.returnValue(3);
expect(kmlLayerComponent.getZIndex()).toBe(3);
});

it('initializes KmlLayer event handlers', () => {
const kmlLayerSpy = createKmlLayerSpy(DEFAULT_KML_OPTIONS);
createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();

const addSpy = kmlLayerSpy.addListener;
const fixture = TestBed.createComponent(TestApp);
fixture.detectChanges();

expect(addSpy).toHaveBeenCalledWith('click', jasmine.any(Function));
expect(addSpy).not.toHaveBeenCalledWith('defaultviewport_changed', jasmine.any(Function));
expect(addSpy).toHaveBeenCalledWith('status_changed', jasmine.any(Function));
});

it('should be able to add an event listener after init', () => {
const kmlLayerSpy = createKmlLayerSpy(DEFAULT_KML_OPTIONS);
createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();

const addSpy = kmlLayerSpy.addListener;
const fixture = TestBed.createComponent(TestApp);
fixture.detectChanges();

expect(addSpy).not.toHaveBeenCalledWith('defaultviewport_changed', jasmine.any(Function));

// Pick an event that isn't bound in the template.
const subscription = fixture.componentInstance.kmlLayer.defaultviewportChanged.subscribe();
fixture.detectChanges();

expect(addSpy).toHaveBeenCalledWith('defaultviewport_changed', jasmine.any(Function));
subscription.unsubscribe();
});
});

@Component({
selector: 'test-app',
template: `<google-map>
<map-kml-layer [options]="options"
[url]="url"
(kmlClick)="handleClick()"
(statusChanged)="handleStatusChange()">
</map-kml-layer>
</google-map>`,
})
class TestApp {
@ViewChild(MapKmlLayer) kmlLayer: MapKmlLayer;
options?: google.maps.KmlLayerOptions;
url?: string;

handleClick() {}

handleStatusChange() {}
}

0 comments on commit 2b00725

Please sign in to comment.