Skip to content

fix(google-maps): allow ground overlay bounds to be changed #20873

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
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
23 changes: 16 additions & 7 deletions src/google-maps/map-ground-overlay/map-ground-overlay.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,6 @@ describe('MapGroundOverlay', () => {
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(mapSpy);
});

it('has an error if required bounds are not provided', () => {
expect(() => {
const fixture = TestBed.createComponent(TestApp);
fixture.detectChanges();
}).toThrow(new Error('Image bounds are required'));
});

it('exposes methods that provide information about the Ground Overlay', () => {
const groundOverlaySpy = createGroundOverlaySpy(url, bounds, groundOverlayOptions);
createGroundOverlayConstructorSpy(groundOverlaySpy).and.callThrough();
Expand Down Expand Up @@ -143,6 +136,22 @@ describe('MapGroundOverlay', () => {
expect(groundOverlaySpy.setMap).toHaveBeenCalledWith(mapSpy);
});

it('should recreate the ground overlay when the bounds change', () => {
const groundOverlaySpy = createGroundOverlaySpy(url, bounds, groundOverlayOptions);
createGroundOverlayConstructorSpy(groundOverlaySpy).and.callThrough();

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

const oldOverlay = fixture.componentInstance.groundOverlay.groundOverlay;
fixture.componentInstance.bounds = {...bounds};
fixture.detectChanges();

const newOverlay = fixture.componentInstance.groundOverlay.groundOverlay;
expect(newOverlay).toBeTruthy();
expect(newOverlay).not.toBe(oldOverlay);
});

});

@Component({
Expand Down
56 changes: 32 additions & 24 deletions src/google-maps/map-ground-overlay/map-ground-overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import {Directive, Input, NgZone, OnDestroy, OnInit, Output} from '@angular/core';
import {BehaviorSubject, Observable, Subject} from 'rxjs';
import {map, take, takeUntil} from 'rxjs/operators';
import {takeUntil} from 'rxjs/operators';

import {GoogleMap} from '../google-map/google-map';
import {MapEventManager} from '../map-event-manager';
Expand All @@ -30,6 +30,9 @@ export class MapGroundOverlay implements OnInit, OnDestroy {

private readonly _opacity = new BehaviorSubject<number>(1);
private readonly _url = new BehaviorSubject<string>('');
private readonly _bounds =
new BehaviorSubject<google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral|undefined>(
undefined);
private readonly _destroyed = new Subject<void>();

/**
Expand All @@ -46,7 +49,13 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
}

/** Bounds for the overlay. */
@Input() bounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral;
@Input()
get bounds(): google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral {
return this._bounds.value!;
}
set bounds(bounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral) {
this._bounds.next(bounds);
}

/** Whether the overlay is clickable */
@Input() clickable: boolean = false;
Expand Down Expand Up @@ -77,21 +86,30 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
constructor(private readonly _map: GoogleMap, private readonly _ngZone: NgZone) {}

ngOnInit() {
if (!this.bounds && (typeof ngDevMode === 'undefined' || ngDevMode)) {
throw Error('Image bounds are required');
}
if (this._map._isBrowser) {
this._combineOptions().pipe(take(1)).subscribe(options => {
// The ground overlay setup is slightly different from the other Google Maps objects in that
// we have to recreate the `GroundOverlay` object whenever the bounds change, because
// Google Maps doesn't provide an API to update the bounds of an existing overlay.
this._bounds.pipe(takeUntil(this._destroyed)).subscribe(bounds => {
if (this.groundOverlay) {
this.groundOverlay.setMap(null);
this.groundOverlay = undefined;
}

// Create the object outside the zone so its events don't trigger change detection.
// We'll bring it back in inside the `MapEventManager` only for the events that the
// user has subscribed to.
this._ngZone.runOutsideAngular(() => {
this.groundOverlay =
new google.maps.GroundOverlay(this._url.getValue(), this.bounds, options);
});
this._assertInitialized();
this.groundOverlay.setMap(this._map.googleMap!);
this._eventManager.setTarget(this.groundOverlay);
if (bounds) {
this._ngZone.runOutsideAngular(() => {
this.groundOverlay = new google.maps.GroundOverlay(this._url.getValue(), bounds, {
clickable: this.clickable,
opacity: this._opacity.value,
});
});
this._assertInitialized();
this.groundOverlay.setMap(this._map.googleMap!);
this._eventManager.setTarget(this.groundOverlay);
}
});

this._watchForOpacityChanges();
Expand Down Expand Up @@ -138,19 +156,9 @@ export class MapGroundOverlay implements OnInit, OnDestroy {
return this.groundOverlay.getUrl();
}

private _combineOptions(): Observable<google.maps.GroundOverlayOptions> {
return this._opacity.pipe(map(opacity => {
const combinedOptions: google.maps.GroundOverlayOptions = {
clickable: this.clickable,
opacity,
};
return combinedOptions;
}));
}

private _watchForOpacityChanges() {
this._opacity.pipe(takeUntil(this._destroyed)).subscribe(opacity => {
if (opacity) {
if (opacity != null) {
this._assertInitialized();
this.groundOverlay.setOpacity(opacity);
}
Expand Down
3 changes: 2 additions & 1 deletion tools/public_api_guard/google-maps/google-maps.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ export declare class MapCircle implements OnInit, OnDestroy {
}

export declare class MapGroundOverlay implements OnInit, OnDestroy {
bounds: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
get bounds(): google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
set bounds(bounds: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral);
clickable: boolean;
groundOverlay?: google.maps.GroundOverlay;
mapClick: Observable<google.maps.MouseEvent>;
Expand Down