mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-21 00:52:55 +00:00
feat: add map module
This commit is contained in:
232
src/app/modules/map/map.provider.ts
Normal file
232
src/app/modules/map/map.provider.ts
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (C) 2019-2021 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {ElementRef, Injectable} from '@angular/core';
|
||||
import {
|
||||
SCSearchFilter,
|
||||
SCSearchQuery,
|
||||
SCSearchResponse,
|
||||
SCThingType,
|
||||
SCUuid,
|
||||
} from '@openstapps/core';
|
||||
import {Point, Polygon} from 'geojson';
|
||||
import {divIcon, geoJSON, icon, LatLng, Map, marker, Marker} from 'leaflet';
|
||||
import {DataProvider} from '../data/data.provider';
|
||||
import {MapPosition, PositionService} from './position.service';
|
||||
import Timeout = NodeJS.Timeout;
|
||||
|
||||
/**
|
||||
* Provides methods for presenting the map
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class MapProvider {
|
||||
/**
|
||||
* Area to show when the map is initialized (shown for the first time)
|
||||
*/
|
||||
defaultPolygon: Polygon;
|
||||
|
||||
/**
|
||||
* Provide a point marker for a leaflet map
|
||||
*
|
||||
* @param point Point to get marker for
|
||||
*/
|
||||
static getPointMarker(point: Point) {
|
||||
return marker(geoJSON(point).getBounds().getCenter(), {
|
||||
icon: icon({
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
iconAnchor: [13, 41],
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
iconSize: [25, 41],
|
||||
iconUrl: '../assets/marker-icon.png',
|
||||
shadowUrl: '../assets/marker-shadow.png',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a position marker for a leaflet map
|
||||
*
|
||||
* @param position Current position
|
||||
* @param className CSS class name
|
||||
* @param iconSize Size of the position icon
|
||||
*/
|
||||
static getPositionMarker(
|
||||
position: MapPosition,
|
||||
className: string,
|
||||
iconSize: number,
|
||||
) {
|
||||
return new Marker(new LatLng(position.latitude, position.longitude), {
|
||||
icon: divIcon({
|
||||
className: className,
|
||||
html:
|
||||
typeof position.heading !== 'undefined'
|
||||
? `<ion-icon name="navigate-straight"
|
||||
style="transform-origin: center; transform: rotate(${position.heading}deg);">
|
||||
</ion-icon>`
|
||||
: '<ion-icon name="locate"></ion-icon>',
|
||||
iconSize: [iconSize, iconSize],
|
||||
}),
|
||||
zIndexOffset: 1000,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the issue of missing tiles when map renders before its container element
|
||||
*
|
||||
* @param map The initialized map
|
||||
* @param element The element containing the map
|
||||
* @param interval Interval to clear when map's appearance is corrected
|
||||
*/
|
||||
static invalidateWhenRendered = (
|
||||
map: Map,
|
||||
element: ElementRef,
|
||||
interval: Timeout,
|
||||
) => {
|
||||
if (element.nativeElement.offsetWidth === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// map's container is ready
|
||||
map.invalidateSize();
|
||||
// stop repeating when it's rendered and invalidateSize done
|
||||
clearInterval(interval);
|
||||
};
|
||||
|
||||
constructor(
|
||||
private dataProvider: DataProvider,
|
||||
private positionService: PositionService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Provide the specific place by its UID
|
||||
*
|
||||
* @param uid UUID of the place to look for
|
||||
*/
|
||||
async searchPlace(uid: SCUuid): Promise<SCSearchResponse> {
|
||||
const uidFilter: SCSearchFilter = {
|
||||
arguments: {
|
||||
field: 'uid',
|
||||
value: uid,
|
||||
},
|
||||
type: 'value',
|
||||
};
|
||||
|
||||
return this.dataProvider.search({filter: uidFilter});
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide places (buildings and canteens)
|
||||
*
|
||||
* @param contextFilter Additional contextual filter (e.g. from the context menu)
|
||||
* @param queryText Query (text) of the search query
|
||||
*/
|
||||
async searchPlaces(
|
||||
contextFilter?: SCSearchFilter,
|
||||
queryText?: string,
|
||||
): Promise<SCSearchResponse> {
|
||||
const buildingFilter: SCSearchFilter = {
|
||||
arguments: {
|
||||
field: 'type',
|
||||
value: SCThingType.Building,
|
||||
},
|
||||
type: 'value',
|
||||
};
|
||||
|
||||
const mensaFilter: SCSearchFilter = {
|
||||
arguments: {
|
||||
filters: [
|
||||
{
|
||||
arguments: {
|
||||
field: 'categories',
|
||||
value: 'canteen',
|
||||
},
|
||||
type: 'value',
|
||||
},
|
||||
{
|
||||
arguments: {
|
||||
field: 'categories',
|
||||
value: 'student canteen',
|
||||
},
|
||||
type: 'value',
|
||||
},
|
||||
{
|
||||
arguments: {
|
||||
field: 'categories',
|
||||
value: 'cafe',
|
||||
},
|
||||
type: 'value',
|
||||
},
|
||||
{
|
||||
arguments: {
|
||||
field: 'categories',
|
||||
value: 'restaurant',
|
||||
},
|
||||
type: 'value',
|
||||
},
|
||||
],
|
||||
operation: 'or',
|
||||
},
|
||||
type: 'boolean',
|
||||
};
|
||||
|
||||
// initial filter for the places
|
||||
const baseFilter: SCSearchFilter = {
|
||||
arguments: {
|
||||
operation: 'or',
|
||||
filters: [buildingFilter, mensaFilter],
|
||||
},
|
||||
type: 'boolean',
|
||||
};
|
||||
|
||||
let filter = baseFilter;
|
||||
|
||||
if (typeof contextFilter !== 'undefined') {
|
||||
filter = {
|
||||
arguments: {
|
||||
operation: 'and',
|
||||
filters: [baseFilter, contextFilter],
|
||||
},
|
||||
type: 'boolean',
|
||||
};
|
||||
}
|
||||
|
||||
const query: SCSearchQuery = {
|
||||
filter,
|
||||
};
|
||||
|
||||
if (queryText && queryText.length > 0) {
|
||||
query.query = queryText;
|
||||
}
|
||||
|
||||
if (this.positionService.position) {
|
||||
query.sort = [
|
||||
{
|
||||
type: 'distance',
|
||||
order: 'asc',
|
||||
arguments: {
|
||||
field: 'geo.point.coordinates',
|
||||
position: [
|
||||
this.positionService.position.longitude,
|
||||
this.positionService.position.latitude,
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return this.dataProvider.search(query);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user