Compare commits
3 Commits
b1464cc32e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 98f5be7c74 | |||
| 57e3c4d3ad | |||
| 7fb116ab2f |
1657
pnpm-lock.yaml
generated
1657
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -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
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
@@ -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,73 +55,71 @@ 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,
|
|
||||||
onEachFeature: (feature, layer) => {
|
const buildingLayer = new L.LayerGroup();
|
||||||
layer.on({
|
|
||||||
click: (e) => {
|
this.overpass.fetchBuildingData().subscribe((data) => {
|
||||||
this.isFullscreen = !this.isFullscreen;
|
console.log('Received building data:', data);
|
||||||
if (!this.isFullscreen) {
|
L.geoJSON(this.createFeatureCollection(data), {
|
||||||
this.map?.setView(this.getCenter(feature.geometry), 20);
|
style: { color: '#d62305', weight: 2, fillColor: '#d4caca', fillOpacity: 1 },
|
||||||
this.buildingLayer = this.queryBuildings(feature.properties.name);
|
onEachFeature: (feature, layer) => {
|
||||||
this.buildingLayer.addTo(this.map!);
|
layer.on({
|
||||||
this.details = new D.Details(feature.properties.name, feature.properties.building)
|
click: (e) => {
|
||||||
} else {
|
this.isFullscreen = !this.isFullscreen;
|
||||||
this.map?.setView(this.getCenter(), 18);
|
if (!this.isFullscreen) {
|
||||||
if (this.buildingLayer) {
|
this.map?.setView(this.getCenter(feature.geometry), 20);
|
||||||
this.map?.removeLayer(this.buildingLayer);
|
this.buildingLayer = this.queryIndoorData(feature.properties.name);
|
||||||
|
this.buildingLayer.addTo(this.map!);
|
||||||
|
this.details = new D.Details(feature.properties.name, feature.properties.building);
|
||||||
|
} else {
|
||||||
|
this.map?.setView(this.getCenter(), 18);
|
||||||
|
if (this.buildingLayer) {
|
||||||
|
this.map?.removeLayer(this.buildingLayer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
console.log('Set isFullscreen to:', this.isFullscreen);
|
||||||
console.log('Set isFullscreen to:', this.isFullscreen);
|
console.log('Clicked on feature:', feature.properties?.name);
|
||||||
console.log('Clicked on feature:', feature.properties?.name);
|
this.cdr.detectChanges();
|
||||||
this.cdr.detectChanges();
|
},
|
||||||
},
|
|
||||||
});
|
|
||||||
if (feature.properties && feature.properties.name) {
|
|
||||||
layer.bindTooltip(feature.properties.name, {
|
|
||||||
permanent: false,
|
|
||||||
direction: 'top',
|
|
||||||
});
|
});
|
||||||
}
|
if (feature.properties && feature.properties.name) {
|
||||||
},
|
layer.bindTooltip(feature.properties.name, {
|
||||||
|
permanent: false,
|
||||||
|
direction: 'top',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}).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);
|
|
||||||
|
|
||||||
// Create a mapping of node IDs to their coordinates
|
this.overpass.fetchIndoorData(building_name).subscribe((data) => {
|
||||||
const nodes: Map<number, L.LatLng> = new Map();
|
console.log('Received indoor data for building:', building_name, data);
|
||||||
data.elements.forEach((element: any) => {
|
L.geoJSON(this.createFeatureCollection(data), {
|
||||||
if (element.type === 'node') {
|
style: { color: '#999999', fillColor: '#e0e0e0', fillOpacity: 1 },
|
||||||
nodes.set(element.id, new L.LatLng(element.lat, element.lon));
|
onEachFeature: (feature, layer) => {
|
||||||
}
|
if (feature.properties) {
|
||||||
});
|
layer.bindTooltip(feature.properties.ref ?? 'Test', {
|
||||||
|
permanent: false,
|
||||||
// Process ways and create polygons
|
direction: 'top',
|
||||||
// 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[] = [];
|
}).addTo(resultingLayer);
|
||||||
element.nodes.forEach((nodeId: number) => {
|
|
||||||
coordinates.push(nodes.get(nodeId)!);
|
|
||||||
});
|
|
||||||
L.polygon(coordinates, { color: '#999999', fillColor: '#e0e0e0', fillOpacity: 1 }).addTo(
|
|
||||||
resultingLayer,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
return resultingLayer;
|
return resultingLayer;
|
||||||
}
|
}
|
||||||
@@ -142,4 +135,46 @@ export class OSMMap implements OnInit {
|
|||||||
const result = new L.LatLng(centerPoint[0], centerPoint[1]);
|
const result = new L.LatLng(centerPoint[0], centerPoint[1]);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createFeatureCollection(data: any): G.FeatureCollection {
|
||||||
|
const buildingData: G.FeatureCollection = {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a mapping of node IDs to their coordinates
|
||||||
|
const nodes: Map<number, L.LatLng> = 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)!);
|
||||||
|
});
|
||||||
|
buildingData.features.push(
|
||||||
|
this.createFeature(new Map(Object.entries(element.tags || {})), coordinates),
|
||||||
|
// this.createFeature(new Map(Object.entries({ name: 'Test' })), coordinates),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return buildingData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createFeature(properties: Map<string, string>, coordinates: L.LatLng[]): G.Feature {
|
||||||
|
return {
|
||||||
|
type: 'Feature',
|
||||||
|
properties: Object.fromEntries(properties),
|
||||||
|
geometry: {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [coordinates.map((coord) => [coord.lng, coord.lat])],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user