Skip to content

Commit 3f1e8ff

Browse files
Merge pull request #91 from Sybit-Education/feat/map-icons
Feat/map icons
2 parents b10a495 + b9e5b1f commit 3f1e8ff

File tree

3 files changed

+126
-122
lines changed

3 files changed

+126
-122
lines changed
Lines changed: 123 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
2-
import {Map, MapBrowserEvent, View} from 'ol';
2+
import {Map, View} from 'ol';
33
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
44
import {OSM, Vector as VectorSource} from 'ol/source';
55
import Feature from 'ol/Feature';
6-
import {Geometry, Point} from 'ol/geom';
7-
import {Circle, Fill, Stroke, Style} from 'ol/style';
8-
import {fromLonLat} from 'ol/proj';
9-
import {Router} from '@angular/router';
10-
import {AirtableService} from '../../services/airtable.service';
11-
import {NgStyle} from "@angular/common";
6+
import { Point, Geometry } from 'ol/geom';
7+
import { Style, Icon } from 'ol/style';
8+
import { fromLonLat } from 'ol/proj';
9+
import { Router } from '@angular/router';
10+
import { AirtableService } from '../../services/airtable.service';
11+
import { MapBrowserEvent } from 'ol';
12+
import { NgStyle } from "@angular/common";
1213

1314
@Component({
1415
selector: 'app-map',
@@ -20,126 +21,128 @@ import {NgStyle} from "@angular/common";
2021
styleUrls: ['./map.component.scss']
2122
})
2223
export class MapComponent implements OnInit {
23-
map!: Map;
24-
vectorSource!: VectorSource;
24+
map!: Map;
25+
vectorSource!: VectorSource;
2526

26-
@ViewChild('tooltip_map', {static: true}) tooltip!: ElementRef;
27-
28-
constructor(private router: Router, private airtableService: AirtableService) {
29-
}
30-
31-
getBookmarked(osm_id: number | null | undefined) {
32-
const item = localStorage.getItem("savedLocations")
33-
if (item) {
34-
const savedLocations = JSON.parse(item)
35-
return savedLocations.includes(osm_id)
36-
}
37-
}
38-
39-
ngOnInit(): void {
40-
this.vectorSource = new VectorSource();
41-
const vectorLayer = new VectorLayer({
42-
source: this.vectorSource
43-
});
44-
45-
this.map = new Map({
46-
target: 'map',
47-
layers: [
48-
new TileLayer({
49-
source: new OSM()
50-
}),
51-
vectorLayer
52-
],
53-
view: new View({
54-
center: fromLonLat([8.970869314606485, 47.73981783654207]),
55-
zoom: 3,
56-
minZoom: 12,
57-
maxZoom: 25
58-
})
59-
});
60-
61-
this.map.on('click', this.handleMapClick.bind(this));
62-
this.map.on('pointermove', this.handlePointerMove.bind(this));
63-
64-
this.airtableService.getActivityList().subscribe(activities => {
65-
activities.forEach(activity => {
66-
const feature = new Feature({
67-
geometry: new Point(fromLonLat([activity.longitude, activity.latitude])),
68-
activity: activity
69-
});
27+
iconSize = 0.15;
7028

71-
const local = this.getBookmarked(activity.osm_id);
72-
const borderSize = local ? 10.0 : 0.75
73-
const color = local ? "gold" : "black"
74-
75-
feature.setStyle(new Style({
76-
image: new Circle({
77-
radius: 8,
78-
fill: new Fill({color: activity.type.color}),
79-
stroke: new Stroke({color: color, width: borderSize})
80-
})
81-
}));
82-
83-
this.vectorSource.addFeature(feature);
84-
});
85-
});
86-
87-
}
29+
@ViewChild('tooltip_map', {static: true}) tooltip!: ElementRef;
8830

89-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
90-
handleMapClick(event: MapBrowserEvent<any>) {
91-
this.map.forEachFeatureAtPixel(event.pixel, (feature) => {
92-
const activity = (feature as Feature<Geometry>).get('activity');
93-
if (activity) {
94-
this.openDetailPage(activity.osm_id);
95-
}
96-
});
31+
constructor(private router: Router, private airtableService: AirtableService) {}
32+
33+
getBookmarked(osm_id: number | null | undefined) {
34+
const item = localStorage.getItem("savedLocations")
35+
if (item) {
36+
const savedLocations = JSON.parse(item)
37+
return savedLocations.includes(osm_id)
9738
}
98-
99-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
100-
handlePointerMove(event: MapBrowserEvent<any>) {
101-
const pixel = this.map.getEventPixel(event.originalEvent);
102-
let featureFound = false;
103-
104-
this.map.forEachFeatureAtPixel(pixel, (feature) => {
105-
featureFound = true;
106-
const activity = (feature as Feature<Geometry>).get('activity');
107-
if (activity) {
108-
const tooltipElement = this.tooltip.nativeElement;
109-
const borderSize = this.getBookmarked(activity.osm_id) ? 5.0 : 0.75
110-
const color = this.getBookmarked(activity.osm_id) ? "gold" : "black"
111-
tooltipElement.innerHTML = activity.name + ' (' + activity.type.name + (this.getBookmarked(activity.osm_id) ? " / favorisiert" : "") + ')';
112-
tooltipElement.style.display = 'block';
113-
tooltipElement.style.left = event.originalEvent.pageX + 'px';
114-
tooltipElement.style.top = (event.originalEvent.pageY - 15) + 'px';
115-
(feature as Feature<Geometry>).setStyle(new Style({
116-
image: new Circle({
117-
radius: 10,
118-
fill: new Fill({color: activity.type.color}),
119-
stroke: new Stroke({color: color, width: borderSize})
120-
})
121-
}));
122-
}
39+
return false;
40+
}
41+
42+
ngOnInit(): void {
43+
this.vectorSource = new VectorSource();
44+
const vectorLayer = new VectorLayer({
45+
source: this.vectorSource
46+
});
47+
48+
this.map = new Map({
49+
target: 'map',
50+
layers: [
51+
new TileLayer({
52+
source: new OSM()
53+
}),
54+
vectorLayer
55+
],
56+
view: new View({
57+
center: fromLonLat([8.970869314606485, 47.73981783654207]),
58+
zoom: 3,
59+
minZoom: 10,
60+
maxZoom: 25
61+
})
62+
});
63+
64+
this.map.on('click', this.handleMapClick.bind(this));
65+
this.map.on('pointermove', this.handlePointerMove.bind(this));
66+
67+
// INITIALIZE
68+
69+
this.airtableService.getActivityList().subscribe(activities => {
70+
activities.forEach(activity => {
71+
const local = this.getBookmarked(activity.osm_id);
72+
console.log(`Activity: ${activity.name}, Bookmarked: ${local}, Color: ${activity.type.color}`);
73+
74+
const feature = new Feature({
75+
geometry: new Point(fromLonLat([activity.longitude, activity.latitude])),
76+
activity: activity
12377
});
124-
125-
if (!featureFound) {
126-
this.tooltip.nativeElement.style.display = 'none';
127-
this.vectorSource.getFeatures().forEach((feature: Feature<Geometry>) => {
128-
const activity = feature.get('activity');
129-
const borderSize = this.getBookmarked(activity.osm_id) ? 5.0 : 0.75
130-
const color = this.getBookmarked(activity.osm_id) ? "gold" : "black"
131-
feature.setStyle(new Style({
132-
image: new Circle({
133-
radius: 8,
134-
fill: new Fill({color: activity.type.color}),
135-
stroke: new Stroke({color: color, width: borderSize})
136-
})
137-
}));
138-
});
139-
}
78+
79+
feature.setStyle(new Style({
80+
image: new Icon({
81+
src: 'data:image/svg+xml;utf8,' + activity.type.svg,
82+
color: activity.type.color,
83+
size: [this.iconSize, this.iconSize]
84+
})
85+
}));
86+
87+
this.vectorSource.addFeature(feature);
88+
});
89+
});
90+
}
91+
92+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
93+
handleMapClick(event: MapBrowserEvent<any>) {
94+
this.map.forEachFeatureAtPixel(event.pixel, (feature) => {
95+
const activity = (feature as Feature<Geometry>).get('activity');
96+
if (activity) {
97+
this.openDetailPage(activity.osm_id);
98+
}
99+
});
100+
}
101+
102+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
103+
handlePointerMove(event: MapBrowserEvent<any>) {
104+
const pixel = this.map.getEventPixel(event.originalEvent);
105+
let featureFound = false;
106+
107+
// HOVER
108+
109+
this.map.forEachFeatureAtPixel(pixel, (feature) => {
110+
featureFound = true;
111+
const activity = (feature as Feature<Geometry>).get('activity');
112+
if (activity) {
113+
const tooltipElement = this.tooltip.nativeElement;
114+
tooltipElement.innerHTML = activity.name + ' (' + activity.type.name + (this.getBookmarked(activity.osm_id) ? " / favorisiert" : "") + ')';
115+
tooltipElement.style.display = 'block';
116+
tooltipElement.style.left = event.originalEvent.pageX + 'px';
117+
tooltipElement.style.top = (event.originalEvent.pageY - 15) + 'px';
118+
(feature as Feature<Geometry>).setStyle(new Style({
119+
image: new Icon({
120+
src: 'data:image/svg+xml;utf8,' + activity.type.svg,
121+
color: activity.type.color,
122+
scale: this.iconSize,
123+
})
124+
}));
125+
}
126+
});
127+
128+
// MOUSE MOVE NO HOVER
129+
130+
if (!featureFound) {
131+
this.tooltip.nativeElement.style.display = 'none';
132+
this.vectorSource.getFeatures().forEach((feature: Feature<Geometry>) => {
133+
const activity = feature.get('activity');
134+
feature.setStyle(new Style({
135+
image: new Icon({
136+
src: 'data:image/svg+xml;utf8,' + activity.type.svg,
137+
color: activity.type.color,
138+
scale: this.iconSize
139+
})
140+
}));
141+
});
140142
}
143+
}
141144

142145
openDetailPage(activityId: string): void {
143146
this.router.navigate(['/activity-details', activityId]);
144147
}
145-
}
148+
}

src/app/services/airtable.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class AirtableService {
4242
id: record["id"] as string,
4343
name: record.fields["name"] as string,
4444
color: record.fields["color"] as string,
45-
svg: record.fields["svg"] as string,
45+
svg: record.fields["svg"] as string
4646
};
4747
});
4848
}),);

src/app/types/types.interface.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export interface TypesInterface {
22
id?: string,
33
name: string,
4-
color: string
4+
color: string,
55
svg: string
6+
67
}

0 commit comments

Comments
 (0)