Skip to content

Commit 2b00725

Browse files
authored
feat(google-maps): Add KML Layer component to Google Maps (#19226)
* 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.
1 parent ef8fc4f commit 2b00725

File tree

8 files changed

+437
-1
lines changed

8 files changed

+437
-1
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
<map-ground-overlay *ngIf="isGroundOverlayDisplayed"
2525
[url]="groundOverlayUrl"
2626
[bounds]="groundOverlayBounds"></map-ground-overlay>
27+
<map-kml-layer *ngIf="isKmlLayerDisplayed"
28+
[url]="demoKml"></map-kml-layer>
2729
</google-map>
2830

2931
<p><label>Latitude:</label> {{display?.lat}}</p>
@@ -116,4 +118,11 @@
116118
</label>
117119
</div>
118120

121+
<div>
122+
<label for="kml-layer-checkbox">
123+
Toggle KML Layer
124+
<input type="checkbox" (click)="toggleKmlLayerDisplay()">
125+
</label>
126+
</div>
127+
119128
</div>

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export class GoogleMapDemo {
5656
isPolylineDisplayed = false;
5757
polylineOptions:
5858
google.maps.PolylineOptions = {path: POLYLINE_PATH, strokeColor: 'grey', strokeOpacity: 0.8};
59+
5960
isPolygonDisplayed = false;
6061
polygonOptions:
6162
google.maps.PolygonOptions = {paths: POLYGON_PATH, strokeColor: 'grey', strokeOpacity: 0.8};
@@ -65,6 +66,7 @@ export class GoogleMapDemo {
6566
isCircleDisplayed = false;
6667
circleOptions: google.maps.CircleOptions =
6768
{center: CIRCLE_CENTER, radius: CIRCLE_RADIUS, strokeColor: 'grey', strokeOpacity: 0.8};
69+
6870
isGroundOverlayDisplayed = false;
6971
groundOverlayImages = [
7072
{
@@ -78,6 +80,9 @@ export class GoogleMapDemo {
7880
];
7981
groundOverlayUrl = this.groundOverlayImages[0].url;
8082
groundOverlayBounds = RECTANGLE_BOUNDS;
83+
isKmlLayerDisplayed = false;
84+
demoKml =
85+
'https://developers.google.com/maps/documentation/javascript/examples/kml/westcampus.kml';
8186

8287
mapTypeId: google.maps.MapTypeId;
8388
mapTypeIds = [
@@ -163,4 +168,8 @@ export class GoogleMapDemo {
163168
groundOverlayUrlChanged(event: Event) {
164169
this.groundOverlayUrl = (event.target as HTMLSelectElement).value;
165170
}
171+
172+
toggleKmlLayerDisplay() {
173+
this.isKmlLayerDisplayed = !this.isKmlLayerDisplayed;
174+
}
166175
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {GoogleMap} from './google-map/google-map';
1212
import {MapCircle} from './map-circle/map-circle';
1313
import {MapGroundOverlay} from './map-ground-overlay/map-ground-overlay';
1414
import {MapInfoWindow} from './map-info-window/map-info-window';
15+
import {MapKmlLayer} from './map-kml-layer/map-kml-layer';
1516
import {MapMarker} from './map-marker/map-marker';
1617
import {MapPolygon} from './map-polygon/map-polygon';
1718
import {MapPolyline} from './map-polyline/map-polyline';
@@ -22,6 +23,7 @@ const COMPONENTS = [
2223
MapCircle,
2324
MapGroundOverlay,
2425
MapInfoWindow,
26+
MapKmlLayer,
2527
MapMarker,
2628
MapPolygon,
2729
MapPolyline,
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import {Component, ViewChild} from '@angular/core';
2+
import {async, TestBed} from '@angular/core/testing';
3+
import {By} from '@angular/platform-browser';
4+
5+
import {DEFAULT_OPTIONS} from '../google-map/google-map';
6+
import {GoogleMapsModule} from '../google-maps-module';
7+
import {
8+
createKmlLayerConstructorSpy,
9+
createKmlLayerSpy,
10+
createMapConstructorSpy,
11+
createMapSpy,
12+
} from '../testing/fake-google-map-utils';
13+
14+
import {MapKmlLayer} from './map-kml-layer';
15+
16+
const DEMO_URL = 'www.test.kml';
17+
const DEFAULT_KML_OPTIONS: google.maps.KmlLayerOptions = {
18+
clickable: true,
19+
preserveViewport: true,
20+
url: DEMO_URL,
21+
};
22+
23+
describe('MapKmlLayer', () => {
24+
let mapSpy: jasmine.SpyObj<google.maps.Map>;
25+
26+
beforeEach(async(() => {
27+
TestBed.configureTestingModule({
28+
imports: [GoogleMapsModule],
29+
declarations: [TestApp],
30+
});
31+
}));
32+
33+
beforeEach(() => {
34+
TestBed.compileComponents();
35+
36+
mapSpy = createMapSpy(DEFAULT_OPTIONS);
37+
createMapConstructorSpy(mapSpy).and.callThrough();
38+
});
39+
40+
afterEach(() => {
41+
delete window.google;
42+
});
43+
44+
it('initializes a Google Map Kml Layer', () => {
45+
const kmlLayerSpy = createKmlLayerSpy({});
46+
const kmlLayerConstructorSpy = createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();
47+
48+
const fixture = TestBed.createComponent(TestApp);
49+
fixture.detectChanges();
50+
51+
expect(kmlLayerConstructorSpy).toHaveBeenCalledWith({url: undefined});
52+
expect(kmlLayerSpy.setMap).toHaveBeenCalledWith(mapSpy);
53+
});
54+
55+
it('sets url from input', () => {
56+
const options: google.maps.KmlLayerOptions = {url: DEMO_URL};
57+
const kmlLayerSpy = createKmlLayerSpy(options);
58+
const kmlLayerConstructorSpy = createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();
59+
60+
const fixture = TestBed.createComponent(TestApp);
61+
fixture.componentInstance.url = DEMO_URL;
62+
fixture.detectChanges();
63+
64+
expect(kmlLayerConstructorSpy).toHaveBeenCalledWith(options);
65+
});
66+
67+
it('gives precedence to url input over options', () => {
68+
const expectedUrl = 'www.realurl.kml';
69+
const expectedOptions: google.maps.KmlLayerOptions = {...DEFAULT_KML_OPTIONS, url: expectedUrl};
70+
const kmlLayerSpy = createKmlLayerSpy(expectedOptions);
71+
const kmlLayerConstructorSpy = createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();
72+
73+
const fixture = TestBed.createComponent(TestApp);
74+
fixture.componentInstance.options = DEFAULT_KML_OPTIONS;
75+
fixture.componentInstance.url = expectedUrl;
76+
fixture.detectChanges();
77+
78+
expect(kmlLayerConstructorSpy).toHaveBeenCalledWith(expectedOptions);
79+
});
80+
81+
it('exposes methods that provide information about the KmlLayer', () => {
82+
const kmlLayerSpy = createKmlLayerSpy(DEFAULT_KML_OPTIONS);
83+
createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();
84+
85+
const fixture = TestBed.createComponent(TestApp);
86+
const kmlLayerComponent = fixture.debugElement.query(By.directive(
87+
MapKmlLayer))!.injector.get<MapKmlLayer>(MapKmlLayer);
88+
fixture.detectChanges();
89+
90+
kmlLayerComponent.getDefaultViewport();
91+
expect(kmlLayerSpy.getDefaultViewport).toHaveBeenCalled();
92+
93+
const metadata: google.maps.KmlLayerMetadata = {
94+
author: {
95+
email: 'test@test.com',
96+
name: 'author',
97+
uri: 'www.author.com',
98+
},
99+
description: 'test',
100+
hasScreenOverlays: true,
101+
name: 'metadata',
102+
snippet: '...',
103+
};
104+
kmlLayerSpy.getMetadata.and.returnValue(metadata);
105+
expect(kmlLayerComponent.getMetadata()).toBe(metadata);
106+
107+
kmlLayerComponent.getStatus();
108+
expect(kmlLayerSpy.getStatus).toHaveBeenCalled();
109+
110+
kmlLayerSpy.getUrl.and.returnValue(DEMO_URL);
111+
expect(kmlLayerComponent.getUrl()).toBe(DEMO_URL);
112+
113+
kmlLayerSpy.getZIndex.and.returnValue(3);
114+
expect(kmlLayerComponent.getZIndex()).toBe(3);
115+
});
116+
117+
it('initializes KmlLayer event handlers', () => {
118+
const kmlLayerSpy = createKmlLayerSpy(DEFAULT_KML_OPTIONS);
119+
createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();
120+
121+
const addSpy = kmlLayerSpy.addListener;
122+
const fixture = TestBed.createComponent(TestApp);
123+
fixture.detectChanges();
124+
125+
expect(addSpy).toHaveBeenCalledWith('click', jasmine.any(Function));
126+
expect(addSpy).not.toHaveBeenCalledWith('defaultviewport_changed', jasmine.any(Function));
127+
expect(addSpy).toHaveBeenCalledWith('status_changed', jasmine.any(Function));
128+
});
129+
130+
it('should be able to add an event listener after init', () => {
131+
const kmlLayerSpy = createKmlLayerSpy(DEFAULT_KML_OPTIONS);
132+
createKmlLayerConstructorSpy(kmlLayerSpy).and.callThrough();
133+
134+
const addSpy = kmlLayerSpy.addListener;
135+
const fixture = TestBed.createComponent(TestApp);
136+
fixture.detectChanges();
137+
138+
expect(addSpy).not.toHaveBeenCalledWith('defaultviewport_changed', jasmine.any(Function));
139+
140+
// Pick an event that isn't bound in the template.
141+
const subscription = fixture.componentInstance.kmlLayer.defaultviewportChanged.subscribe();
142+
fixture.detectChanges();
143+
144+
expect(addSpy).toHaveBeenCalledWith('defaultviewport_changed', jasmine.any(Function));
145+
subscription.unsubscribe();
146+
});
147+
});
148+
149+
@Component({
150+
selector: 'test-app',
151+
template: `<google-map>
152+
<map-kml-layer [options]="options"
153+
[url]="url"
154+
(kmlClick)="handleClick()"
155+
(statusChanged)="handleStatusChange()">
156+
</map-kml-layer>
157+
</google-map>`,
158+
})
159+
class TestApp {
160+
@ViewChild(MapKmlLayer) kmlLayer: MapKmlLayer;
161+
options?: google.maps.KmlLayerOptions;
162+
url?: string;
163+
164+
handleClick() {}
165+
166+
handleStatusChange() {}
167+
}

0 commit comments

Comments
 (0)