diff --git a/angular.json b/angular.json index 960a8947..22d35c21 100644 --- a/angular.json +++ b/angular.json @@ -59,13 +59,19 @@ "tsConfig": "src/frontend/tsconfig.app.json", "polyfills": "src/frontend/polyfills.ts", "assets": [ - "src/frontend/assets" + "src/frontend/assets", + { + "glob": "**/*", + "input": "node_modules/leaflet/dist/images/", + "output": "./" + } ], "styles": [ "bootstrap/dist/css/bootstrap.min.css", "ngx-bootstrap/datepicker/bs-datepicker.css", "open-iconic/font/css/open-iconic-bootstrap.css", "ngx-toastr/toastr.css", + "leaflet/dist/leaflet.css", "src/frontend/styles.css" ], "scripts": [], @@ -73,11 +79,8 @@ }, "configurations": { "dev": { - "localize": [ - "en", - "hu" - ], - "outputPath": "dist", + "outputPath": "dist/en", + "localize": false, "watch": true }, "production": { @@ -140,10 +143,16 @@ "bootstrap/dist/css/bootstrap.css", "open-iconic/font/css/open-iconic-bootstrap.css", "ngx-bootstrap/datepicker/bs-datepicker.css", + "leaflet/dist/leaflet.css", "src/frontend/styles.css" ], "assets": [ - "src/frontend/assets" + "src/frontend/assets", + { + "glob": "**/*", + "input": "node_modules/leaflet/dist/images/", + "output": "./" + } ] } }, diff --git a/package-lock.json b/package-lock.json index 47baa963..8779098d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -764,6 +764,12 @@ } } }, + "@asymmetrik/ngx-leaflet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@asymmetrik/ngx-leaflet/-/ngx-leaflet-8.1.0.tgz", + "integrity": "sha512-lq7LduBP/vXcaSEmKnx7mzCR8WsoYqh9pB6BNnq53yeCwsqRbG3GdKye1/i8VvoRzjDsmQBPQsIFZ9uclXrtgg==", + "dev": true + }, "@babel/code-frame": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", @@ -3500,9 +3506,9 @@ "dev": true }, "@types/leaflet": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.2.14.tgz", - "integrity": "sha512-acP2w5DygY0V7bwmjFmaen5I2iBl8RkWx9kon1IJA7k9mNFgBb6702WApjZSrM4AG1ucJVxFcTlS6nr4HvahEw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.7.0.tgz", + "integrity": "sha512-ltv5jR+VjKSMtoDkxH61Rsbo0zLU7iqyOXpVPkAX4F+79fg2eymC7t0msWsfNaEZO1FGTIQATCCCQe+ijWoicg==", "dev": true, "requires": { "@types/geojson": "*" @@ -3868,17 +3874,6 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, - "@yaga/leaflet-ng2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@yaga/leaflet-ng2/-/leaflet-ng2-1.0.0.tgz", - "integrity": "sha512-8qVI9xophJbMfDeCbHfW91+ksI6snolFV25jw7sg1bpPUVNReWjLGbZEIgshyToppcwKRx0OxHQMb9JvK3bt7w==", - "dev": true, - "requires": { - "@types/geojson": "^7946.0.4", - "@types/leaflet": "^1.2.8", - "leaflet": "^1.3.2" - } - }, "@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", @@ -12101,9 +12096,9 @@ } }, "leaflet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.3.4.tgz", - "integrity": "sha512-FYL1LGFdj6v+2Ifpw+AcFIuIOqjNggfoLUwuwQv6+3sS21Za7Wvapq+LhbSE4NDXrEj6eYnW3y7LsaBICpyXtw==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.7.1.tgz", + "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==", "dev": true }, "less": { diff --git a/package.json b/package.json index 6442e959..960c784b 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "@angular/platform-browser": "11.2.9", "@angular/platform-browser-dynamic": "11.2.9", "@angular/router": "11.2.9", + "@asymmetrik/ngx-leaflet": "^8.1.0", "@ngx-loading-bar/core": "5.1.1", "@types/bcrypt": "3.0.1", "@types/bcryptjs": "2.4.2", @@ -90,12 +91,12 @@ "@types/image-size": "0.8.0", "@types/jasmine": "3.6.9", "@types/jsonwebtoken": "8.5.1", + "@types/leaflet": "^1.7.0", "@types/node": "14.14.37", "@types/node-geocoder": "3.24.1", "@types/sharp": "0.23.1", "@types/winston": "2.4.4", "@types/xml2js": "0.4.8", - "@yaga/leaflet-ng2": "1.0.0", "bootstrap": "4.6.0", "chai": "4.3.4", "chai-http": "4.3.0", @@ -121,6 +122,7 @@ "karma-jasmine-html-reporter": "1.5.4", "karma-remap-istanbul": "0.6.0", "karma-systemjs": "0.16.0", + "leaflet": "^1.7.1", "mocha": "8.3.2", "natural-orderby": "2.0.3", "ngx-bootstrap": "6.2.0", diff --git a/src/frontend/app/app.module.ts b/src/frontend/app/app.module.ts index 2b632f9c..230746e9 100644 --- a/src/frontend/app/app.module.ts +++ b/src/frontend/app/app.module.ts @@ -10,7 +10,7 @@ import {FullScreenService} from './ui/gallery/fullscreen.service'; import {AuthenticationService} from './model/network/authentication.service'; import {UserMangerSettingsComponent} from './ui/settings/usermanager/usermanager.settings.component'; import {FrameComponent} from './ui/frame/frame.component'; -import {YagaModule} from '@yaga/leaflet-ng2'; +import { LeafletModule } from '@asymmetrik/ngx-leaflet'; import {LoadingBarModule} from '@ngx-loading-bar/core'; import {GalleryLightboxMediaComponent} from './ui/gallery/lightbox/media/media.lightbox.gallery.component'; import {GalleryPhotoLoadingComponent} from './ui/gallery/grid/photo/loading/loading.photo.grid.gallery.component'; @@ -144,9 +144,9 @@ export class CustomUrlSerializer implements UrlSerializer { PopoverModule.forRoot(), BsDropdownModule.forRoot(), BsDatepickerModule.forRoot(), - YagaModule, TimepickerModule.forRoot(), - LoadingBarModule + LoadingBarModule, + LeafletModule ], declarations: [AppComponent, LoginComponent, diff --git a/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.css b/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.css index d32aa097..54ba5751 100644 --- a/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.css +++ b/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.css @@ -66,6 +66,6 @@ text-decoration: underline; } -.keywords .oi-person{ +.keywords .oi-person { margin-right: 2px; } diff --git a/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html b/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html index 0e476baa..fe647043 100644 --- a/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html +++ b/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html @@ -1,4 +1,4 @@ -
+
- - - # - {{keyword.value}} - + #{{keyword.value}} - , - + , +
@@ -123,20 +123,19 @@
+
+
- - - - - - - + id="map" + leaflet + [leafletOptions]="{zoom:10, + center:{lat:PositionData.GPSData.latitude, + lng:PositionData.GPSData.longitude}, + layers:[baseLayer], + zoomControl: false}" + [leafletLayers]="markerLayer"> +
+ diff --git a/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts b/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts index c50d5f45..3eb38ed6 100644 --- a/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts +++ b/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts @@ -1,4 +1,4 @@ -import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {Component, EventEmitter, Input, OnChanges, OnInit, Output} from '@angular/core'; import {CameraMetadata, PhotoDTO, PhotoMetadata, PositionMetaData} from '../../../../../../common/entities/PhotoDTO'; import {Config} from '../../../../../../common/config/public/Config'; import {MediaDTO, MediaDTOUtils} from '../../../../../../common/entities/MediaDTO'; @@ -8,26 +8,31 @@ import {QueryService} from '../../../../model/query.service'; import {MapService} from '../../map/map.service'; import {SearchQueryTypes, TextSearch, TextSearchQueryMatchTypes} from '../../../../../../common/entities/SearchQueryDTO'; import {AuthenticationService} from '../../../../model/network/authentication.service'; +import {LatLngLiteral, marker, Marker, TileLayer, tileLayer} from 'leaflet'; @Component({ selector: 'app-info-panel', styleUrls: ['./info-panel.lightbox.gallery.component.css'], templateUrl: './info-panel.lightbox.gallery.component.html', }) -export class InfoPanelLightboxComponent implements OnInit { +export class InfoPanelLightboxComponent implements OnInit, OnChanges { @Input() media: MediaDTO; @Output() closed = new EventEmitter(); public readonly mapEnabled: boolean; public readonly searchEnabled: boolean; - keywords: { value: string, type: SearchQueryTypes }[] = null; - readonly SearchQueryTypes: typeof SearchQueryTypes = SearchQueryTypes; + public keywords: { value: string, type: SearchQueryTypes }[] = null; + public readonly SearchQueryTypes: typeof SearchQueryTypes = SearchQueryTypes; + + public baseLayer: TileLayer; + public markerLayer: Marker[] = []; constructor(public queryService: QueryService, public mapService: MapService, private authService: AuthenticationService) { this.mapEnabled = Config.Client.Map.enabled; this.searchEnabled = Config.Client.Search.enabled && this.authService.canSearch(); + this.baseLayer = tileLayer(mapService.MapLayer, {attribution: mapService.ShortAttributions}); } get FullPath(): string { @@ -53,6 +58,15 @@ export class InfoPanelLightboxComponent implements OnInit { return (this.media as PhotoDTO).metadata.cameraData; } + ngOnChanges(): void { + if (this.hasGPS()) { + this.markerLayer = [marker({ + lat: this.PositionData.GPSData.latitude, + lng: this.PositionData.GPSData.longitude + } as LatLngLiteral)]; + } + } + ngOnInit(): void { const metadata = this.media.metadata as PhotoMetadata; if ((metadata.keywords && metadata.keywords.length > 0) || @@ -101,9 +115,9 @@ export class InfoPanelLightboxComponent implements OnInit { (this.media as PhotoDTO).metadata.positionData.country); } - hasGPS(): number { - return (this.media as PhotoDTO).metadata.positionData && (this.media as PhotoDTO).metadata.positionData.GPSData && - (this.media as PhotoDTO).metadata.positionData.GPSData.latitude && (this.media as PhotoDTO).metadata.positionData.GPSData.longitude; + hasGPS(): boolean { + return !!((this.media as PhotoDTO).metadata.positionData && (this.media as PhotoDTO).metadata.positionData.GPSData && + (this.media as PhotoDTO).metadata.positionData.GPSData.latitude && (this.media as PhotoDTO).metadata.positionData.GPSData.longitude); } getPositionText(): string { diff --git a/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.css b/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.css index 0afd86d5..a813d172 100644 --- a/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.css +++ b/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.css @@ -73,13 +73,13 @@ opacity: 1.0; } -.preview-loading { +::ng-deep .lightbox-map-gallery-component-preview-loading { background-color: #bbbbbb; color: #7f7f7f; font-size: 50px; } -.preview-loading span { +::ng-deep .lightbox-map-gallery-component-preview-loading span { top: calc(50% - 25px); left: calc(50% - 25px); } diff --git a/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.html b/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.html index 1e9e4e6e..f689a901 100644 --- a/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.html +++ b/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.html @@ -6,75 +6,15 @@ [style.top.px]="lightboxDimension.top" [style.left.px]="lightboxDimension.left" [style.opacity]="opacity"> +
- - - - - - - - - - - - - - -
- -
-
-
- -
- - - - -
- - - - - - + leaflet + [leafletOptions]="mapOptions" + (leafletMapZoom)="onLeafletZoom()" + (leafletMapReady)="onMapReady($event)"> +
diff --git a/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.ts b/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.ts index 492f3532..ba83588c 100644 --- a/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.ts +++ b/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.ts @@ -1,8 +1,8 @@ -import {AfterViewInit, Component, ElementRef, HostListener, Input, OnChanges, ViewChild} from '@angular/core'; +import {Component, ElementRef, HostListener, Input, OnChanges, ViewChild} from '@angular/core'; import {PhotoDTO} from '../../../../../../common/entities/PhotoDTO'; import {Dimension} from '../../../../model/IRenderable'; import {FullScreenService} from '../../fullscreen.service'; -import {IconThumbnail, Thumbnail, ThumbnailManagerService} from '../../thumbnailManager.service'; +import {IconThumbnail, Thumbnail, ThumbnailBase, ThumbnailManagerService} from '../../thumbnailManager.service'; import {MediaIcon} from '../../MediaIcon'; import {Media} from '../../Media'; import {PageHelper} from '../../../../model/page.helper'; @@ -10,15 +10,31 @@ import {FileDTO} from '../../../../../../common/entities/FileDTO'; import {Utils} from '../../../../../../common/Utils'; import {Config} from '../../../../../../common/config/public/Config'; import {MapService} from '../map.service'; -import {LatLng, Point} from 'leaflet'; -import {MapComponent} from '@yaga/leaflet-ng2'; +import { + control, + Control, + icon, + LatLng, + latLng, + latLngBounds, + layerGroup, + LayerGroup, + Map, + MapOptions, + Marker, + marker, + Point, + polyline, + tileLayer +} from 'leaflet'; +import {LeafletControlLayersConfig} from '@asymmetrik/ngx-leaflet'; @Component({ selector: 'app-gallery-map-lightbox', styleUrls: ['./lightbox.map.gallery.component.css'], templateUrl: './lightbox.map.gallery.component.html', }) -export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit { +export class GalleryMapLightboxComponent implements OnChanges { @Input() photos: PhotoDTO[]; @@ -28,19 +44,46 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit { public visible = false; public controllersVisible = false; public opacity = 1.0; - mapPhotos: MapPhoto[] = []; - paths: LatLng[][] = []; @ViewChild('root', {static: true}) elementRef: ElementRef; - @ViewChild('yagaMap', {static: true}) yagaMap: MapComponent; - public smallIconSize = new Point(Config.Client.Media.Thumbnail.iconSize * 0.75, Config.Client.Media.Thumbnail.iconSize * 0.75); - public iconSize = new Point(Config.Client.Media.Thumbnail.iconSize, Config.Client.Media.Thumbnail.iconSize); + public mapOptions: MapOptions = { + zoom: 2, + center: latLng(0, 0) + }; + private smallIconSize = new Point(Config.Client.Media.Thumbnail.iconSize * 0.75, Config.Client.Media.Thumbnail.iconSize * 0.75); + private iconSize = new Point(Config.Client.Media.Thumbnail.iconSize, Config.Client.Media.Thumbnail.iconSize); + private mapLayersControlOption: LeafletControlLayersConfig & { overlays: { Photos: LayerGroup, Paths: LayerGroup } } = + {baseLayers: {}, overlays: {Photos: layerGroup([]), Paths: layerGroup([])}}; + + private mapLayerControl: Control.Layers; + private thumbnailsOnLoad: ThumbnailBase[] = []; private startPosition: Dimension = null; + private leafletMap: Map; constructor(public fullScreenService: FullScreenService, private thumbnailService: ThumbnailManagerService, public mapService: MapService) { + this.mapOptions.layers = [this.mapLayersControlOption.overlays.Photos, + this.mapLayersControlOption.overlays.Paths]; + for (let i = 0; i < mapService.Layers.length; ++i) { + const l = mapService.Layers[i]; + const tl = tileLayer(l.url, {attribution: mapService.Attributions}); + if (i === 0) { + this.mapOptions.layers.push(tl); + } + this.mapLayersControlOption.baseLayers[l.name] = tl; + } + + this.mapLayerControl = control.layers(this.mapLayersControlOption.baseLayers, + this.mapLayersControlOption.overlays, {position: 'bottomright'}); } + private static getScreenWidth(): number { + return window.innerWidth; + } + + private static getScreenHeight(): number { + return window.innerHeight; + } ngOnChanges(): void { if (this.visible === false) { @@ -50,37 +93,24 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit { } - ngAfterViewInit(): void { - // TODO: remove it once yaga/leaflet-ng2 is fixes. - // See issue: https://github.com/yagajs/leaflet-ng2/issues/440 - let i = 0; - this.yagaMap.eachLayer((l): void => { - if (i >= 3 || (this.paths.length === 0 && i >= 2)) { - this.yagaMap.removeLayer(l); - } - ++i; - }); - } - @HostListener('window:resize', ['$event']) async onResize(): Promise { this.lightboxDimension = ({ top: 0, left: 0, - width: this.getScreenWidth(), - height: this.getScreenHeight() + width: GalleryMapLightboxComponent.getScreenWidth(), + height: GalleryMapLightboxComponent.getScreenHeight() } as Dimension); this.mapDimension = ({ top: 0, left: 0, - width: this.getScreenWidth(), - height: this.getScreenHeight() + width: GalleryMapLightboxComponent.getScreenWidth(), + height: GalleryMapLightboxComponent.getScreenHeight() } as Dimension); await Utils.wait(0); - this.yagaMap.invalidateSize(); + this.leafletMap.invalidateSize(); } - public async show(position: Dimension): Promise { this.hideImages(); this.visible = true; @@ -91,8 +121,8 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit { this.mapDimension = ({ top: 0, left: 0, - width: this.getScreenWidth(), - height: this.getScreenHeight() + width: GalleryMapLightboxComponent.getScreenWidth(), + height: GalleryMapLightboxComponent.getScreenHeight() } as Dimension); this.showImages(); this.centerMap(); @@ -101,11 +131,11 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit { this.lightboxDimension = ({ top: 0, left: 0, - width: this.getScreenWidth(), - height: this.getScreenHeight() + width: GalleryMapLightboxComponent.getScreenWidth(), + height: GalleryMapLightboxComponent.getScreenHeight() } as Dimension); await Utils.wait(350); - this.yagaMap.invalidateSize(); + this.leafletMap.invalidateSize(); this.centerMap(); this.controllersVisible = true; } @@ -116,7 +146,7 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit { const to = this.startPosition; // iff target image out of screen -> scroll to there - if (PageHelper.ScrollY > to.top || PageHelper.ScrollY + this.getScreenHeight() < to.top) { + if (PageHelper.ScrollY > to.top || PageHelper.ScrollY + GalleryMapLightboxComponent.getScreenHeight() < to.top) { PageHelper.ScrollY = to.top; } @@ -127,18 +157,30 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit { setTimeout((): void => { this.visible = false; this.hideImages(); - this.yagaMap.zoom = 2; + this.leafletMap.setZoom(2); }, 500); } showImages(): void { this.hideImages(); - this.mapPhotos = this.photos.filter((p): number => { + this.mapLayersControlOption.overlays.Photos.clearLayers(); + + // make sure to enable photos layers when opening map + if (this.leafletMap && !this.leafletMap.hasLayer(this.mapLayersControlOption.overlays.Photos)) { + this.leafletMap.addLayer(this.mapLayersControlOption.overlays.Photos); + } + this.thumbnailsOnLoad = []; + this.photos.filter((p): number => { return p.metadata && p.metadata.positionData && p.metadata.positionData.GPSData && p.metadata.positionData.GPSData.latitude && p.metadata.positionData.GPSData.longitude; - }).map((p): MapPhoto => { + }).forEach((p): void => { + const mkr = marker({ + lat: p.metadata.positionData.GPSData.latitude, + lng: p.metadata.positionData.GPSData.longitude + }); + this.mapLayersControlOption.overlays.Photos.addLayer(mkr); let width = 500; let height = 500; const size = p.metadata.size; @@ -147,30 +189,56 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit { } else { width = height * (size.width / size.height); } - const iconTh = this.thumbnailService.getIcon(new MediaIcon(p)); - iconTh.Visible = true; - const obj: MapPhoto = { - name: p.name, - lat: p.metadata.positionData.GPSData.latitude, - lng: p.metadata.positionData.GPSData.longitude, - iconThumbnail: iconTh, - preview: { - width, - height, - thumbnail: this.thumbnailService.getLazyThumbnail(new Media(p, width, height)) - } + const photoTh = this.thumbnailService.getLazyThumbnail(new Media(p, width, height)); + this.thumbnailsOnLoad.push(photoTh); - }; - if (Config.Client.Map.useImageMarkers === true) { - if (iconTh.Available === true) { - obj.iconUrl = iconTh.Src; + // Setting popup photo + const setPopUpPhoto = () => { + const photoPopup = `preview`; + if (!mkr.getPopup()) { + mkr.bindPopup(photoPopup, {minWidth: width}); } else { - iconTh.OnLoad = (): void => { - obj.iconUrl = iconTh.Src; - }; + mkr.setPopupContent(photoPopup); + } + }; + + if (photoTh.Available) { + setPopUpPhoto(); + } else { + const noPhotoPopup = ``; + + mkr.bindPopup(noPhotoPopup, {minWidth: width}); + mkr.on('popupopen', () => { + photoTh.load(); + photoTh.CurrentlyWaiting = true; + }); + photoTh.OnLoad = setPopUpPhoto; + } + + // Setting photo icon + if (Config.Client.Map.useImageMarkers === true) { + const iconTh = this.thumbnailService.getIcon(new MediaIcon(p)); + this.thumbnailsOnLoad.push(iconTh); + iconTh.Visible = true; + const setIcon = () => { + mkr.setIcon(icon({ + iconUrl: iconTh.Src, + iconSize: this.iconSize, // size of the icon + })); + }; + + if (iconTh.Available === true) { + setIcon(); + } else { + iconTh.OnLoad = setIcon; } } - return obj; }); if (this.gpxFiles) { this.loadGPXFiles().catch(console.error); @@ -184,11 +252,10 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit { } hideImages(): void { - this.mapPhotos.forEach((mp): void => { - mp.iconThumbnail.destroy(); - mp.preview.thumbnail.destroy(); + this.thumbnailsOnLoad.forEach((th): void => { + th.destroy(); }); - this.mapPhotos = []; + this.thumbnailsOnLoad = []; } @HostListener('window:keydown', ['$event']) @@ -206,20 +273,57 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit { this.fullScreenService.showFullScreen(this.elementRef.nativeElement); } break; - case 'Escape': // escape + case 'Escape': this.hide(); break; } } + onMapReady(map: Map): void { + this.leafletMap = map; + this.leafletMap.zoomControl.setPosition('bottomright'); + this.mapLayerControl.addTo(this.leafletMap); + } + + onLeafletZoom(): void { + (this.mapLayersControlOption.overlays.Photos.getLayers() as Marker[]).forEach(mkr => { + if (this.leafletMap.getZoom() < 15) { + mkr.getIcon().options.iconSize = this.smallIconSize; + mkr.setIcon(mkr.getIcon()); + } else { + mkr.getIcon().options.iconSize = this.iconSize; + mkr.setIcon(mkr.getIcon()); + } + }); + } + private centerMap(): void { - if (this.mapPhotos.length > 0) { - this.yagaMap.fitBounds(this.mapPhotos as any); - } + this.leafletMap.fitBounds( + latLngBounds((this.mapLayersControlOption.overlays.Photos.getLayers() as Marker[]) + .map(m => m.getLatLng()) + ) + ); } private async loadGPXFiles(): Promise { - this.paths = []; + + this.mapLayersControlOption.overlays.Paths.clearLayers(); + if (this.gpxFiles.length === 0) { + // remove from controls + this.mapLayerControl.removeLayer(this.mapLayersControlOption.overlays.Paths); + // remove from map + if (this.leafletMap) { + this.leafletMap.removeLayer(this.mapLayersControlOption.overlays.Paths); + } + } else { + // make sure it does not appear twice + this.mapLayerControl.removeLayer(this.mapLayersControlOption.overlays.Paths); + this.mapLayerControl.addOverlay(this.mapLayersControlOption.overlays.Paths, 'Paths'); + if (this.leafletMap && !this.leafletMap.hasLayer(this.mapLayersControlOption.overlays.Paths)) { + this.leafletMap.addLayer(this.mapLayersControlOption.overlays.Paths); + } + } + // tslint:disable-next-line:prefer-for-of for (let i = 0; i < this.gpxFiles.length; i++) { const file = this.gpxFiles[i]; @@ -230,19 +334,10 @@ export class GalleryMapLightboxComponent implements OnChanges, AfterViewInit { if (path.length === 0) { continue; } - this.paths.push(path as LatLng[]); + this.mapLayersControlOption.overlays.Paths.addLayer(marker(path[0] as LatLng)); + this.mapLayersControlOption.overlays.Paths.addLayer(polyline(path as LatLng[])); } } - - private getScreenWidth(): number { - return window.innerWidth; - } - - private getScreenHeight(): number { - return window.innerHeight; - } - - } export interface MapPhoto { diff --git a/src/frontend/app/ui/gallery/map/map.gallery.component.css b/src/frontend/app/ui/gallery/map/map.gallery.component.css index 476f4b4f..176cb91e 100644 --- a/src/frontend/app/ui/gallery/map/map.gallery.component.css +++ b/src/frontend/app/ui/gallery/map/map.gallery.component.css @@ -1,4 +1,4 @@ -.yaga-map{ +.map{ /*z-index: 0;*/ width: 100%; height: 100%; diff --git a/src/frontend/app/ui/gallery/map/map.gallery.component.html b/src/frontend/app/ui/gallery/map/map.gallery.component.html index 9d12a232..4cf4d29b 100644 --- a/src/frontend/app/ui/gallery/map/map.gallery.component.html +++ b/src/frontend/app/ui/gallery/map/map.gallery.component.html @@ -1,26 +1,13 @@ - +
- - - - - - - - +
+
diff --git a/src/frontend/app/ui/gallery/map/map.gallery.component.ts b/src/frontend/app/ui/gallery/map/map.gallery.component.ts index 0d1022f4..96fc560f 100644 --- a/src/frontend/app/ui/gallery/map/map.gallery.component.ts +++ b/src/frontend/app/ui/gallery/map/map.gallery.component.ts @@ -1,82 +1,74 @@ -import {AfterViewInit, Component, ElementRef, Input, OnChanges, ViewChild} from '@angular/core'; +import {Component, ElementRef, Input, OnChanges, ViewChild} from '@angular/core'; import {PhotoDTO} from '../../../../../common/entities/PhotoDTO'; import {Dimension, IRenderable} from '../../../model/IRenderable'; import {GalleryMapLightboxComponent} from './lightbox/lightbox.map.gallery.component'; import {FileDTO} from '../../../../../common/entities/FileDTO'; import {MapService} from './map.service'; -import {MapComponent} from '@yaga/leaflet-ng2'; import {Config} from '../../../../../common/config/public/Config'; +import {LatLngLiteral, Map, MapOptions, Marker, marker, tileLayer} from 'leaflet'; @Component({ selector: 'app-gallery-map', templateUrl: './map.gallery.component.html', styleUrls: ['./map.gallery.component.css'] }) -export class GalleryMapComponent implements OnChanges, IRenderable, AfterViewInit { +export class GalleryMapComponent implements OnChanges, IRenderable { @Input() photos: PhotoDTO[]; @Input() gpxFiles: FileDTO[]; @ViewChild(GalleryMapLightboxComponent, {static: false}) mapLightbox: GalleryMapLightboxComponent; - - mapPhotos: Array<{ lat: number, lng: number }> = []; @ViewChild('map', {static: false}) mapElement: ElementRef; - @ViewChild('yagaMap', {static: false}) yagaMap: MapComponent; -// height: number = null; + leafletMap: Map; + + + options: MapOptions = { + zoomControl: false, + dragging: false, + keyboard: false, + tap: false, + doubleClickZoom: false, + boxZoom: false, + zoom: 0, + }; + markerLayer: Marker[] = []; constructor(public mapService: MapService) { + this.options.layers = [ + tileLayer(mapService.MapLayer, {attribution: mapService.ShortAttributions}) + ]; } - get EnableMapPreview(): boolean { - /** - * Disabling map preview on IOS as safari has issues handling z-index of leaflet (yaga-maps) - * Details https://github.com/bpatrik/pigallery2/issues/155 - * TODO: re enable it once yaga-maps is fixed - */ - const isIOS = [ - 'iPad Simulator', - 'iPhone Simulator', - 'iPod Simulator', - 'iPad', - 'iPhone', - 'iPod' - ].includes(navigator.platform) - // iPad on iOS 13 detection - || (navigator.userAgent.includes('Mac') && 'ontouchend' in document); - - return !isIOS; + onMapReady(map: Map): void { + this.leafletMap = map; + this.leafletMap.setView(this.markerLayer[0].getLatLng(), 99); + this.leafletMap.fitBounds(this.markerLayer.map((mp): [number, number] => + [mp.getLatLng().lat, mp.getLatLng().lng] as [number, number])); + this.leafletMap.setZoom(0); } ngOnChanges(): void { - this.mapPhotos = this.photos.filter((p): number => { + this.markerLayer = this.photos.filter((p): number => { return p.metadata && p.metadata.positionData && p.metadata.positionData.GPSData && p.metadata.positionData.GPSData.latitude && p.metadata.positionData.GPSData.longitude; - }).slice(0, Config.Client.Map.maxPreviewMarkers).map((p): { lng: number; lat: number } => { - return { + }).slice(0, Config.Client.Map.maxPreviewMarkers).map((p): Marker => { + return marker({ lat: p.metadata.positionData.GPSData.latitude, lng: p.metadata.positionData.GPSData.longitude - }; + } as LatLngLiteral); }); - if (this.yagaMap && this.mapPhotos.length > 0) { - this.yagaMap.setView(this.mapPhotos[0], 99); - this.yagaMap.fitBounds(this.mapPhotos.map((mp): [number, number] => [mp.lat, mp.lng] as [number, number])); - this.yagaMap.zoom = 0; + if (this.leafletMap && this.markerLayer.length > 0) { + this.options.center = this.markerLayer[0].getLatLng(); + this.leafletMap.setView(this.markerLayer[0].getLatLng(), 99); + this.leafletMap.fitBounds(this.markerLayer.map((mp): [number, number] => + [mp.getLatLng().lat, mp.getLatLng().lng] as [number, number])); + this.leafletMap.setZoom(0); } } - ngAfterViewInit(): void { - setTimeout((): void => { - // this.height = this.mapElement.nativeElement.clientHeight; - this.yagaMap.setView(this.mapPhotos[0], 99); - this.yagaMap.fitBounds(this.mapPhotos.map((mp): [number, number] => [mp.lat, mp.lng] as [number, number])); - this.yagaMap.zoom = 0; - }, 0); - } - - click(): void { this.mapLightbox.show(this.getDimension()); } diff --git a/src/frontend/app/ui/gallery/map/map.service.ts b/src/frontend/app/ui/gallery/map/map.service.ts index 2dc1912e..4e98aba2 100644 --- a/src/frontend/app/ui/gallery/map/map.service.ts +++ b/src/frontend/app/ui/gallery/map/map.service.ts @@ -25,37 +25,33 @@ export class MapService { ]; } - public get ShortAttributions(): string[] { - const yaga = 'YAGA'; - const lf = 'leaflet-ng2'; + public get ShortAttributions(): string { const OSM = 'OSM'; const MB = 'Mapbox'; if (Config.Client.Map.mapProvider === MapProviders.OpenStreetMap) { - return [yaga + ' | © ' + OSM]; + return ' © ' + OSM; } if (Config.Client.Map.mapProvider === MapProviders.Mapbox) { - return [yaga + ' | ' + OSM + ' | ' + MB]; + return OSM + ' | ' + MB; } - return [yaga + ' | ' + lf]; + return ''; } - public get Attributions(): string[] { - const yagalf = 'YAGA | ' + - 'leaflet-ng2'; + public get Attributions(): string { const OSM = '© OpenStreetMap'; const MB = '© Mapbox'; if (Config.Client.Map.mapProvider === MapProviders.OpenStreetMap) { - return [yagalf + ' | ' + OSM]; + return OSM; } if (Config.Client.Map.mapProvider === MapProviders.Mapbox) { - return [yagalf + ' | ' + OSM + ' | ' + MB]; + return OSM + ' | ' + MB; } - return [yagalf]; + return ''; } public get MapLayer(): string { diff --git a/src/frontend/index.html b/src/frontend/index.html index 130d77ea..97f52c52 100644 --- a/src/frontend/index.html +++ b/src/frontend/index.html @@ -14,9 +14,7 @@ - +