From 93f88c99338f3d2c36940bc59d9e9f26eec46ecf Mon Sep 17 00:00:00 2001 From: Ol86 Date: Mon, 20 Apr 2026 23:13:23 +0200 Subject: [PATCH] Adding the Overpass API data to the map with the help of polygons --- src/app/app.ts | 4 +-- src/app/map/geojson.ts | 10 +++--- src/app/map/map.html | 7 ---- src/app/map/map.scss | 1 + src/app/map/map.ts | 77 +++++++++++++++++++++++++++++------------- src/app/overpass.ts | 23 +++++++++++-- 6 files changed, 81 insertions(+), 41 deletions(-) diff --git a/src/app/app.ts b/src/app/app.ts index 2198e1e..5723740 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -1,11 +1,11 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { Header } from './header/header'; -import { Map } from './map/map'; +import { OSMMap } from './map/map'; @Component({ selector: 'app-root', - imports: [RouterOutlet, Header, Map], + imports: [RouterOutlet, Header, OSMMap], templateUrl: './app.html', styleUrl: './app.scss', }) diff --git a/src/app/map/geojson.ts b/src/app/map/geojson.ts index 4dec1c9..5435e85 100644 --- a/src/app/map/geojson.ts +++ b/src/app/map/geojson.ts @@ -4,8 +4,8 @@ import * as G from 'geojson'; export const buildingSytle = { color: '#d62305', // Red border weight: 2, // Border width - fillColor: '#d62305', // Red fill - fillOpacity: 0.1, // Semi-transparent fill + fillColor: '#d4caca', // Red fill + fillOpacity: 1, // Semi-transparent fill }; // Example GeoJSON data @@ -28,9 +28,8 @@ export const buildings: G.FeatureCollection = { { type: 'Feature', properties: { - name: 'Gebäude E', + name: 'E', building: 'university', - center: [8.39007, 49.015], }, geometry: { type: 'Polygon', @@ -48,9 +47,8 @@ export const buildings: G.FeatureCollection = { { type: 'Feature', properties: { - name: 'Gebäude F', + name: 'F', building: 'university', - center: [8.3901208597194, 49.0156137375335], }, geometry: { type: 'Polygon', diff --git a/src/app/map/map.html b/src/app/map/map.html index 7778e6a..6395a5a 100644 --- a/src/app/map/map.html +++ b/src/app/map/map.html @@ -1,15 +1,8 @@
- @if (!isFullscreen && details) { -
-

{{ details.name }}

-

{{ details.building }}

-
- }
diff --git a/src/app/map/map.scss b/src/app/map/map.scss index eff4b78..352eacb 100644 --- a/src/app/map/map.scss +++ b/src/app/map/map.scss @@ -4,6 +4,7 @@ position: absolute; top: 54px; height: calc(100vh - 54px); + width: 100%; } .map-fullscreen { diff --git a/src/app/map/map.ts b/src/app/map/map.ts index 24f41d1..e8df937 100644 --- a/src/app/map/map.ts +++ b/src/app/map/map.ts @@ -1,6 +1,7 @@ import { Component, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { LeafletModule } from '@bluehalo/ngx-leaflet'; +import { center } from '@turf/turf'; import { Overpass } from '../overpass'; import * as L from 'leaflet'; @@ -13,12 +14,11 @@ import * as E from './geojson'; templateUrl: './map.html', styleUrl: './map.scss', }) -export class Map { - private centerLocation = { lat: 49.015514096207895, lng: 8.391567600294243 }; +export class OSMMap { + private buildingLayer?: L.LayerGroup; protected readonly title = signal('HKA-OSM'); map?: L.Map; - details?: D.Details; isFullscreen = true; @@ -33,7 +33,7 @@ export class Map { options: L.MapOptions = { layers: [this.osm], zoom: 18, // Zoom level - center: L.latLng(this.centerLocation.lat, this.centerLocation.lng), + center: this.getCenter(), // Center of the map maxBounds: L.latLngBounds( L.latLng(49.014442, 8.387954), // Southwest corner L.latLng(49.017847, 8.395448), // Northeast corner @@ -51,7 +51,6 @@ export class Map { this.map = map; this.layerControl.addTo(this.map); this.loadBuildings().addTo(this.map); - this.queryBuildings(); console.log('Map is ready'); } @@ -63,23 +62,14 @@ export class Map { click: (e) => { this.isFullscreen = !this.isFullscreen; if (!this.isFullscreen) { - this.map?.setView( - L.latLng(feature.properties.center[1], feature.properties.center[0]), - 20, - ); - //FIXME: Disable dragging and keyboard interactions when in fullscreen mode - this.map?.dragging.disable() && - this.map?.keyboard.disable() && - this.map?.scrollWheelZoom.disable(); - this.details = new D.Details(feature.properties.name, feature.properties.building); + this.map?.setView(this.getCenter(feature.geometry), 20); + this.buildingLayer = this.queryBuildings(feature.properties.name); + this.buildingLayer.addTo(this.map!); } else { - this.map?.setView(L.latLng(this.centerLocation.lat, this.centerLocation.lng), 18); - - //FIXME: Enable dragging and keyboard interactions when in fullscreen mode - this.map?.dragging.enable() && - this.map?.keyboard.enable() && - this.map?.scrollWheelZoom.enable(); - this.details = undefined; + this.map?.setView(this.getCenter(), 18); + if (this.buildingLayer) { + this.map?.removeLayer(this.buildingLayer); + } } console.log('Set isFullscreen to:', this.isFullscreen); console.log('Clicked on feature:', feature.properties?.name); @@ -95,10 +85,49 @@ export class Map { }); } - queryBuildings() { - this.overpass.fetchBuildings('E').subscribe((data) => { + queryBuildings(building_name: string) { + // Get the building name from the feature properties and query the Overpass API for detailed building data, + // including indoor features. + + // Create a new LayerGroup to hold the building polygons + const resultingLayer = new L.LayerGroup(); + this.overpass.fetchBuildings(building_name).subscribe((data) => { console.log('Fetched building data from Overpass API:', data); - // Here you would typically process the data and add it to the map + + // Create a mapping of node IDs to their coordinates + const nodes: Map = new Map(); + data.elements.forEach((element: any) => { + if (element.type === 'node') { + nodes.set(element.id, new L.LatLng(element.lat, element.lon)); + } + }); + + // Process ways and create polygons + // TODO: Change to features, to also add the meta data of the building, e.g. the name of the room + data.elements.forEach((element: any) => { + if (element.type === 'way') { + const coordinates: L.LatLng[] = []; + element.nodes.forEach((nodeId: number) => { + coordinates.push(nodes.get(nodeId)!); + }); + L.polygon(coordinates, { color: '#999999', fillColor: '#e0e0e0', fillOpacity: 1 }).addTo( + resultingLayer, + ); + } + }); }); + return resultingLayer; + } + + private getCenter(feature?: any): L.LatLng { + // Calculate the center of the feature using Turf.js, or use a default center if no feature is provided. + let centerPoint: number[] = []; + if (feature) { + centerPoint = center(feature).geometry.coordinates.reverse(); + } else { + centerPoint = [49.015514096207895, 8.391567600294243]; + } + const result = new L.LatLng(centerPoint[0], centerPoint[1]); + return result; } } diff --git a/src/app/overpass.ts b/src/app/overpass.ts index e03812b..fb39c74 100644 --- a/src/app/overpass.ts +++ b/src/app/overpass.ts @@ -1,6 +1,25 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; +export interface OverpassElementNode { + type: 'node'; + id: number; + lat: number; + lon: number; +} + +export interface OverpassElementWay { + type: 'way'; + id: number; + geometry?: Array<{ lat: number; lon: number }>; +} + +export type OverpassElement = OverpassElementNode | OverpassElementWay; + +export interface OverpassResponse { + elements: OverpassElement[]; +} + @Injectable({ providedIn: 'root', }) @@ -13,7 +32,7 @@ export class Overpass { const header = { 'Content-Type': 'text/plain' }; const query = ` [out:json][timeout:25]; - nwr["name"="${building_name}"](49.014442, 8.387954, 49.017847, 8.395448)->.building; + nwr["building"]["operator"="Hochschule Karlsruhe"]["name"="${building_name}"](49.014442, 8.387954, 49.017847, 8.395448)->.building; ( nwr["indoor"="room"](area.building); ); @@ -21,6 +40,6 @@ export class Overpass { out body; `; - return this.http.post(this.apiUrl, query, { headers: header }); + return this.http.post(this.apiUrl, query, { headers: header }); } }