Compare commits

...

3 Commits

4 changed files with 1768 additions and 130 deletions

1657
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,67 +0,0 @@
import * as G from 'geojson';
// Custom GeoJSON style for the building
export const buildingSytle = {
color: '#d62305', // Red border
weight: 2, // Border width
fillColor: '#d4caca', // Red fill
fillOpacity: 1, // Semi-transparent fill
};
// Example GeoJSON data
export const examplePolygon: G.Polygon = {
type: 'Polygon',
coordinates: [
[
[8.3897067, 49.0149349], // Southwest corner
[8.3904111, 49.0149078], // Southeast corner
[8.3904345, 49.0151542], // Northeast corner
[8.3897302, 49.0151832], // Northwest corner
[8.3897067, 49.0149349], // Closing the polygon by repeating the first point
],
],
};
export const buildings: G.FeatureCollection = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {
name: 'E',
building: 'university',
},
geometry: {
type: 'Polygon',
coordinates: [
[
[8.3897067, 49.0149349], // Southwest corner
[8.3904111, 49.0149078], // Southeast corner
[8.3904345, 49.0151542], // Northeast corner
[8.3897302, 49.0151832], // Northwest corner
[8.3897067, 49.0149349], // Closing the polygon by repeating the first point
],
],
},
},
{
type: 'Feature',
properties: {
name: 'F',
building: 'university',
},
geometry: {
type: 'Polygon',
coordinates: [
[
[8.3897585, 49.015502], // Southwest corner
[8.3904592, 49.0154731], // Southeast corner
[8.3904833, 49.0157255], // Northeast corner
[8.3897827, 49.0157543], // Northwest corner
[8.3897585, 49.015502], // Closing the polygon by repeating the first point
],
],
},
},
],
};

View File

@@ -4,9 +4,9 @@ import { LeafletModule } from '@bluehalo/ngx-leaflet';
import { center } from '@turf/turf'; import { center } from '@turf/turf';
import { Overpass } from '../overpass'; import { Overpass } from '../overpass';
import * as L from 'leaflet'; import * as L from 'leaflet';
import * as G from 'geojson';
import * as D from './details'; import * as D from './details';
import * as E from './geojson';
@Component({ @Component({
selector: 'app-map', selector: 'app-map',
@@ -29,7 +29,7 @@ export class OSMMap implements OnInit {
constructor( constructor(
private overpass: Overpass, private overpass: Overpass,
private cdr: ChangeDetectorRef private cdr: ChangeDetectorRef,
) {} ) {}
ngOnInit() { ngOnInit() {
@@ -45,11 +45,6 @@ export class OSMMap implements OnInit {
layers: [this.osm], layers: [this.osm],
zoom: 18, // Zoom level zoom: 18, // Zoom level
center: this.getCenter(), // Center of the map 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
),
maxBoundsViscosity: 1.0, // Prevent panning outside bounds
}; };
this.layerControl = L.control.layers({ this.layerControl = L.control.layers({
@@ -60,22 +55,28 @@ export class OSMMap implements OnInit {
onMapReady(map: L.Map) { onMapReady(map: L.Map) {
this.map = map; this.map = map;
this.layerControl?.addTo(this.map); this.layerControl?.addTo(this.map);
this.loadBuildings().addTo(this.map); this.queryBuildingData().addTo(this.map);
console.log('Map is ready'); console.log('Map is ready');
} }
loadBuildings() { queryBuildingData() {
return L.geoJSON(E.buildings, { // Query the Overpass API for building data and add it to the map as a GeoJSON layer.
style: E.buildingSytle,
const buildingLayer = new L.LayerGroup();
this.overpass.fetchBuildingData().subscribe((data) => {
console.log('Received building data:', data);
L.geoJSON(this.createFeatureCollection(data), {
style: { color: '#d62305', weight: 2, fillColor: '#d4caca', fillOpacity: 1 },
onEachFeature: (feature, layer) => { onEachFeature: (feature, layer) => {
layer.on({ layer.on({
click: (e) => { click: (e) => {
this.isFullscreen = !this.isFullscreen; this.isFullscreen = !this.isFullscreen;
if (!this.isFullscreen) { if (!this.isFullscreen) {
this.map?.setView(this.getCenter(feature.geometry), 20); this.map?.setView(this.getCenter(feature.geometry), 20);
this.buildingLayer = this.queryBuildings(feature.properties.name); this.buildingLayer = this.queryIndoorData(feature.properties.name);
this.buildingLayer.addTo(this.map!); this.buildingLayer.addTo(this.map!);
this.details = new D.Details(feature.properties.name, feature.properties.building) this.details = new D.Details(feature.properties.name, feature.properties.building);
} else { } else {
this.map?.setView(this.getCenter(), 18); this.map?.setView(this.getCenter(), 18);
if (this.buildingLayer) { if (this.buildingLayer) {
@@ -94,17 +95,52 @@ export class OSMMap implements OnInit {
}); });
} }
}, },
}).addTo(buildingLayer);
}); });
return buildingLayer;
} }
queryBuildings(building_name: string) { queryIndoorData(building_name: string): L.LayerGroup {
// Get the building name from the feature properties and query the Overpass API for detailed building data, // Get the building name from the feature properties and query the Overpass API for detailed building data,
// including indoor features. // including indoor features.
// Create a new LayerGroup to hold the building polygons // Create a new LayerGroup to hold the building polygons
const resultingLayer = new L.LayerGroup(); const resultingLayer = new L.LayerGroup();
this.overpass.fetchBuildings(building_name).subscribe((data) => {
console.log('Fetched building data from Overpass API:', data); this.overpass.fetchIndoorData(building_name).subscribe((data) => {
console.log('Received indoor data for building:', building_name, data);
L.geoJSON(this.createFeatureCollection(data), {
style: { color: '#999999', fillColor: '#e0e0e0', fillOpacity: 1 },
onEachFeature: (feature, layer) => {
if (feature.properties) {
layer.bindTooltip(feature.properties.ref ?? 'Test', {
permanent: false,
direction: 'top',
});
}
},
}).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;
}
private createFeatureCollection(data: any): G.FeatureCollection {
const buildingData: G.FeatureCollection = {
type: 'FeatureCollection',
features: [],
};
// Create a mapping of node IDs to their coordinates // Create a mapping of node IDs to their coordinates
const nodes: Map<number, L.LatLng> = new Map(); const nodes: Map<number, L.LatLng> = new Map();
@@ -122,24 +158,23 @@ export class OSMMap implements OnInit {
element.nodes.forEach((nodeId: number) => { element.nodes.forEach((nodeId: number) => {
coordinates.push(nodes.get(nodeId)!); coordinates.push(nodes.get(nodeId)!);
}); });
L.polygon(coordinates, { color: '#999999', fillColor: '#e0e0e0', fillOpacity: 1 }).addTo( buildingData.features.push(
resultingLayer, this.createFeature(new Map(Object.entries(element.tags || {})), coordinates),
// this.createFeature(new Map(Object.entries({ name: 'Test' })), coordinates),
); );
} }
}); });
}); return buildingData;
return resultingLayer;
} }
private getCenter(feature?: any): L.LatLng { private createFeature(properties: Map<string, string>, coordinates: L.LatLng[]): G.Feature {
// Calculate the center of the feature using Turf.js, or use a default center if no feature is provided. return {
let centerPoint: number[] = []; type: 'Feature',
if (feature) { properties: Object.fromEntries(properties),
centerPoint = center(feature).geometry.coordinates.reverse(); geometry: {
} else { type: 'Polygon',
centerPoint = [49.015514096207895, 8.391567600294243]; coordinates: [coordinates.map((coord) => [coord.lng, coord.lat])],
} },
const result = new L.LatLng(centerPoint[0], centerPoint[1]); };
return result;
} }
} }

View File

@@ -28,11 +28,11 @@ export class Overpass {
constructor(private http: HttpClient) {} constructor(private http: HttpClient) {}
fetchBuildings(building_name: string) { fetchIndoorData(building_name: string) {
const header = { 'Content-Type': 'text/plain' }; const header = { 'Content-Type': 'text/plain' };
const query = ` const query = `
[out:json][timeout:25]; [out:json][timeout:90];
nwr["building"]["operator"="Hochschule Karlsruhe"]["name"="${building_name}"](49.014442, 8.387954, 49.017847, 8.395448)->.building; nwr["building"]["operator"="Hochschule Karlsruhe"]["name"="${building_name}"]->.building;
( (
nwr["indoor"="room"](area.building); nwr["indoor"="room"](area.building);
); );
@@ -42,4 +42,17 @@ export class Overpass {
return this.http.post<OverpassResponse>(this.apiUrl, query, { headers: header }); return this.http.post<OverpassResponse>(this.apiUrl, query, { headers: header });
} }
fetchBuildingData() {
const header = { 'Content-Type': 'text/plain' };
const query = `
[out:json][timeout:90];
(
way["building"="university"]["operator"="Hochschule Karlsruhe"];
);
(._;>;);
out body;
`;
return this.http.post<OverpassResponse>(this.apiUrl, query, { headers: header });
}
} }