Skip to content

Commit

Permalink
feat(core): add rectangle support (#1450)
Browse files Browse the repository at this point in the history
add rectangle functionality

closes #570
  • Loading branch information
sebholstein committed Jul 4, 2018
1 parent 9c76e7e commit 2e4878b
Show file tree
Hide file tree
Showing 9 changed files with 768 additions and 3 deletions.
3 changes: 2 additions & 1 deletion packages/core/core.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {ModuleWithProviders, NgModule} from '@angular/core';
import {AgmMap} from './directives/map';
import {AgmCircle} from './directives/circle';
import {AgmRectangle} from './directives/rectangle';
import {AgmInfoWindow} from './directives/info-window';
import {AgmMarker} from './directives/marker';
import {AgmPolygon} from './directives/polygon';
Expand All @@ -18,7 +19,7 @@ import {BROWSER_GLOBALS_PROVIDERS} from './utils/browser-globals';
*/
export function coreDirectives() {
return [
AgmMap, AgmMarker, AgmInfoWindow, AgmCircle,
AgmMap, AgmMarker, AgmInfoWindow, AgmCircle, AgmRectangle,
AgmPolygon, AgmPolyline, AgmPolylinePoint, AgmKmlLayer,
AgmDataLayer
];
Expand Down
1 change: 1 addition & 0 deletions packages/core/directives.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export {AgmMap} from './directives/map';
export {AgmCircle} from './directives/circle';
export {AgmRectangle} from './directives/rectangle';
export {AgmInfoWindow} from './directives/info-window';
export {AgmKmlLayer} from './directives/kml-layer';
export {AgmDataLayer} from './directives/data-layer';
Expand Down
5 changes: 3 additions & 2 deletions packages/core/directives/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
RotateControlOptions, ScaleControlOptions, StreetViewControlOptions, ZoomControlOptions} from '../services/google-maps-types';
import {LatLngBounds, LatLngBoundsLiteral, MapTypeStyle} from '../services/google-maps-types';
import {CircleManager} from '../services/managers/circle-manager';
import {RectangleManager} from '../services/managers/rectangle-manager';
import {InfoWindowManager} from '../services/managers/info-window-manager';
import {MarkerManager} from '../services/managers/marker-manager';
import {PolygonManager} from '../services/managers/polygon-manager';
Expand Down Expand Up @@ -41,8 +42,8 @@ import {DataLayerManager} from './../services/managers/data-layer-manager';
@Component({
selector: 'agm-map',
providers: [
GoogleMapsAPIWrapper, MarkerManager, InfoWindowManager, CircleManager, PolylineManager,
PolygonManager, KmlLayerManager, DataLayerManager
GoogleMapsAPIWrapper, MarkerManager, InfoWindowManager, CircleManager, RectangleManager,
PolylineManager, PolygonManager, KmlLayerManager, DataLayerManager
],
host: {
// todo: deprecated - we will remove it with the next version
Expand Down
296 changes: 296 additions & 0 deletions packages/core/directives/rectangle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
import {
Directive,
EventEmitter,
OnChanges,
OnDestroy,
OnInit,
SimpleChange,
Input,
Output
} from '@angular/core';
import { Subscription } from 'rxjs';
import { MouseEvent } from '../map-types';
import {
LatLngBounds,
LatLngBoundsLiteral
} from '../services/google-maps-types';
import { MouseEvent as MapMouseEvent } from '../services/google-maps-types';
import { RectangleManager } from '../services/managers/rectangle-manager';

@Directive({
selector: 'agm-rectangle'
})
export class AgmRectangle implements OnInit, OnChanges, OnDestroy {
/**
* The north position of the rectangle (required).
*/
@Input() north: number;

/**
* The east position of the rectangle (required).
*/
@Input() east: number;

/**
* The south position of the rectangle (required).
*/
@Input() south: number;

/**
* The west position of the rectangle (required).
*/
@Input() west: number;

/**
* Indicates whether this Rectangle handles mouse events. Defaults to true.
*/
@Input() clickable: boolean = true;

/**
* If set to true, the user can drag this rectangle over the map. Defaults to false.
*/
// tslint:disable-next-line:no-input-rename
@Input('rectangleDraggable') draggable: boolean = false;

/**
* If set to true, the user can edit this rectangle by dragging the control points shown at
* the center and around the circumference of the rectangle. Defaults to false.
*/
@Input() editable: boolean = false;

/**
* The fill color. All CSS3 colors are supported except for extended named colors.
*/
@Input() fillColor: string;

/**
* The fill opacity between 0.0 and 1.0.
*/
@Input() fillOpacity: number;

/**
* The stroke color. All CSS3 colors are supported except for extended named colors.
*/
@Input() strokeColor: string;

/**
* The stroke opacity between 0.0 and 1.0
*/
@Input() strokeOpacity: number;

/**
* The stroke position. Defaults to CENTER.
* This property is not supported on Internet Explorer 8 and earlier.
*/
@Input() strokePosition: 'CENTER' | 'INSIDE' | 'OUTSIDE' = 'CENTER';

/**
* The stroke width in pixels.
*/
@Input() strokeWeight: number = 0;

/**
* Whether this rectangle is visible on the map. Defaults to true.
*/
@Input() visible: boolean = true;

/**
* The zIndex compared to other polys.
*/
@Input() zIndex: number;

/**
* This event is fired when the rectangle's is changed.
*/
@Output()
boundsChange: EventEmitter<LatLngBoundsLiteral> = new EventEmitter<
LatLngBoundsLiteral
>();

/**
* This event emitter gets emitted when the user clicks on the rectangle.
*/
@Output()
rectangleClick: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event emitter gets emitted when the user clicks on the rectangle.
*/
@Output()
rectangleDblClick: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is repeatedly fired while the user drags the rectangle.
*/
@Output() drag: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is fired when the user stops dragging the rectangle.
*/
@Output() dragEnd: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is fired when the user starts dragging the rectangle.
*/
@Output()
dragStart: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is fired when the DOM mousedown event is fired on the rectangle.
*/
@Output()
mouseDown: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is fired when the DOM mousemove event is fired on the rectangle.
*/
@Output()
mouseMove: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is fired on rectangle mouseout.
*/
@Output() mouseOut: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is fired on rectangle mouseover.
*/
@Output()
mouseOver: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is fired when the DOM mouseup event is fired on the rectangle.
*/
@Output() mouseUp: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is fired when the rectangle is right-clicked on.
*/
@Output()
rightClick: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

private _rectangleAddedToManager: boolean = false;

private static _mapOptions: string[] = [
'fillColor',
'fillOpacity',
'strokeColor',
'strokeOpacity',
'strokePosition',
'strokeWeight',
'visible',
'zIndex',
'clickable'
];

private _eventSubscriptions: Subscription[] = [];

constructor(private _manager: RectangleManager) {}

/** @internal */
ngOnInit() {
this._manager.addRectangle(this);
this._rectangleAddedToManager = true;
this._registerEventListeners();
}

/** @internal */
ngOnChanges(changes: { [key: string]: SimpleChange }) {
if (!this._rectangleAddedToManager) {
return;
}
if (
changes['north'] ||
changes['east'] ||
changes['south'] ||
changes['west']
) {
this._manager.setBounds(this);
}
if (changes['editable']) {
this._manager.setEditable(this);
}
if (changes['draggable']) {
this._manager.setDraggable(this);
}
if (changes['visible']) {
this._manager.setVisible(this);
}
this._updateRectangleOptionsChanges(changes);
}

private _updateRectangleOptionsChanges(changes: {
[propName: string]: SimpleChange;
}) {
let options: { [propName: string]: any } = {};
let optionKeys = Object.keys(changes).filter(
k => AgmRectangle._mapOptions.indexOf(k) !== -1
);
optionKeys.forEach(k => {
options[k] = changes[k].currentValue;
});
if (optionKeys.length > 0) {
this._manager.setOptions(this, options);
}
}

private _registerEventListeners() {
let events: Map<string, EventEmitter<any>> = new Map<
string,
EventEmitter<any>
>();
events.set('bounds_changed', this.boundsChange);
events.set('click', this.rectangleClick);
events.set('dblclick', this.rectangleDblClick);
events.set('drag', this.drag);
events.set('dragend', this.dragEnd);
events.set('dragStart', this.dragStart);
events.set('mousedown', this.mouseDown);
events.set('mousemove', this.mouseMove);
events.set('mouseout', this.mouseOut);
events.set('mouseover', this.mouseOver);
events.set('mouseup', this.mouseUp);
events.set('rightclick', this.rightClick);

events.forEach((eventEmitter, eventName) => {
this._eventSubscriptions.push(
this._manager
.createEventObservable<MapMouseEvent>(eventName, this)
.subscribe(value => {
switch (eventName) {
case 'bounds_changed':
this._manager.getBounds(this).then(bounds =>
eventEmitter.emit(<LatLngBoundsLiteral>{
north: bounds.getNorthEast().lat(),
east: bounds.getNorthEast().lng(),
south: bounds.getSouthWest().lat(),
west: bounds.getSouthWest().lng()
})
);
break;
default:
eventEmitter.emit(<MouseEvent>{
coords: { lat: value.latLng.lat(), lng: value.latLng.lng() }
});
}
})
);
});
}

/** @internal */
ngOnDestroy() {
this._eventSubscriptions.forEach(function(s: Subscription) {
s.unsubscribe();
});
this._eventSubscriptions = null;
this._manager.removeRectangle(this);
}

/**
* Gets the LatLngBounds of this Rectangle.
*/
getBounds(): Promise<LatLngBounds> {
return this._manager.getBounds(this);
}
}
1 change: 1 addition & 0 deletions packages/core/services.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export {GoogleMapsAPIWrapper} from './services/google-maps-api-wrapper';
export {CircleManager} from './services/managers/circle-manager';
export {RectangleManager} from './services/managers/rectangle-manager';
export {InfoWindowManager} from './services/managers/info-window-manager';
export {MarkerManager} from './services/managers/marker-manager';
export {PolygonManager} from './services/managers/polygon-manager';
Expand Down
10 changes: 10 additions & 0 deletions packages/core/services/google-maps-api-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ export class GoogleMapsAPIWrapper {
});
}

/**
* Creates a google.map.Rectangle for the current map.
*/
createRectangle(options: mapTypes.RectangleOptions): Promise<mapTypes.Rectangle> {
return this._map.then((map: mapTypes.GoogleMap) => {
options.map = map;
return new google.maps.Rectangle(options);
});
}

createPolyline(options: PolylineOptions): Promise<Polyline> {
return this.getNativeMap().then((map: mapTypes.GoogleMap) => {
let line = new google.maps.Polyline(options);
Expand Down

0 comments on commit 2e4878b

Please sign in to comment.