diff --git a/E-Bau-Fluchtplaene.pdf b/E-Bau-Fluchtplaene.pdf new file mode 100644 index 0000000..5437045 Binary files /dev/null and b/E-Bau-Fluchtplaene.pdf differ diff --git a/angular.json b/angular.json index b272e94..cb227e8 100644 --- a/angular.json +++ b/angular.json @@ -27,11 +27,14 @@ { "glob": "**/*", "input": "public" + }, + { + "glob": "**/*", + "input": "src/assets", + "output": "assets" } ], - "styles": [ - "src/styles.scss" - ] + "styles": ["src/styles.scss", "node_modules/leaflet/dist/leaflet.css"] }, "configurations": { "production": { diff --git a/package.json b/package.json index 19d78ee..4645b6c 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,11 @@ "@angular/forms": "^21.2.0", "@angular/platform-browser": "^21.2.0", "@angular/router": "^21.2.0", + "@bluehalo/ngx-leaflet": "^21.2.0", + "@types/geojson": "^7946.0.16", + "@types/leaflet": "^1.9.21", + "leaflet": "^1.9.4", + "leaflet-indoor": "^1.0.3", "rxjs": "~7.8.0", "tslib": "^2.3.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb70ac9..d43cf90 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,21 @@ importers: '@angular/router': specifier: ^21.2.0 version: 21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2))(@angular/platform-browser@21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)))(rxjs@7.8.2) + '@bluehalo/ngx-leaflet': + specifier: ^21.2.0 + version: 21.2.0(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2))(leaflet@1.9.4) + '@types/geojson': + specifier: ^7946.0.16 + version: 7946.0.16 + '@types/leaflet': + specifier: ^1.9.21 + version: 1.9.21 + leaflet: + specifier: ^1.9.4 + version: 1.9.4 + leaflet-indoor: + specifier: ^1.0.3 + version: 1.0.3 rxjs: specifier: ~7.8.0 version: 7.8.2 @@ -338,6 +353,13 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@bluehalo/ngx-leaflet@21.2.0': + resolution: {integrity: sha512-gZn6uuQbGI8xK4Q7xqIfzZblNfkuEgKe1vm1lG0Z4SbUKkr/4bNt4bNtvE9XJikVHUGaiYjKHA7wr1OzarP/Aw==} + peerDependencies: + '@angular/common': '21' + '@angular/core': '21' + leaflet: '1' + '@bramus/specificity@2.4.2': resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true @@ -1325,6 +1347,12 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/leaflet@1.9.21': + resolution: {integrity: sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==} + '@vitejs/plugin-basic-ssl@2.1.4': resolution: {integrity: sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -1932,6 +1960,12 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} + leaflet-indoor@1.0.3: + resolution: {integrity: sha512-sSeezQXNXU7RFEbpD5RTJx1vULbU3teA48kF9N3mBdw7tBSAMbGnDXacV7Ctox7XR2ycLclsf+Xyd4FEAVWHtA==} + + leaflet@1.9.4: + resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==} + listr2@9.0.5: resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} engines: {node: '>=20.0.0'} @@ -3058,6 +3092,13 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@bluehalo/ngx-leaflet@21.2.0(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2))(leaflet@1.9.4)': + dependencies: + '@angular/common': 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2))(rxjs@7.8.2) + '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2) + leaflet: 1.9.4 + tslib: 2.8.1 + '@bramus/specificity@2.4.2': dependencies: css-tree: 3.2.1 @@ -3766,6 +3807,12 @@ snapshots: '@types/estree@1.0.8': {} + '@types/geojson@7946.0.16': {} + + '@types/leaflet@1.9.21': + dependencies: + '@types/geojson': 7946.0.16 + '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.1(sass@1.97.3))': dependencies: vite: 7.3.1(sass@1.97.3) @@ -4412,6 +4459,10 @@ snapshots: jsonparse@1.3.1: {} + leaflet-indoor@1.0.3: {} + + leaflet@1.9.4: {} + listr2@9.0.5: dependencies: cli-truncate: 5.2.0 diff --git a/public/favicon.ico b/public/favicon.ico index 57614f9..2e84106 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/src/app/app.config.ts b/src/app/app.config.ts index cb1270e..203905a 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -1,11 +1,9 @@ import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core'; import { provideRouter } from '@angular/router'; +import { provideHttpClient } from '@angular/common/http'; import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { - providers: [ - provideBrowserGlobalErrorListeners(), - provideRouter(routes) - ] + providers: [provideBrowserGlobalErrorListeners(), provideRouter(routes), provideHttpClient()], }; diff --git a/src/app/app.html b/src/app/app.html index a1c4296..885ece7 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -1,344 +1,4 @@ - - - - - - - - - - - -
-
-
- -

Hello, {{ title() }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'Prompt and best practices for AI', link: 'https://angular.dev/ai/develop-with-ai'}, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
-
-
- - - - - - - - - + + diff --git a/src/app/app.ts b/src/app/app.ts index 0c3ee63..2198e1e 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -1,12 +1,12 @@ -import { Component, signal } from '@angular/core'; +import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; +import { Header } from './header/header'; +import { Map } from './map/map'; @Component({ selector: 'app-root', - imports: [RouterOutlet], + imports: [RouterOutlet, Header, Map], templateUrl: './app.html', - styleUrl: './app.scss' + styleUrl: './app.scss', }) -export class App { - protected readonly title = signal('HKA-OSM'); -} +export class App {} diff --git a/src/app/header/header.html b/src/app/header/header.html new file mode 100644 index 0000000..e4401ae --- /dev/null +++ b/src/app/header/header.html @@ -0,0 +1,3 @@ +
+

HKA OSM

+
diff --git a/src/app/header/header.scss b/src/app/header/header.scss new file mode 100644 index 0000000..9baafa2 --- /dev/null +++ b/src/app/header/header.scss @@ -0,0 +1,13 @@ +.header-wrapper { + position: fixed; + display: block; + top: 0; + left: 0; + width: 100%; + height: 54px; + background-color: #d62305; + color: #fff; + text-align: center; + font-weight: bold; + font-family: 'Arial', sans-serif; +} diff --git a/src/app/header/header.spec.ts b/src/app/header/header.spec.ts new file mode 100644 index 0000000..9ef7403 --- /dev/null +++ b/src/app/header/header.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Header } from './header'; + +describe('Header', () => { + let component: Header; + let fixture: ComponentFixture
; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Header], + }).compileComponents(); + + fixture = TestBed.createComponent(Header); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/header/header.ts b/src/app/header/header.ts new file mode 100644 index 0000000..1cd804a --- /dev/null +++ b/src/app/header/header.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-header', + imports: [], + templateUrl: './header.html', + styleUrl: './header.scss', +}) +export class Header {} diff --git a/src/app/map/geojson.ts b/src/app/map/geojson.ts new file mode 100644 index 0000000..6d9b448 --- /dev/null +++ b/src/app/map/geojson.ts @@ -0,0 +1,33 @@ +import * as G from 'geojson'; + +// Custom GeoJSON style for the building +export const buildingSytle = { + color: '#d62305', // Red border + weight: 2, // Border width + fillColor: '#d62305', // Red fill + fillOpacity: 0.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 buildingE: G.Feature = { + type: 'Feature', + properties: { + name: 'Gebäude E', + building: 'university', + center: [8.39007, 49.015], + }, + geometry: examplePolygon, +}; diff --git a/src/app/map/map.html b/src/app/map/map.html new file mode 100644 index 0000000..6395a5a --- /dev/null +++ b/src/app/map/map.html @@ -0,0 +1,8 @@ +
+
+
diff --git a/src/app/map/map.module.ts b/src/app/map/map.module.ts new file mode 100644 index 0000000..062324c --- /dev/null +++ b/src/app/map/map.module.ts @@ -0,0 +1,8 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + declarations: [], + imports: [CommonModule], +}) +export class MapModule {} diff --git a/src/app/map/map.scss b/src/app/map/map.scss new file mode 100644 index 0000000..032c309 --- /dev/null +++ b/src/app/map/map.scss @@ -0,0 +1,9 @@ +.map-container { + padding: 0; + margin: 0; + position: absolute; + top: 54px; + left: 0; + width: 100%; + height: calc(100vh - 54px); +} diff --git a/src/app/map/map.spec.ts b/src/app/map/map.spec.ts new file mode 100644 index 0000000..cceb94a --- /dev/null +++ b/src/app/map/map.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Map } from './map'; + +describe('Map', () => { + let component: Map; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Map], + }).compileComponents(); + + fixture = TestBed.createComponent(Map); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/map/map.ts b/src/app/map/map.ts new file mode 100644 index 0000000..568078b --- /dev/null +++ b/src/app/map/map.ts @@ -0,0 +1,68 @@ +import { Component, signal } from '@angular/core'; +import { LeafletModule } from '@bluehalo/ngx-leaflet'; +import * as L from 'leaflet'; +import * as E from './geojson'; + +@Component({ + selector: 'app-map', + imports: [LeafletModule], + templateUrl: './map.html', + styleUrl: './map.scss', +}) +export class Map { + private centerLocation = { lat: 49.015514096207895, lng: 8.391567600294243 }; + + protected readonly title = signal('HKA-OSM'); + map?: L.Map; + + isFullscreen = false; + + // Define map options + options: L.MapOptions = { + layers: [ + // OpenStreetMap tile layer + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + maxNativeZoom: 19, + maxZoom: 21, + minZoom: 18, + attribution: '© OpenStreetMap contributors', + }), + ], + zoom: 18, // Zoom level + center: L.latLng(this.centerLocation.lat, this.centerLocation.lng), + 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 + }; + + // Optional: Add markers or other features + onMapReady(map: L.Map) { + this.map = map; + L.geoJSON(E.buildingE, { + style: E.buildingSytle, + onEachFeature: (feature, layer) => { + layer.on({ + click: (e) => { + this.map?.setView( + L.latLng( + feature.properties.center[1], // Latitude of the first point + feature.properties.center[0], // Longitude of the first point + ), + 20, + ); + console.log('Clicked on feature:', feature.properties?.name); + }, + }); + if (feature.properties && feature.properties.name) { + layer.bindTooltip(feature.properties.name, { + permanent: false, + direction: 'top', + }); + } + }, + }).addTo(this.map); + console.log('Map is ready'); + } +} diff --git a/src/assets/HKA-Header.svg b/src/assets/HKA-Header.svg new file mode 100644 index 0000000..b13818a --- /dev/null +++ b/src/assets/HKA-Header.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/blue-marker.svg b/src/assets/blue-marker.svg new file mode 100644 index 0000000..40e4839 --- /dev/null +++ b/src/assets/blue-marker.svg @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/src/index.html b/src/index.html index ba421c7..5618293 100644 --- a/src/index.html +++ b/src/index.html @@ -1,13 +1,13 @@ - - - HKAOSM - - - - - - - + + + HKAOSM + + + + + + + diff --git a/src/styles.scss b/src/styles.scss index 90d4ee0..ff669d9 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1 +1,5 @@ /* You can add global styles to this file, and also import other style files */ +body { + margin: 0; + padding: 0; +}