Skip to content

Commit

Permalink
feat(polylines): add support for Polylines
Browse files Browse the repository at this point in the history
Closes #554
  • Loading branch information
andir authored and sebholstein committed Aug 15, 2016
1 parent 836078a commit a0ba861
Show file tree
Hide file tree
Showing 11 changed files with 505 additions and 4 deletions.
8 changes: 6 additions & 2 deletions src/core/directives-const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import {SebmGoogleMap} from './directives/google-map';
import {SebmGoogleMapCircle} from './directives/google-map-circle';
import {SebmGoogleMapInfoWindow} from './directives/google-map-info-window';
import {SebmGoogleMapMarker} from './directives/google-map-marker';
import {SebmGoogleMapPolyline} from './directives/google-map-polyline';
import {SebmGoogleMapPolylinePoint} from './directives/google-map-polyline-point';

export const GOOGLE_MAPS_DIRECTIVES: any[] =
[SebmGoogleMap, SebmGoogleMapMarker, SebmGoogleMapInfoWindow, SebmGoogleMapCircle];
export const GOOGLE_MAPS_DIRECTIVES: any[] = [
SebmGoogleMap, SebmGoogleMapMarker, SebmGoogleMapInfoWindow, SebmGoogleMapCircle,
SebmGoogleMapPolyline, SebmGoogleMapPolylinePoint
];
2 changes: 2 additions & 0 deletions src/core/directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export {SebmGoogleMap} from './directives/google-map';
export {SebmGoogleMapCircle} from './directives/google-map-circle';
export {SebmGoogleMapInfoWindow} from './directives/google-map-info-window';
export {SebmGoogleMapMarker} from './directives/google-map-marker';
export {SebmGoogleMapPolyline} from './directives/google-map-polyline';
export {SebmGoogleMapPolylinePoint} from './directives/google-map-polyline-point';
36 changes: 36 additions & 0 deletions src/core/directives/google-map-polyline-point.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {Directive, EventEmitter, Input, OnChanges, Output, SimpleChanges} from '@angular/core';
import {LatLngLiteral} from '../../core/services/google-maps-types';

/**
* SebmGoogleMapPolylinePoint represents one element of a polyline within a {@link
* SembGoogleMapPolyline}
*/
@Directive({selector: 'sebm-google-map-polyline-point'})
export class SebmGoogleMapPolylinePoint implements OnChanges {
/**
* The latitude position of the point.
*/
@Input() public latitude: number;

/**
* The longitude position of the point;
*/
@Input() public longitude: number;

/**
* This event emitter gets emitted when the position of the point changed.
*/
@Output() positionChanged: EventEmitter<LatLngLiteral> = new EventEmitter<LatLngLiteral>();

constructor() {}

ngOnChanges(changes: SimpleChanges): any {
if (changes['latitude'] || changes['longitude']) {
const position: LatLngLiteral = <LatLngLiteral>{
lat: changes['latitude'].currentValue,
lng: changes['longitude'].currentValue
};
this.positionChanged.emit(position);
}
}
}
245 changes: 245 additions & 0 deletions src/core/directives/google-map-polyline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import {AfterContentInit, ContentChildren, Directive, EventEmitter, OnChanges, OnDestroy, QueryList, SimpleChanges} from '@angular/core';
import {Subscription} from 'rxjs/Rx';

import {PolyMouseEvent} from '../services/google-maps-types';
import {PolylineManager} from '../services/managers/polyline-manager';

import {SebmGoogleMapPolylinePoint} from './google-map-polyline-point';

let polylineId = 0;
/**
* SebmGoogleMapPolyline renders a polyline on a {@link SebmGoogleMap}
*
* ### Example
* ```typescript
* import {Component} from 'angular2/core';
* import {SebmGoogleMap, SebmGooglePolyline, SebmGooglePolylinePoint} from
* 'angular2-google-maps/core';
*
* @Component({
* selector: 'my-map-cmp',
* directives: [SebmGoogleMap, SebmGooglePolyline, SebmGooglePolylinePoint],
* styles: [`
* .sebm-google-map-container {
* height: 300px;
* }
* `],
* template: `
* <sebm-google-map [latitude]="lat" [longitude]="lng" [zoom]="zoom">
* <sebm-google-map-polyline>
* <sebm-google-map-polyline-point [latitude]="latA" [longitude]="lngA">
* </sebm-google-map-polyline-point>
* <sebm-google-map-polyline-point [latitude]="latB" [longitude]="lngB">
* </sebm-google-map-polyline-point>
* </sebm-google-map-polyline>
* </sebm-google-map>
* `
* })
* ```
*/
@Directive({
selector: 'sebm-google-map-polyline',
inputs: [
'clickable', 'draggable: polylineDraggable', 'editable', 'geodesic', 'strokeColor',
'strokeWeight', 'strokeOpacity', 'visible', 'zIndex'
],
outputs: [
'lineClick', 'lineDblClick', 'lineDrag', 'lineDragEnd', 'lineMouseDown', 'lineMouseMove',
'lineMouseOut', 'lineMouseOver', 'lineMouseUp', 'lineRightClick'
]
})
export class SebmGoogleMapPolyline implements OnDestroy, OnChanges, AfterContentInit {
/**
* Indicates whether this Polyline handles mouse events. Defaults to true.
*/
clickable: boolean = true;

/**
* If set to true, the user can drag this shape over the map. The geodesic property defines the
* mode of dragging. Defaults to false.
*/
draggable: boolean = false;

/**
* If set to true, the user can edit this shape by dragging the control points shown at the
* vertices and on each segment. Defaults to false.
*/
editable: boolean = false;

/**
* When true, edges of the polygon are interpreted as geodesic and will follow the curvature of
* the Earth. When false, edges of the polygon are rendered as straight lines in screen space.
* Note that the shape of a geodesic polygon may appear to change when dragged, as the dimensions
* are maintained relative to the surface of the earth. Defaults to false.
*/
geodesic: boolean = false;

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

/**
* The stroke opacity between 0.0 and 1.0.
*/
strokeOpacity: number;

/**
* The stroke width in pixels.
*/
strokeWeight: number;

/**
* Whether this polyline is visible on the map. Defaults to true.
*/
visible: boolean = true;

/**
* The zIndex compared to other polys.
*/
zIndex: number;

/**
* This event is fired when the DOM click event is fired on the Polyline.
*/
lineClick: EventEmitter<PolyMouseEvent> = new EventEmitter<PolyMouseEvent>();

/**
* This event is fired when the DOM dblclick event is fired on the Polyline.
*/
lineDblClick: EventEmitter<PolyMouseEvent> = new EventEmitter<PolyMouseEvent>();

/**
* This event is repeatedly fired while the user drags the polyline.
*/
lineDrag: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is fired when the user stops dragging the polyline.
*/
lineDragEnd: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is fired when the user starts dragging the polyline.
*/
lineDragStart: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();

/**
* This event is fired when the DOM mousedown event is fired on the Polyline.
*/
lineMouseDown: EventEmitter<PolyMouseEvent> = new EventEmitter<PolyMouseEvent>();

/**
* This event is fired when the DOM mousemove event is fired on the Polyline.
*/
lineMouseMove: EventEmitter<PolyMouseEvent> = new EventEmitter<PolyMouseEvent>();

/**
* This event is fired on Polyline mouseout.
*/
lineMouseOut: EventEmitter<PolyMouseEvent> = new EventEmitter<PolyMouseEvent>();

/**
* This event is fired on Polyline mouseover.
*/
lineMouseOver: EventEmitter<PolyMouseEvent> = new EventEmitter<PolyMouseEvent>();

/**
* This event is fired whe the DOM mouseup event is fired on the Polyline
*/
lineMouseUp: EventEmitter<PolyMouseEvent> = new EventEmitter<PolyMouseEvent>();

/**
* This even is fired when the Polyline is right-clicked on.
*/
lineRightClick: EventEmitter<PolyMouseEvent> = new EventEmitter<PolyMouseEvent>();

@ContentChildren(SebmGoogleMapPolylinePoint)
private _points: QueryList<SebmGoogleMapPolylinePoint>;

private static _polylineOptionsAttributes: Array<string> = [
'draggable', 'editable', 'visible', 'geodesic', 'strokeColor', 'strokeOpacity', 'strokeWeight',
'zIndex'
];

private _id: string;
private _polylineAddedToManager: boolean = false;
private _subscriptions: Subscription[] = [];

constructor(private _polylineManager: PolylineManager) { this._id = (polylineId++).toString(); }

/** @internal */
ngAfterContentInit() {
if (this._points.length) {
this._points.forEach((point: SebmGoogleMapPolylinePoint) => {
const s = point.positionChanged.subscribe(
() => { this._polylineManager.updatePolylinePoints(this); });
this._subscriptions.push(s);
});
}
if (!this._polylineAddedToManager) {
this._init();
}
const s =
this._points.changes.subscribe(() => this._polylineManager.updatePolylinePoints(this));
this._subscriptions.push(s);
this._polylineManager.updatePolylinePoints(this);
}

ngOnChanges(changes: SimpleChanges): any {
if (!this._polylineAddedToManager) {
this._init();
return;
}

let options: {[propName: string]: any} = {};
const optionKeys = Object.keys(changes).filter(
k => SebmGoogleMapPolyline._polylineOptionsAttributes.indexOf(k) !== -1);
optionKeys.forEach(k => options[k] = changes[k].currentValue);
this._polylineManager.setPolylineOptions(this, options);
}

private _init() {
this._polylineManager.addPolyline(this);
this._polylineAddedToManager = true;
this._addEventListeners();
}

private _addEventListeners() {
const handlers = [
{name: 'click', handler: (ev: PolyMouseEvent) => this.lineClick.emit(ev)},
{name: 'dbclick', handler: (ev: PolyMouseEvent) => this.lineDblClick.emit(ev)},
{name: 'drag', handler: (ev: MouseEvent) => this.lineDrag.emit(ev)},
{name: 'dragend', handler: (ev: MouseEvent) => this.lineDragEnd.emit(ev)},
{name: 'dragstart', handler: (ev: MouseEvent) => this.lineDragStart.emit(ev)},
{name: 'mousedown', handler: (ev: PolyMouseEvent) => this.lineMouseDown.emit(ev)},
{name: 'mousemove', handler: (ev: PolyMouseEvent) => this.lineMouseMove.emit(ev)},
{name: 'mouseout', handler: (ev: PolyMouseEvent) => this.lineMouseOut.emit(ev)},
{name: 'mouseover', handler: (ev: PolyMouseEvent) => this.lineMouseOver.emit(ev)},
{name: 'mouseup', handler: (ev: PolyMouseEvent) => this.lineMouseUp.emit(ev)},
{name: 'rightclick', handler: (ev: PolyMouseEvent) => this.lineRightClick.emit(ev)},
];
handlers.forEach((obj) => {
const os = this._polylineManager.createEventObservable(obj.name, this).subscribe(obj.handler);
this._subscriptions.push(os);
});
}

/** @internal */
_getPoints(): Array<SebmGoogleMapPolylinePoint> {
if (this._points) {
return this._points.toArray();
}
return [];
}

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

/** @internal */
ngOnDestroy() {
this._polylineManager.deletePolyline(this);
// unsubscribe all registered observable subscriptions
this._subscriptions.forEach((s) => s.unsubscribe());
}
}
4 changes: 3 additions & 1 deletion src/core/directives/google-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {LatLngBounds, LatLngBoundsLiteral, MapTypeStyle} from '../services/googl
import {CircleManager} from '../services/managers/circle-manager';
import {InfoWindowManager} from '../services/managers/info-window-manager';
import {MarkerManager} from '../services/managers/marker-manager';
import {PolylineManager} from '../services/managers/polyline-manager';

/**
* SebMGoogleMap renders a Google Map.
Expand Down Expand Up @@ -36,7 +37,8 @@ import {MarkerManager} from '../services/managers/marker-manager';
*/
@Component({
selector: 'sebm-google-map',
providers: [GoogleMapsAPIWrapper, MarkerManager, InfoWindowManager, CircleManager],
providers:
[GoogleMapsAPIWrapper, MarkerManager, InfoWindowManager, CircleManager, PolylineManager],
inputs: [
'longitude', 'latitude', 'zoom', 'draggable: mapDraggable', 'disableDoubleClickZoom',
'disableDefaultUI', 'scrollwheel', 'backgroundColor', 'draggableCursor', 'draggingCursor',
Expand Down
2 changes: 1 addition & 1 deletion src/core/map-types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {LatLngLiteral} from './services/google-maps-types';

// exported map types
export {LatLngBounds, LatLngBoundsLiteral, LatLngLiteral} from './services/google-maps-types';
export {LatLngBounds, LatLngBoundsLiteral, LatLngLiteral, PolyMouseEvent} from './services/google-maps-types';

/**
* MouseEvent gets emitted when the user triggers mouse events on the map.
Expand Down
1 change: 1 addition & 0 deletions src/core/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export {GoogleMapsAPIWrapper} from './services/google-maps-api-wrapper';
export {CircleManager} from './services/managers/circle-manager';
export {InfoWindowManager} from './services/managers/info-window-manager';
export {MarkerManager} from './services/managers/marker-manager';
export {PolylineManager} from './services/managers/polyline-manager';
export {GoogleMapsScriptProtocol, LazyMapsAPILoader, LazyMapsAPILoaderConfig, LazyMapsAPILoaderConfigLiteral, provideLazyMapsAPILoaderConfig} from './services/maps-api-loader/lazy-maps-api-loader';
export {MapsAPILoader} from './services/maps-api-loader/maps-api-loader';
export {NoOpMapsAPILoader} from './services/maps-api-loader/noop-maps-api-loader';
10 changes: 10 additions & 0 deletions src/core/services/google-maps-api-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';

import * as mapTypes from './google-maps-types';
import {Polyline} from './google-maps-types';
import {PolylineOptions} from './google-maps-types';
import {MapsAPILoader} from './maps-api-loader/maps-api-loader';

// todo: add types for this
Expand Down Expand Up @@ -59,6 +61,14 @@ export class GoogleMapsAPIWrapper {
});
}

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

subscribeToMapEvent<E>(eventName: string): Observable<E> {
return Observable.create((observer: Observer<E>) => {
this._map.then((m: mapTypes.GoogleMap) => {
Expand Down
Loading

0 comments on commit a0ba861

Please sign in to comment.