Skip to content

Commit

Permalink
Merge pull request #750 from gisaia/feat/offline-basemaps
Browse files Browse the repository at this point in the history
Integrate offline basemaps provided by protomap
  • Loading branch information
MohamedHamouGisaia authored Oct 26, 2023
2 parents c671dc6 + 4fa3602 commit 4886481
Show file tree
Hide file tree
Showing 19 changed files with 747 additions and 156 deletions.
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
"tslib": "^2.3.0",
"turf-extent": "1.0.4",
"wellknown": "0.5.0",
"zone.js": "^0.11.4"
"zone.js": "^0.11.4",
"pmtiles": "^2.11.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^14.2.13",
Expand Down
3 changes: 2 additions & 1 deletion projects/arlas-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"shpjs": "^4.0.4",
"turf-extent": "1.0.4",
"wellknown": "0.5.0",
"tslib": "^2.3.0"
"tslib": "^2.3.0",
"pmtiles": "^2.11.0"
},
"exports": {
"./assets/i18n/en.json": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
cursor: pointer;
}

.basemap-container .basemap.selected {
.basemap-container .selected {
border: 2px solid #337ab7;
background-color: #337ab7;
color: #337ab7;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
<div *ngIf="basemapStyles && basemapStyles.length > 1" class="basemap-container">

<div *ngFor="let style of basemapStyles" class="basemap" [class.selected]="style.name === selectedBasemap?.name" (click)="onChangeBasemapStyle(style)">
<div class="image">
<img *ngIf="!!style?.image && style?.image !== ''" src="{{style?.image}}" (error)="style.image = null" />
<div *ngIf="!style?.image || (!!style?.image && style?.image === '')" class="no-image">
<mat-icon>wallpaper</mat-icon>
<div *ngIf="showList" class="basemap-container">
<ng-container *ngIf="isOnline; else offline">
<div *ngFor="let style of onlineBasemaps?._styles" class="basemap" [class.selected]="style.name === onlineBasemaps?._selectedStyle?.name" (click)="onChangeOnlineBasemap(style)">
<div class="image">
<img *ngIf="!!style?.image && style?.image !== ''" src="{{style?.image}}" (error)="style.image = null" />
<div *ngIf="!style?.image || (!!style?.image && style?.image === '')" class="no-image">
<mat-icon>wallpaper</mat-icon>
</div>
<div class="name">{{style.name | translate}}</div>
</div>
<div class="name">{{style.name | translate}}</div>
</div>
</div>
</ng-container>
<ng-template #offline>
<div *ngFor="let theme of offlineBasemaps?._themes" class="basemap" [class.selected]="theme.name === offlineBasemaps?._selectedTheme?.name" (click)="onChangeOfflineBasemap(theme)">
<div class="image">
<div class="no-image">
<mat-icon>wallpaper</mat-icon>
</div>
<div class="name">{{theme.name | translate}}</div>
</div>
</div>
</ng-template>
</div>
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { MapglBasemapComponent } from './mapgl-basemap.component';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { MapboxBasemapService } from '../mapgl/basemaps/basemap.service';

describe('MapglBasemapComponent', () => {
let component: MapglBasemapComponent;
let fixture: ComponentFixture<MapglBasemapComponent>;

const mockMapboxBasemapService = jasmine.createSpyObj('MapboxBasemapService', ['isOnline']);
mockMapboxBasemapService.isOnline.and.returnValue(false);
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [MapglBasemapComponent]
declarations: [MapglBasemapComponent],
imports: [
HttpClientModule
],
providers: [
HttpClient,
{
provide: MapboxBasemapService,
useValue: mockMapboxBasemapService
}
]
})
.compileComponents();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,53 @@
import { Component, Input, OnInit, Output, OnChanges, SimpleChanges } from '@angular/core';
import { Component, Input, OnInit, Output, OnChanges, SimpleChanges, EventEmitter } from '@angular/core';
import { Subject } from 'rxjs/internal/Subject';
import { BasemapStyle } from '../mapgl/model/mapLayers';
import mapboxgl, { AnyLayer } from 'mapbox-gl';
import { MapSource } from '../mapgl/model/mapSource';
import { MapglService } from '../../services/mapgl.service';
import { HttpClient } from '@angular/common/http';
import { MapboxBasemapService } from '../mapgl/basemaps/basemap.service';
import { BasemapStyle, OfflineBasemapTheme } from '../mapgl/basemaps/basemap.config';
import { OfflineBasemap } from '../mapgl/basemaps/offline-basemap';
import { OnlineBasemap } from '../mapgl/basemaps/online-basemap';

@Component({
selector: 'arlas-mapgl-basemap',
templateUrl: './mapgl-basemap.component.html',
styleUrls: ['./mapgl-basemap.component.css']
})
export class MapglBasemapComponent implements OnInit {
private LOCAL_STORAGE_BASEMAPS = 'arlas_last_base_map';

@Input() public basemapStyles: BasemapStyle[];
@Input() public selectedBasemap: BasemapStyle;
@Input() public map: mapboxgl.Map;
@Input() public mapSources: Array<MapSource>;

@Output() public basemapChanged = new Subject<BasemapStyle>();
@Output() public basemapChanged = new EventEmitter<void>();
@Output() public blur = new Subject<void>();

public constructor() { }
public showList = false;
public isOnline = true;
public onlineBasemaps: OnlineBasemap;
public offlineBasemaps: OfflineBasemap;

public constructor(
private mapglService: MapglService,
private basemapService: MapboxBasemapService,
private http: HttpClient) { }

public ngOnInit(): void {
if (this.basemapStyles) {
this.basemapStyles.filter(bm => !bm.image).forEach(bm => {
this.isOnline = this.basemapService.isOnline();
if (this.isOnline) {
this.initOnlineBasemaps();
} else {
this.initOfflineBasemaps();
}
}

private initOnlineBasemaps() {
this.onlineBasemaps = this.basemapService.onlineBasemaps;
const styles = this.onlineBasemaps.styles();
if (!!this.onlineBasemaps && !!styles) {
this.showList = styles.length > 1;
styles.filter(bm => !bm.image).forEach(bm => {
const splitUrl = bm.styleFile.toString().split('/style.json?key=');
if (splitUrl.length === 2) {
bm.image = `${splitUrl[0]}/0/0/0.png?key=${splitUrl[1]}`;
Expand All @@ -28,7 +56,64 @@ export class MapglBasemapComponent implements OnInit {
}
}

public onChangeBasemapStyle(style: BasemapStyle){
this.basemapChanged.next(style);
private initOfflineBasemaps() {
this.offlineBasemaps = this.basemapService.offlineBasemaps;
if (!!this.offlineBasemaps && !!this.offlineBasemaps.themes()) {
this.showList = this.offlineBasemaps.themes().length > 1;
}
}

public onChangeOfflineBasemap(selectedTheme: OfflineBasemapTheme) {
this.basemapService.changeOfflineBasemap(this.map, selectedTheme);
}

public onChangeOnlineBasemap(selectedStyle: BasemapStyle) {
this.setBaseMapStyle(selectedStyle.styleFile);
localStorage.setItem(this.LOCAL_STORAGE_BASEMAPS, JSON.stringify(selectedStyle));
this.onlineBasemaps.setSelected(selectedStyle);
}

public setBaseMapStyle(style: string | mapboxgl.Style) {
if (this.map) {
const selectedStyle = this.onlineBasemaps.getSelected();
if (typeof selectedStyle.styleFile === 'string') {
this.http.get(selectedStyle.styleFile).subscribe((s: any) => {
this.setStyle(s, style);
});
} else {
this.setStyle(selectedStyle.styleFile, style);
}
}
}

public setStyle(s: mapboxgl.Style, style: string | mapboxgl.Style) {
const selectedBasemapLayersSet = new Set<string>();
const layers: Array<mapboxgl.Layer> = (<mapboxgl.Map>this.map).getStyle().layers;
const sources = (<mapboxgl.Map>this.map).getStyle().sources;
if (s.layers) {
s.layers.forEach(l => selectedBasemapLayersSet.add(l.id));
}
const layersToSave = new Array<mapboxgl.Layer>();
const sourcesToSave = new Array<MapSource>();
layers.filter((l: mapboxgl.Layer) => !selectedBasemapLayersSet.has(l.id)).forEach(l => {
layersToSave.push(l);
if (sourcesToSave.filter(ms => ms.id === l.source.toString()).length === 0) {
sourcesToSave.push({ id: l.source.toString(), source: sources[l.source.toString()] });
}
});
const sourcesToSaveSet = new Set<string>();
sourcesToSave.forEach(mapSource => sourcesToSaveSet.add(mapSource.id));
if (this.mapSources) {
this.mapSources.forEach(mapSource => {
if (!sourcesToSaveSet.has(mapSource.id)) {
sourcesToSave.push(mapSource);
}
});
}
this.map.setStyle(style).once('styledata', () => {
this.mapglService.addSourcesToMap(sourcesToSave, this.map);
layersToSave.forEach(l => this.map.addLayer(l as AnyLayer));
this.basemapChanged.emit();
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Licensed to Gisaïa under one or more contributor
* license agreements. See the NOTICE.txt file distributed with
* this work for additional information regarding copyright
* ownership. Gisaïa licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/


// DISCLAIMER
// -- The word 'Style' is reserved to online basemaps.
// -- The word 'Theme' is reserved to offline basemaps.

export interface BasemapsConfig {
isOnline: boolean;
onlineConfig?: OnlineBasemapConfig;
offlineConfig?: OfflineBasemapsConfig;
}

export interface OnlineBasemapConfig {
styles: BasemapStyle[];
defaultStyle: BasemapStyle;
}

export interface OfflineBasemapsConfig {
/** Path to pmtiles file */
url: string;
glyphsUrl: string;
themes: OfflineBasemapTheme[];
defaultTheme: OfflineBasemapTheme;
}


export interface OfflineBasemapTheme {
layers: mapboxgl.Layer[];
name: string;
}

export interface BasemapStyle {
name: string;
styleFile: string | mapboxgl.Style;
image?: string;
}

Loading

0 comments on commit 4886481

Please sign in to comment.