...
Use a CLI command to generate a plugin component.
Code Block language bash linenumbers true eo g plugin map-frame
Update map-frame.component.ts to load current address from dms object, and modify matchType property if necessary. Please check normalize function if it match your scheme properties!
Code Block language js title map-frame.component.ts linenumbers true import { Component, AfterViewInit, ViewChild, ElementRef, Renderer2 } from '@angular/core'; import {DmsService, DmsObject, EventService, EnaioEvent, Event} from '@eo-sdk/core'; import {SelectionService, UnsubscribeOnDestroy} from '@eo-sdk/client'; import {takeUntil} from 'rxjs/operators'; @Component({ selector: 'eo-map-frame', templateUrl: './map-frame.component.html', styleUrls: ['./map-frame.component.scss'] }) export class MapFrameComponent extends UnsubscribeOnDestroy implements AfterViewInit { static id = 'eo.custom.plugin.map-frame'; static matchType = new RegExp('object-details-tab.*'); context; @ViewChild('mapFrame') mapFrame: ElementRef; constructor(private selectionService: SelectionService, private dmsService: DmsService, private renderer: Renderer2, private eventService: EventService) { super(); } /** * normalize Address data - map your data based on scheme properties * @param data * @returns */ private normalize(data: any = {}): any { return { streethw: data.strassehw, townhw: data.orthw, countryhw: data.landhw, ...data }; } /** * Process dmsObject to get the url for map frame * @param dmsObj */ setupMap(dmsObj: DmsObject) { if (dmsObj) { const {streethw, townhw, countryhw} = this.normalize(dmsObj.data); const url = `https://www.google.com/maps/embed/v1/place?key=AIzaSyDX8znfh-d4u3spGhC1GvCjq6EA1pjPovQ&q=${streethw}+${townhw}+${countryhw}`; this.renderer.setAttribute(this.mapFrame.nativeElement, 'src', url); } } /** * Load & update current context/dmsObject * @param event */ eventHandler(event: Event) { if (event.type === EnaioEvent.DMS_OBJECT_LOADED || (this.context && this.context.id === event.data.id)) { this.context = event.data; this.setupMap(event.data); } } ngAfterViewInit() { this.eventService .on(EnaioEvent.DMS_OBJECT_LOADED, EnaioEvent.DMS_OBJECT_UPDATED) .pipe( takeUntil(this.componentDestroyed$) ) .subscribe(e => this.eventHandler(e)); } }
Update map-frame.component.html to include a google maps iframe based on address of the selected dms object.
Code Block language xml title map-frame.component.html linenumbers true <p> map-frame works! </p> <iframe #mapFrame width="100%" height="90%" frameborder="0" style="border:0" allowfullscreen> </iframe>
Use a CLI command to generate labels/translations
Code Block language bash linenumbers true eo g label eo.custom.plugin.map-frame --en GoogleMaps --de GoogleMaps
Example 2b: MAP with Address and Geocoord via iframe + core services
- We start by extending the "MAP via iframe + core service"
For readability, we move most of our functionality to a service
Code Block language bash title Terminal ng g s eo-custom-client/services/location --spec false
To keep the code organized we introduce LocationType enum.
Code Block language bash title Terminal ng g enum eo-custom-client/enum/LocationType
Update locationType.ts
Code Block language js title locationTypes.rs linenumbers true export enum LocationType { COORDS = 'coords', ADDRESS = 'address', EMPTY = 'empty' }
To keep the code organized we introduce a Interfaces for the Locations.
Code Block language bash title Terminal ng g interface eo-custom-client/interfaces/Location
Update location.interface.ts
Code Block language js title location.interface.ts linenumbers true export interface LocationByAddress { type: string; streethw: string; townhw: string; countryhw: string } export interface LocationByCoords { type: string; photogpsla: number; photogpslo: number; } export interface NoLocation { type: string; }
Final location.service.ts. Check that the normalize function matches your scheme properties!
Code Block language js title location.service.ts linenumbers true import {Injectable} from '@angular/core'; import {LocationType} from '../enums/location-type.enum'; import {Observable, of, throwError} from 'rxjs'; import {LocationByAddress, LocationByCoords, NoLocation} from '../interfaces/location'; @Injectable({ providedIn: 'root' }) export class LocationService { private readonly apiKey = `AIzaSyDX8znfh-d4u3spGhC1GvCjq6EA1pjPovQ`; private readonly mapUrl = `https://www.google.com/maps/embed/v1/place`; /** * normalize Address data - map your data based on scheme properties * @param data * @returns */ private normalize(data: any = {}): any { return { streethw: data.strassehw, townhw: data.orthw, countryhw: data.landhw, photogpsla: data.photogpsla, photogpslo: data.photogpslo, ...data }; } /** * retrieve Address from data object * @param data * @returns */ private locationbDataWithAddress(data): LocationByAddress | NoLocation { const {streethw, townhw, countryhw} = data; return (townhw && countryhw) ? {type: LocationType.ADDRESS, streethw, townhw, countryhw} : {type: LocationType.EMPTY}; } /** * retrieve Geo Coordinates from data object * @param data * @returns */ private locationbDataWithCoords(data): LocationByCoords | NoLocation { const {photogpsla, photogpslo} = data; return (photogpsla && photogpslo) ? {type: LocationType.COORDS, photogpsla, photogpslo} : {type: LocationType.EMPTY}; } /** * Geo Coordinates are assumed to be only present in photo Objects. * We check the objects type to either retrieve Geo Coordinates or Address Data. * If neither is available we return empty type. * * @param typeName * @param data * @returns */ locationbData(typeName: string, data: Object): LocationByAddress | LocationByCoords | NoLocation { let normalizedData = this.normalize(data); return typeName === 'albumphoto' ? this.locationbDataWithCoords(normalizedData) : this.locationbDataWithAddress(normalizedData); } /** * Depending on the Location type we build a different URL. * If we have no Location type we return an error. * * @param location * @returns */ mapsUrl(location): Observable<string> { let params: string; if (location.type === LocationType.ADDRESS) { const {streethw, townhw, countryhw} = location; params = `${streethw || ''}+${townhw}+${countryhw}`; } else if (location.type === LocationType.COORDS) { const {photogpsla, photogpslo} = location; params = `${photogpsla},${photogpslo}`; } else { return throwError(true) } return of(`${this.mapUrl}?key=${this.apiKey}&q=${params}`); } }
Since we want to hide the Map when we don't have any Location information, we need to update the map.component.html.
Code Block language xml title map-frame.component.html linenumbers true <iframe class="map" [hidden]="mapAvailable" #mapFrame frameborder="0" allowfullscreen> </iframe> <section class="map__not-available" [hidden]="!mapAvailable"> <eo-icon class="no-file" [iconSrc]="'assets/_default/svg/ic_no-file.svg'"></eo-icon> </section>
As a last step in the TS files we need to update map-frame.component.ts
Code Block language js title map-frame.component.ts linenumbers true import { Component, AfterViewInit, ViewChild, ElementRef, Renderer2 } from '@angular/core'; import {DmsService, DmsObject, EventService, EnaioEvent, Event} from '@eo-sdk/core'; import {SelectionService, UnsubscribeOnDestroy} from '@eo-sdk/client'; import {takeUntil} from 'rxjs/operators'; import {LocationService} from '../../services/location.service'; @Component({ selector: 'eo-map-frame', templateUrl: './map-frame.component.html', styleUrls: ['./map-frame.component.scss'] }) export class MapFrameComponent extends UnsubscribeOnDestroy implements AfterViewInit { static id = 'eo.custom.plugin.map-frame'; static matchType = new RegExp('object-details-tab.*'); context; mapAvailable; @ViewChild('mapFrame') mapFrame: ElementRef; constructor(private selectionService: SelectionService, private dmsService: DmsService, private renderer: Renderer2, private eventService: EventService, private locationService: LocationService) { super(); } /** * Process dmsObject to get the url for map frame * @param dmsObj */ setupMap(dmsObj: DmsObject) { if (dmsObj) { const {typeName, data} = dmsObj; const location = this.locationService.locationbData(typeName, data); this.locationService .mapsUrl(location) .pipe( takeUntil(this.componentDestroyed$) ) .subscribe(url => { this.mapAvailable = false; this.renderer.setAttribute(this.mapFrame.nativeElement, 'src', url); }, error => this.mapAvailable = error); } } /** * Load & update current context/dmsObject * @param event */ eventHandler(event: Event) { if (event.type === EnaioEvent.DMS_OBJECT_LOADED || (this.context && this.context.id === event.data.id)) { this.context = event.data; this.setupMap(event.data); } } ngAfterViewInit() { this.eventService .on(EnaioEvent.DMS_OBJECT_LOADED, EnaioEvent.DMS_OBJECT_UPDATED) .pipe( takeUntil(this.componentDestroyed$) ) .subscribe(e => this.eventHandler(e)); } }
To finish this plugin, add some styling.
Code Block language css title map-frame.component.scss linenumbers true .map{ width: 100%; height: 90%; &__not-available{ height: 100%; display: flex; justify-content: center; align-items: center; text-transform: capitalize; border: 0; .no-file{ width: 128px; height: 128px; opacity: .06; } } }
The results (left: address data, right: coordinates, click on the image enlarges it)
Gallery