infusion-code/angular-maps

View on GitHub
src/services/google/google-layer-base.ts

Summary

Maintainability
C
1 day
Test Coverage
import { Injectable, NgZone } from '@angular/core';
import { IMarkerOptions } from '../../interfaces/imarker-options';
import { IMarkerIconInfo } from '../../interfaces/imarker-icon-info';
import { Marker } from '../../models/marker';
import { Layer } from '../../models/layer';
import { MarkerTypeId } from '../../models/marker-type-id';
import { MapService } from '../map.service';
import { MapLayerDirective } from '../../components/map-layer';
import { LayerService } from '../layer.service';
import { GoogleMapService } from './google-map.service';
import { GoogleConversions } from './google-conversions';
import { GoogleMarker } from '../../models/google/google-marker';
import * as GoogleMapTypes from './google-map-types';

declare var google: any;

/**
 * This abstract partially implements the contract for the {@link LayerService}
 * and {@link ClusterService} for the Google Maps archtiecture. It serves
 * as the base class for basic layer ({@link GoogleLayerService}) and cluster layer ({@link GoogleClusterLayer}).
 *
 * @export
 * @abstract
 */
export abstract class GoogleLayerBase {

    ///
    /// Field declarations
    ///
    protected abstract _layers: Map<number, Promise<Layer>>;

    ///
    /// Constructor
    ///

    /**
     * Creates an instance of GoogleLayerBase.
     * @param _mapService - Concrete {@link MapService} implementation for Google Maps.
     * An instance of {@link GoogleMapService}.
     * @param _zone - NgZone instance to provide zone aware promises.
     *
     * @memberof GoogleLayerBase
     */
    constructor(protected _mapService: MapService, protected _zone: NgZone) { }

    ///
    /// Public methods
    ///

    /**
     * Adds a layer to the map.
     *
     * @abstract
     * @param layer - MapLayerDirective component object.
     * Generally, MapLayerDirective will be injected with an instance of the
     * LayerService and then self register on initialization.
     *
     * @memberof GoogleLayerBase
     */
    public abstract AddLayer(layer: MapLayerDirective): void;

    /**
     * Deletes the layer
     *
     * @param layer - MapLayerDirective component object for which to retrieve the layer.
     * @returns - A promise that is fullfilled when the layer has been removed.
     *
     * @memberof GoogleLayerBase
     */
    public DeleteLayer(layer: MapLayerDirective): Promise<void> {
        const l = this._layers.get(layer.Id);
        if (l == null) {
            return Promise.resolve();
        }
        return l.then((l1: Layer) => {
            return this._zone.run(() => {
                l1.Delete();
                this._layers.delete(layer.Id);
            });
        });
    }

    /**
     * Returns the Layer model represented by this layer.
     *
     * @param layer - MapLayerDirective component object or layer id for which to retrieve the layer model.
     * @returns - A promise that when resolved contains the Layer model.
     *
     * @memberof GoogleLayerBase
     */
    public GetNativeLayer(layer: MapLayerDirective|number): Promise<Layer> {
        let p: Promise<Layer> = null;
        if (typeof(layer) === 'number') {
            p = this._layers.get(layer);
        }
        else {
            p = this._layers.get((<MapLayerDirective>layer).Id);
        }
        return p;
    }

    /**
     * Creates a marker in the layer.
     *
     * @param layer - The Id of the layer in which to create the marker.
     * @param options - {@link IMarkerOptions} object containing the marker properties.
     * @returns - A promise that when fullfilled contains the {@link Marker} model for the created marker.
     *
     * @memberof GoogleLayerBase
     */
    public CreateMarker(layer: number, options: IMarkerOptions): Promise<Marker> {
        const mp: Promise<GoogleMapTypes.GoogleMap> = this._mapService.MapPromise;
        const lp: Promise<Layer> = this._layers.get(layer);

        return Promise.all([mp, lp]).then(([map, l]) => {
            const payload = (x: GoogleMapTypes.MarkerOptions): GoogleMarker => {
                const marker = new google.maps.Marker(x);
                if (options.metadata) { options.metadata.forEach((val: any, key: string) => marker.Metadata.set(key, val)); }
                marker.setMap(map);
                const m = new GoogleMarker(marker);
                m.IsFirst = options.isFirst;
                m.IsLast = options.isLast;
                if (options.metadata) { options.metadata.forEach((val: any, key: string) => m.Metadata.set(key, val)); }
                l.AddEntity(m);
                return m;
            };
            const o: GoogleMapTypes.MarkerOptions = GoogleConversions.TranslateMarkerOptions(options);
            if (options.iconInfo && options.iconInfo.markerType) {
                const s = Marker.CreateMarker(options.iconInfo);
                if (typeof(s) === 'string') {
                    o.icon = s;
                    return payload(o);
                }
                else {
                    return s.then(x => {
                        o.icon = x.icon;
                        return payload(o);
                    });
                }
            }
            else {
                return payload(o);
            }
        });
    }

    /**
     * Creates an array of unbound markers. Use this method to create arrays of markers to be used in bulk
     * operations.
     *
     * @param options - Marker options defining the markers.
     * @param markerIcon - Optional information to generate custom markers. This will be applied to all markers.
     * @returns - A promise that when fullfilled contains the an arrays of the Marker models.
     *
     * @memberof GoogleLayerBase
     */
    public CreateMarkers(options: Array<IMarkerOptions>, markerIcon?: IMarkerIconInfo): Promise<Array<Marker>> {
        const payload = (icon: string): Array<GoogleMarker> => {
            const markers: Array<GoogleMarker> = options.map(mo => {
                const o: GoogleMapTypes.MarkerOptions = GoogleConversions.TranslateMarkerOptions(mo);
                if (icon && icon !== '') { o.icon = icon; }
                const pushpin = new google.maps.Marker(o);
                const marker: GoogleMarker = new GoogleMarker(pushpin);
                marker.IsFirst = mo.isFirst;
                marker.IsLast = mo.isLast;
                if (mo.metadata) { mo.metadata.forEach((val: any, key: string) => marker.Metadata.set(key, val)); }
                return marker;
            });
            return markers;
        };
        const p: Promise<Array<Marker>> = new Promise<Array<Marker>>((resolve, reject) => {
            if (markerIcon && markerIcon.markerType) {
                const s = Marker.CreateMarker(markerIcon);
                if (typeof(s) === 'string') { resolve(payload(s)); }
                else {
                    return s.then(x => {
                        resolve(payload(x.icon));
                    });
                }
            }
            else {
                resolve (payload(null));
            }
        });
        return p;
    }

    ///
    /// Protected methods
    ///

    /**
     * Gets the layer based on its id.
     *
     * @protected
     * @param id - Layer Id.
     * @returns - A promise that when fullfilled contains the {@link Layer} model for the layer.
     *
     * @memberof GoogleLayerBase
     */
    protected GetLayerById(id: number): Promise<Layer> {
        let p: Promise<Layer>;
        this._layers.forEach((l: Promise<Layer>, k: number) => { if (k === id) { p = l; } });
        return p;
    }

}