This commit is contained in:
2023-04-24 17:21:22 +02:00
parent f303cdd195
commit f2a5d5da1a
17 changed files with 118 additions and 552 deletions

View File

@@ -2,14 +2,12 @@ import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {DetailComponent} from "./detail/detail.component";
import {ListComponent} from "./list/list.component";
@NgModule({
imports: [RouterModule.forRoot([
{path: 'home', component: HomeComponent},
{path: 'location/:location', component: ListComponent},
{path: 'location/:location/:id', component: DetailComponent},
{path: 'detail', component: DetailComponent},
{path: '**', redirectTo: 'home'}
])],
exports: [RouterModule],

View File

@@ -0,0 +1,35 @@
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'm-152'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('m-152');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain('m-152 app is running!');
});
});

View File

@@ -6,23 +6,17 @@ import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { ListComponent } from './list/list.component';
import { DetailComponent } from './detail/detail.component';
import {FormsModule} from "@angular/forms";
import { HttpClientModule} from "@angular/common/http";
import { SafePipe } from './pipes/safe.pipe';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
ListComponent,
DetailComponent,
SafePipe,
DetailComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]

View File

@@ -1,9 +0,0 @@
.titolo {
font-size: 1.5em;
font-weight: bold;
margin-bottom: 0.5em;
}
button {
margin: 1em;
}

View File

@@ -1,24 +1 @@
<div>
<h1 class="titolo">{{test.name}}</h1>
<p>{{test.description}}</p>
<p ng-model="showNav" *ngIf="showNav">Distanza: {{distance}}</p>
<div id="nav" ng-model="showNav" *ngIf="showNav">
<iframe
width="500"
height="300"
frameborder="0" style="border:0"
referrerpolicy="no-referrer-when-downgrade"
[src]='embed | safe '
allowfullscreen>
</iframe>
</div>
<button (click)="generateQR()">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="17.334" height="23.4863">
<g>
<rect height="23.4863" opacity="0" width="17.334" x="0" y="0"/>
<path d="M3.06641 22.041L14.2676 22.041C16.3086 22.041 17.334 21.0254 17.334 19.0137L17.334 9.26758C17.334 7.25586 16.3086 6.24023 14.2676 6.24023L11.543 6.24023L11.543 7.8125L14.2383 7.8125C15.2051 7.8125 15.7617 8.33984 15.7617 9.35547L15.7617 18.9258C15.7617 19.9414 15.2051 20.4688 14.2383 20.4688L3.08594 20.4688C2.10938 20.4688 1.57227 19.9414 1.57227 18.9258L1.57227 9.35547C1.57227 8.33984 2.10938 7.8125 3.08594 7.8125L5.79102 7.8125L5.79102 6.24023L3.06641 6.24023C1.02539 6.24023 0 7.25586 0 9.26758L0 19.0137C0 21.0254 1.02539 22.041 3.06641 22.041ZM8.66211 14.3945C9.08203 14.3945 9.44336 14.043 9.44336 13.6328L9.44336 3.60352L9.38477 2.13867L10.0391 2.83203L11.5234 4.41406C11.6602 4.57031 11.8555 4.64844 12.0508 4.64844C12.4512 4.64844 12.7637 4.35547 12.7637 3.95508C12.7637 3.75 12.6758 3.59375 12.5293 3.44727L9.22852 0.263672C9.0332 0.0683594 8.86719 0 8.66211 0C8.4668 0 8.30078 0.0683594 8.0957 0.263672L4.79492 3.44727C4.64844 3.59375 4.57031 3.75 4.57031 3.95508C4.57031 4.35547 4.86328 4.64844 5.27344 4.64844C5.45898 4.64844 5.67383 4.57031 5.81055 4.41406L7.28516 2.83203L7.94922 2.13867L7.89062 3.60352L7.89062 13.6328C7.89062 14.043 8.24219 14.3945 8.66211 14.3945Z" fill="#000000"/>
</g>
</svg>
</button>
<img src="assets/testDetail/img.png" alt="qr">
</div>
<p>detail works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DetailComponent } from './detail.component';
describe('DetailComponent', () => {
let component: DetailComponent;
let fixture: ComponentFixture<DetailComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DetailComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(DetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,123 +1,10 @@
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from "@angular/router";
import {positionService} from "../service/position.service";
import { getDistance } from 'geolib';
// @ts-ignore
import * as sharp from 'sharp';
import * as qrcode from 'qrcode';
import * as canvas from 'canvas';
import { Component } from '@angular/core';
@Component({
selector: 'app-detail',
templateUrl: './detail.component.html',
styleUrls: ['./detail.component.css']
})
export class DetailComponent implements OnInit {
private location: string | undefined;
private id: number | undefined;
private URLParams: any;
test = {
name: 'SPAI',
cordinates: '46.15187077044123,8.799829438699243',
lat: 46.15187077044123,
lng: 8.799829438699243,
description: "Lorem ipsum"
}
embed = `https://www.google.com/maps/embed/v1/place?key=AIzaSyBJL4FWmG032BG6KXxTb4faxpO_ccyaP3o&q=${this.test.lat},${this.test.lng}`
cord: any;
showNav = true;
distance: number | undefined;
displayedDistance = 0;
constructor(private route: ActivatedRoute , private positionService: positionService) {}
async ngOnInit(){
this.URLParams = this.route.snapshot.url.slice(-2).map(segment => segment.path);
console.log(this.URLParams); // ["lugnao", "1"]
console.log("getting your location: wait...");
this.cord = await this.positionService.getLocation();
console.log("location: ", this.cord);
this.checkDistanceTimer();
}
checkDistanceTimer() {
//set interval
let intervalID = setInterval(() => {
if (this.showNav) {
this.distance = this.positionService.getDistanceBetweenCoordinates(this.cord.lat, this.cord.lon, this.test.lat, this.test.lng);
console.log(this.distance);
if (this.distance == 0) {
this.showNav = false;
this.displayedDistance = Math.round(this.distance * 100) / 100;
}
if (this.distance < 0.05) {
this.showNav = false;
clearInterval(intervalID);
}
} else {
clearInterval(intervalID);
}
}, 1000);
}
async generateQRCodeImage(url: string): Promise<Buffer> {
// Crea il QR code
const qrCode = await qrcode.toDataURL(url, { errorCorrectionLevel: "H" });
// Crea il canvas
const canvasInstance = canvas.createCanvas(300, 300);
const ctx = canvasInstance.getContext("2d");
// Carica il QR code nell'immagine
const qrCodeImage = await canvas.loadImage(qrCode);
// Disegna il QR code nell'immagine
ctx.drawImage(qrCodeImage, 0, 0, 300, 300);
// Ritorna l'immagine come buffer
return canvasInstance.toBuffer();
}
/*generateQR() {
console.log("generating QR code");
let url = `http://localhost:4200/location/${this.URLParams[0]}/${this.URLParams[1]}`;
//this.addQRCodeToImage(url, `assets/testDetail/img.png`, `assets/images/${url}.png`);
console.log(url)
}*/
/*async addQRCodeToImage(url: string, imagePath: string, outputPath: string): Promise<void> {
// Generate QR code
const qrCode = await qrcode.toBuffer(url);
// Load input image using Sharp
const image = sharp(imagePath);
// Get input image dimensions
const { width, height } = await image.metadata();
// Resize QR code to 25% of input image height
const qrCodeHeight = Math.round(height * 0.25);
const qrCodeBuffer = await sharp(qrCode)
.resize(qrCodeHeight, qrCodeHeight)
.toBuffer();
// Composite QR code onto input image at bottom-right corner
await image.composite([
{
input: qrCodeBuffer,
gravity: 'southeast',
top: height - qrCodeHeight,
left: width - qrCodeHeight,
},
]);
// Save output image to file
await image.toFile(outputPath);
}*/
export class DetailComponent {
}

View File

@@ -1,52 +1,6 @@
.search {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: absolute;
top: 35%;
left: 50%;
transform: translate(-50%, -50%);
}
input {
input{
width: 40%;
display: flex;
justify-content: center;
align-items: center;
}
#list {
position: absolute;
top: 46%;
left: 44%;
transform: translate(-50%, -50%);
justify-content: center;
align-items: center;
align-content: center;
margin-top: 10px;
margin-left: 0;
}
#list > li {
}
.inputElements{
display: flex;
justify-content: center;
align-items: center;
align-content: center;
gap: 20px;
}
.bg-image {
background-image: url('src/assets/img/mountains.png');
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
height: 100vh;
}
#translate{
margin: 100px 10px 10px 10px;
}

View File

@@ -1,58 +1,2 @@
<div [ngClass]="{'background-color': backgroundColor}">
<input type="text" placeholder="Type here" class="input input-bordered input-primary w-full max-w-xs" />
<div class="alert alert-warning shadow-lg" *ngIf="allert">
<div>
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
</svg>
<span>{{translations.alertMessage}}</span>
</div>
</div>
<div class="dropdown dropdown-hover">
<label tabindex="0" class="btn m-1 bg-black border-neutral text-base-100">{{translations.translate}}</label>
<ul tabindex="0" class="dropdown-content menu p-2 shadow rounded-box w-52">
<li (click)="switchLanguage('DE')"><a>DE</a></li>
<li (click)="switchLanguage('FR')"><a>FR</a></li>
<li (click)="switchLanguage('IT')"><a>IT</a></li>
</ul>
</div>
<div class="bg-image">
<div class="search">
<div class="inputElements">
<input #myInput type="text" [(ngModel)]="luogoSelezionato" (keyup)="cercaLuogo(myInput.value)"
(keydown)="selezionaSuggerimento($event)" placeholder="{{translations.searchPlaceholder}}"
class="input input-bordered input-primary btn-wide">
<button
class="btn gap-2 border-primary bg-primary text-secondary hover:bg-secondary hover:text-primary hover:border-primary"
(click)="onSearch()">
{{translations.searchButton}}
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
d="M18.125,15.804l-4.038-4.037c0.675-1.079,1.012-2.308,1.01-3.534C15.089,4.62,12.199,1.75,8.584,1.75C4.815,1.75,1.982,4.726,2,8.286c0.021,3.577,2.908,6.549,6.578,6.549c1.241,0,2.417-0.347,3.44-0.985l4.032,4.026c0.167,0.166,0.43,0.166,0.596,0l1.479-1.478C18.292,16.234,18.292,15.968,18.125,15.804 M8.578,13.99c-3.198,0-5.716-2.593-5.733-5.71c-0.017-3.084,2.438-5.686,5.74-5.686c3.197,0,5.625,2.493,5.64,5.624C14.242,11.548,11.621,13.99,8.578,13.99 M16.349,16.981l-3.637-3.635c0.131-0.11,0.721-0.695,0.876-0.884l3.642,3.639L16.349,16.981z"></path>
</svg>
</button>
</div>
</div>
<ng-container *ngIf="locationsPopup | async as locations;">
<ul *ngIf="locations.length > 0" class="menu bg-base-200 p-2 rounded-box" id="list"
[style.width.px]="myInput.offsetWidth">
<li class="menu-title">
<span>{{translations.menuPlaces}}</span>
</li>
<li *ngFor="let luogo of locations" (click)="luogoSelezionato=luogo.location; cercaLuogo(luogo.location)">
{{luogo.location}}
</li>
</ul>
</ng-container>
</div>
</div>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeComponent } from './home.component';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HomeComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,139 +1,10 @@
import {AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {BehaviorSubject, distinctUntilChanged, fromEvent, Observable, Subject, Subscription} from "rxjs";
import {ReadjsonService} from "../service/readjson.service";
import {Locations} from "../interface/data";
import {Router} from "@angular/router";
import { TranslateService } from '../service/translate.service';
import {ReadTranslateJsonService} from "../service/readTranslateJsonService";
import {homeTranslations} from "../interface/translations";
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('myInput') myInput?: ElementRef;
export class HomeComponent {
public locationsPopup: Subject<Locations[]> = new Subject<Locations[]>()
subs: Subscription[] = [];
backgroundColor: string | undefined;
locations: Locations[] = [];
allert: boolean = false;
locationsFiltrati: Locations[] = [];
luogoSelezionato: string = '';
suggerimentoAttivo: boolean = false;
suggerimento: string = '';
completamento: string = '';
translations: homeTranslations = {} as homeTranslations;
constructor(private readjsonService: ReadjsonService, private router: Router, private translateService: TranslateService, private readTranslationJsonService: ReadTranslateJsonService) {
}
ngOnInit(): void {
this.translations = this.readTranslationJsonService.getHomeTranslations();
console.log("translations loaded", this.translations)
this.readjsonService.getLocations().subscribe(data => {
for (let i = 0; i < data.length; i++) {
this.locations.push(<Locations>data[i])
console.log(data[i])
}
});
this.allert = false;
console.log("home init");
}
ngOnDestroy() {
this.subs.forEach(sub => sub.unsubscribe())
}
ngAfterViewInit() {
if (this.locations != undefined) {
fromEvent(this.myInput?.nativeElement, 'focus').pipe(
// debounceTime(500), decommentarlo se bisogna fare una chiamata http
distinctUntilChanged()
).subscribe((val: any) => {
this.locationsPopup.next(this.locations.filter(l => l.location.toLowerCase().startsWith(val.target.value.toLowerCase())))
})
}
fromEvent(this.myInput?.nativeElement, 'input')
.pipe(
// debounceTime(500), decommentarlo se bisogna fare una chiamata http
distinctUntilChanged()
).subscribe((val: any) => {
this.locationsPopup.next(this.locations.filter(l => l.location.toLowerCase().startsWith(val.target.value.toLowerCase())))
})
}
cercaLuogo(locations: string) {
setTimeout(() => {
}, 1000);
this.locationsFiltrati = this.locations.filter((l: Locations) => l.location.toLowerCase().startsWith(locations.toLowerCase()));
if (this.locationsFiltrati.length > 0) {
this.suggerimentoAttivo = true;
this.suggerimento = this.locationsFiltrati[0].location;
this.completamento = stringDifference(locations, this.suggerimento);
} else {
this.suggerimentoAttivo = false;
this.suggerimento = '';
}
this.myInput?.nativeElement.focus();
}
selezionaSuggerimento(event: KeyboardEvent) {
if (event.key === 'Tab' || event.key === 'Enter') {
if (this.suggerimentoAttivo) {
this.luogoSelezionato = this.suggerimento;
this.suggerimentoAttivo = false;
this.suggerimento = '';
}
}
}
luoghiNear() {
return null;
}
onSearch(): void {
if (this.luogoSelezionato === '') {
this.allert = true;
setTimeout(() => {
this.allert = false;
}, 3000);
return;
} else {
const nomeLocation = encodeURIComponent(this.luogoSelezionato);
this.router.navigate(['/location', nomeLocation]);
}
}
async switchLanguage(lang: string) {
this.translations.translate = await this.translateService.getData(this.translations.translate, lang);
this.translations.menuPlaces = await this.translateService.getData(this.translations.menuPlaces, lang);
this.translations.alertMessage = await this.translateService.getData(this.translations.alertMessage, lang);
this.translations.searchPlaceholder = await this.translateService.getData(this.translations.searchPlaceholder, lang);
this.translations.searchButton = await this.translateService.getData(this.translations.searchButton, lang);
}
}
function stringDifference(str1: string, str2: string): string {
let diff = '';
for (let i = 0; i < str2.length; i++) {
if (str1[i] !== str2[i]) {
diff += str2[i];
}
}
return diff;
}

View File

@@ -1,37 +1 @@
<div class="dropdown dropdown-hover">
<label tabindex="0" class="btn m-1 bg-black border-neutral text-base-100">{{translations.translate}}</label>
<ul tabindex="0" class="dropdown-content menu p-2 shadow rounded-box w-52">
<li (click)="switchLanguage('DE')"><a>DE</a></li>
<li (click)="switchLanguage('FR')"><a>FR</a></li>
<li (click)="switchLanguage('IT')"><a>IT</a></li>
</ul>
</div>
<div *ngIf="isNear">
<h1>
{{translations.locationName}}{{locationParams}}
</h1>
<div *ngFor="let locations of locations, let i = index">
<h3>
<a class="link link-primary" href="location/{{locations.location}}">{{locations.location}}</a>
</h3>
<div *ngIf="distance[i]">
<h4>{{translations.distance}}{{distance[i]}} km</h4>
</div>
</div>
</div>
<div *ngIf="!isNear && location && location.waypoints">
<h1>
{{location.location}}
</h1>
<div *ngFor="let waypoinst of location.waypoints, let i = index">
<h3>
<a class="link link-primary" href="location/{{location.location}}/{{waypoinst.id}}"> {{waypoinst.name}}</a>
</h3>
<div *ngIf="distance[i]">
<h4>{{translations.distance}}{{distance[i]}} km</h4>
</div>
</div>
</div>
<p>list works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ListComponent } from './list.component';
describe('ListComponent', () => {
let component: ListComponent;
let fixture: ComponentFixture<ListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ListComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,114 +1,10 @@
import {Component, OnInit, SimpleChanges, OnChanges} from '@angular/core';
import {Locations} from "../interface/data";
import {ReadjsonService} from "../service/readjson.service";
import {ActivatedRoute} from "@angular/router";
import {positionService} from "../service/position.service";
import {listTranslations} from "../interface/translations";
import {TranslateService} from "../service/translate.service";
import {ReadTranslateJsonService} from "../service/readTranslateJsonService";
import { Component } from '@angular/core';
@Component({
selector: 'app-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.css']
})
export class ListComponent implements OnInit, OnChanges {
locationParams: string | undefined
locations: Partial<Locations>[] | undefined;
location: Partial<Locations> | undefined;
positionCord: any;
isNear: boolean = true;
distance: number[] = [];
translations: listTranslations = {} as listTranslations
constructor(private route: ActivatedRoute, private readjsonService: ReadjsonService, private positionService: positionService, private translateService: TranslateService, private readTranslationJsonService: ReadTranslateJsonService) {
}
async ngOnInit() {
this.translations = this.readTranslationJsonService.getListTransaltions();
this.route.params.subscribe(params => {
this.locationParams = params['location'];
});
this.readjsonService.getLocations().subscribe(locations => {
this.locations = locations;
if (this.locationParams != null) {
this.readjsonService.getLocation(this.locationParams ?? "").subscribe(async location => {
this.location = location;
this.readjsonService.getWaypoints(this.locationParams ?? "").subscribe(waypoints => {
if (this.location) {
this.location.waypoints = waypoints ?? []
}
});
await this.checkDataPopulated();
});
}
});
this.getPosition();
}
ngOnChanges(changes: SimpleChanges) {
if (changes['positionCord'] && (changes['positionCord'])) {
console.log("onChanges")
this.setDistance();
}
}
private checkDataPopulated(): void {
if (this.locations && this.location) {
console.log("Dati popolati correttamente:", this.locations, this.location);
for (let i = 0; i < this.locations.length; i++) {
if (this.locations[i].location === this.locationParams) {
this.location = this.locations[i];
console.log("Location trovata:", this.location);
this.isNear = false;
this.setDistance();
break;
}
}
}
}
private setDistance(): void {
if (this.locations && this.location){
if (this.isNear){
console.log("location lenght " + this.locations.length);
for (let i = 0; i < this.locations.length; i++) {
console.log("for"+i);
console.log("lat" + this.locations[i].lat);
this.distance.push(this.positionService.getDistanceBetweenCoordinates(this.locations[i].lat, this.locations[i].lon, this.positionCord.lat, this.positionCord.lon));
}
} else{
if (this.location?.waypoints) {
console.log("waypoints lenght " + this.location.waypoints.length);
for (let i = 0; i < this.location.waypoints.length; i++) {
console.log("for"+i);
console.log("lat" + this.location.waypoints[i].lat);
this.distance.push(this.positionService.getDistanceBetweenCoordinates(this.location.waypoints[i].lat, this.location.waypoints[i].lon, this.positionCord.lat, this.positionCord.lon));
}
}
}
}
console.log("ciao" + this.distance[0])
}
getPosition(): any {
setInterval(async () => {
this.positionCord = await this.positionService.getLocation();
this.setDistance();
}, 2000);
}
async switchLanguage(lang: string) {
this.translations.translate = await this.translateService.getData(this.translations.translate, lang);
this.translations.distance = await this.translateService.getData(this.translations.distance, lang);
this.translations.locationName = await this.translateService.getData(this.translations.locationName, lang);
}
export class ListComponent {
}

BIN
src/assets/mountains.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

View File

@@ -2,7 +2,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body{
overflow-y: hidden;
}