apiplatform 1/2: récupérer les endroits les plus proches filtrer les résultats par distance
26 avril 2020

apiplatform 2/2: filtrer les endroits les plus prôches par distance

Par ghaliano

apiplatform et angular vous aident à Chercher des endroits à visiter en Tunisie
Chercher des endroits à visiter en Tunisie

Objectif

Filtrer à l’aide d’une map des endroits sauvegardés et récupérés à partir de apiplatform
l’idée est de proposer une interface angular capable de positionner un cercle sur une map, de choisir un centre et un rayon de recherche.

cet article et une continuation d’un article précédent intitulé récupérer les endroits les plus proches, prière à le consulter avant de lire celui la

Outils de travail

Pour pouvoir suivre cet article une connaissance préalable des outils suivant est recommandé:

  • api-platform
  • angular
  • mysql
  • Les dataproviders
  • composant angular

Veuillez créer votre projet Angular, l’exemple suivant est mis dans un projet angular minimaliste

le plugin Gmap

Il existe plusieurs tentations pour mettre google map sous forme d’un plugin, y’on a une tentative dans le core officiel d’angular, et une autre (que je préfère)dont voici la commande pour l’installer

npm install @agm/core

Intégration

   import { AgmCoreModule } from '@agm/core';

Générer la clé API Google Maps here

Ajoutez AgmCoreModule à votre tableau d’importations à l’intérieur du fichier app.module.ts.

   imports: [
    BrowserModule,
    AgmCoreModule.forRoot({
      apiKey: environments.google_map_api_key
    })
  ],

En ajoutant le composant Google Maps à un composant, nous pouvons déjà voir et utiliser la carte Google. La carte se comportera comme une carte par défaut avec la fonctionnalité par défaut, par exemple, vous pouvez zoomer et dézoomer et faire glisser la carte. Il nous reste seulement l’ajout des markeurs des différents endroits

<agm-map></agm-map>

Centrer la map avec votre position géolocalisé

ngOnInit(){
    navigator.geolocation.getCurrentPosition(position => {
        this.location = {
            lat: position.coords.latitude,
            lng: position.coords.longitude,
        }
    })
}

Variables d’environnement

Configurer vos variables d »environnements dans le fichier environments/environments.ts

export const environment = {
  production: false,
  server: 'http://127.0.0.1:8000/',
  server_api: 'http://127.0.0.1:8000/api/',
  google_map_api_key: "Votre api ici"
};

la requête vers api-platform

Si vous êtes en local et que vous tomber sur cette erreur

Ajouter votre adresse local via ces deux lignes dans le fichiers config/ packages / framework.yaml

trusted_hosts:
- localhost
- 127.0.0.1

Filtrer par distance

La partie intéressante commence, comment récupérer les endroits relative à une distance donnée ?

Bien évidement, la bonne pratique nous encourage de créer un service pour consommer l’api, lancer cette commande angular pour créer rapidement un service

ng g service services/place

Le code de base du service

import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {environment} from "../../environments/environment";
import {debounceTime, map} from "rxjs/operators";
import {Observable} from "rxjs";

@Injectable({
  providedIn: 'root'
})
export class PlaceService {

  constructor(private http: HttpClient) {
  }

  fetch(params):Observable<any>{
    return this.http.get(environment.server_api + 'places')
        .pipe(debounceTime(5000), map((res)=>{
          return res;
        }));
  }
}

Le code du composant

import {Component, OnInit} from '@angular/core';
import {PlaceService} from "../services/place.service";

interface Place {
  id?: number;
  name: string;
  description?: string;
  longitude: number;
  latitude: number;
  picture?: number;
}

interface Filter {
  longitude?: number;
  latitude?: number;
  distance?: number;
}

@Component({
  selector: 'app-api-platform-part2',
  templateUrl: './api-platform-part2.component.html',
  styleUrls: ['./api-platform-part2.component.css']
})
export class ApiPlatformPart2Component implements OnInit {
  mapOptions: any = {
    radius: 10000, //100km by default,
    latitude: null,
    longitude: null
  };
  filter: Filter = {
    distance: this.mapOptions.radius/1000
  };
  places: Array<Place> = [];
  isLoading:boolean = false;

  constructor(private placeService: PlaceService) {
  }

  ngOnInit(){
    navigator.geolocation.getCurrentPosition(position => {
      this.mapOptions.latitude = this.filter.latitude = position.coords.latitude;
      this.mapOptions.longitude = this.filter.longitude = position.coords.longitude;
      this.fetchPlaces();
    })

  }

  private fetchPlaces(init: Boolean = false) {
    this.isLoading = true;
    this.placeService.fetch(this.filter)
        .subscribe((result)=>{
          if (init){
            this.places = [];
          }

          this.isLoading = false;
          result["hydra:member"].forEach((place)=>{
            this.places.push(place);
          });
        });
  }

  mapCircleDragEndListener(event) {
    this.fetchPlaces(true);
  }

  mapCircleRadiusChangeListener(radius) {
    this.filter.distance = radius/1000;
    this.fetchPlaces(true);
  }

  mapCircleCenterChangeListener(event) {
    this.filter.latitude = event.lat;
    this.filter.longitude = event.lng;
  }
}

Le html du composant

et on fini avec le html (il contient la configuration de la map et l’initialisation du cercle)

<div class="container">
    <div class="map-container">
        <agm-map
            *ngIf="mapOptions.latitude &amp;&amp; mapOptions.longitude"
            [latitude]="mapOptions.latitude"
            [longitude]="mapOptions.longitude">

            <agm-circle
                    [latitude]="mapOptions.latitude"
                    [longitude]="mapOptions.longitude"
                    [radius]="mapOptions.radius"
                    [fillColor]="'red'"
                    [circleDraggable]="true"
                    [editable]="true"
                    (centerChange)="mapCircleCenterChangeListener($event)"
                    (dragEnd)="mapCircleDragEndListener($event)"
                    (radiusChange)="mapCircleRadiusChangeListener($event)"
            >
            </agm-circle>
        </agm-map>
    </div>
    <div class="result-container">
        <ul>
            <li *ngFor="let place of places">
                <img [src]="place.picture" style="margin: 3px; width: 50px; float: left;"/>

                <p><b>{{place.name}}</b>{{place.description}}</p>
            </li>
        </ul>
    </div>
</div>