Cet article est orienté vers les développeurs Angular débutants souhaitant intégrer Leaflet dans leurs applications. L’intégration de dépendances tierces dans Angular peut aller du trivial au problématique. Alors que Leaflet tend plus vers le côté trivial, les cartes qui s’ajustent de manière fluide à la taille du navigateur peuvent présenter quelques problèmes de configuration par rapport au cycle de vie Angular.
Avant de commencer la déconstruction, pointez votre navigateur ami et voisin vers le Github suivant.
Maintenant, vous pouvez suivre ou bifurquer le projet et l’utiliser comme base pour vos propres applications.
Background
Ce tutoriel suppose une petite quantité de connaissances préalables sur Leaflet, mais pas plus que ce qui est exprimé dans le Guide de démarrage rapide de Leaflet.
ArcGis est utilisé pour le tuilage dans l’application couverte dans cet article (le paquet esri-leaflet est installé à partir de npm).
Angular Setup
Très peu de choses doivent être faites après l’installation omniprésente de npm. Les styles Leaflet nécessaires sont mis à disposition via le fichier angular.json,
"styles": ,
J’ai eu des résultats mitigés avec @types/leaflet, donc cette application n’exploite pas les typages pour des raisons de simplicité. Les tuiles Leaflet et ESRI sont importées comme indiqué ci-dessous,
import * as esri from 'esri-leaflet';
import * as L from 'leaflet';
Une carte Leaflet, par exemple, est temporairement typée comme n’importe laquelle ; n’hésitez pas à raffermir les typages comme exercice après avoir déconstruit le tutoriel.
Les coordonnées du centre d’une carte sont importantes pour l’initialisation de la carte. De nombreuses démos codent en dur cette information. Comme alternative, ce tutoriel illustre comment injecter les coordonnées du centre de la carte en utilisant les jetons d’injection d’Angular.
Une coordonnée est un point bidimensionnel sur une carte, représenté par la latitude et la longitude. Un InjectionToken pour une coordonnée peut être créé comme illustré dans le fichier /src/app/tokens.ts,
import { InjectionToken } from '@angular/core';
export const INIT_COORDS = new InjectionToken<{lat: number, long: number}>('INIT_COORDS');
INIT_COORDS est utilisé dans le fichier /src/app/app.module.ts pour effectuer la fourniture (ou la définition) réelle (lat/long),
providers: ,
Cela déplace la définition du centre de la carte au niveau le plus élevé de l’application Angular (où elle est facilement visible et presque auto-documentée).
Pour injecter cette coordonnée dans l’application, importez INIT_COORDS depuis le fichier tokens et injectez depuis un constructeur de classe. Ceci est illustré dans le fichier /src/app/map.component.ts (le principal composant de carte dans ce tutoriel),
constructor( @Inject(INIT_COORDS) protected _initCoords: {lat: number, long: number} )
Composant de carte
L’application couverte dans cet article affiche une carte de hauteur fixe avec une largeur qui s’étend sur toute la fenêtre du navigateur. La carte se redessine lorsque la fenêtre est redimensionnée. Certains marqueurs sont également rendus au-dessus des tuiles de la carte.
La carte est intégrée à l’application par le biais du modèle du composant d’application principal, comme indiqué ci-dessous
/src/app/app.component.html
<app-map ="markers"></app-map>
et le code source du composant de carte se trouve dans /src/app/maps/map.component.ts .
La carte est rendue dans un conteneur (DIV) qui est actuellement dans le modèle du composant de carte,
/src/app/maps/map.component.html
<div>
<div>
<div #primaryMap ="currentHeight" ="currentWidth"></div>
<map-control class="map-control"></map-control>
<div class="mouse-coords">
<span></span>
<span ="mcText"></span>
</div>
</div>
</div>
Si la largeur et la hauteur du DIV contenant sont fixes, alors les tuiles de la carte peuvent être rendues à un point précoce du cycle de vie du composant. Deux approches sont possibles pour passer une largeur et une hauteur variables, outside-in et inside-out. La méthode outside-in définit les entrées Angular pour la largeur et la hauteur. Le composant parent est chargé de communiquer la largeur et la hauteur au composant de la carte. L’approche inside-out définit la largeur et la hauteur à l’intérieur du composant map et » transmet » ces informations à son modèle. La plupart des applications utilisent l’approche outside-in. Ce tutoriel utilise la méthode inside-out à des fins d’illustration. Modifiez le code pour changer le passage de la largeur et de la hauteur comme un exercice.
La largeur et la hauteur variables introduisent quelques problèmes subtils relatifs au rendu de la carte. Il est tentant d’effectuer tout ce qui concerne la configuration de Leaflet dans le gestionnaire on-init, et cela fonctionne généralement avec une largeur et une hauteur fixes. Comme la largeur de la carte varie en fonction de la taille du navigateur dans ce tutoriel, il est préférable de séparer le processus d’initialisation de la carte entre les gestionnaires du cycle de vie on-init et after-view-init. Ceci est illustré dans les segments de code suivants de /src/app/maps/map.component.ts .
Premièrement, l’identification d’un DIV contenant pour l’initialisation de la carte est déchargée à un ViewChild,
@ViewChild('primaryMap', {static: true}) protected mapDivRef: ElementRef;
protected mapDiv: HTMLDivElement;
La plupart de la configuration de la carte est effectuée dans ngOnInit,
public ngOnInit(): void
{
// Reference to DIV containing map is used in Leaflet initialization
this.mapDiv = this.mapDivRef.nativeElement;
this.__initializeMap();
this.__renderMap();
this.__showMarkers();
}
L’invalidation de la taille de la carte est reportée à ngAfterViewInit, lorsque la largeur et la hauteur complètes nécessaires au fonctionnement des tuiles de la carte sont disponibles. La variable map fait référence à la carte Leaflet créée dans le gestionnaire ngOnInit.
public ngAfterViewInit(): void
{
this.map.invalidateSize();
this.__initMapHandlers();
}
Notez que le gestionnaire de mise à jour de la taille de la carte recalcule les variables de classe currentWidth et currentHeight, qui sont liées aux styles DIV qui contrôlent la largeur et la hauteur du DIV de la carte. Leaflet utilise ces informations de largeur/hauteur pour re-carreler et re-rendre la carte.
Puisque la définition de la largeur/hauteur de la carte et le redimensionnement de la carte avec les changements de fenêtre sont gérés en interne au composant de la carte, un simple gestionnaire de redimensionnement de fenêtre est ajouté.
@HostListener('window:resize', )
protected __onResize(event: any): void
{
this.__updateMapSize();
this.map.invalidateSize();
}
Cela pourrait également être géré par un service externe, exécuté en dehors d’Angular, et débité. Si l’intérêt est suffisant, je fournirai un tutoriel qui illustre la technique.
Contrôles de carte
Leaflet permet des contrôles personnalisés, mais vous pouvez également utiliser des composants Angular comme contrôles de carte. Créez le composant Angular et utilisez le CSS pour le positionnement. J’ai utilisé cela dans plusieurs projets pour des contrôles de zoom hautement personnalisés. Voir le dossier /src/app/maps/map-control pour un squelette qui peut être utilisé comme point de départ.
Référer à /src/app/maps/map.component.scss pour le style pertinent.
Attention au z-index
Si le tuilage ou un autre aspect de l’affichage de la carte ne semble pas fonctionner, il peut s’agir d’un problème avec le z-index. Dans le passé, j’ai dû refaire les indices z simplement en changeant de fournisseur de tuiles ou même en migrant d’Angular 7 à Angular 8 !
Marqueurs de carte
Des marqueurs avec des coordonnées connues peuvent être ajoutés à une carte. Définissez les coordonnées, puis créez une icône Leaflet pour chacune d’entre elles. Les coordonnées des marqueurs pour ce tutoriel sont définies dans le composant principal de l’app,
/src/app/app.component.ts
public markers: {lat: number, long: number}; // Map markers (relevance depends on map center)
constructor()
{
// some map markers
this.markers = ;
}
et sont passées comme une entrée Angular au composant de la carte,
/src/app/app.component.html
<app-map ="markers"></app-map>
Les marqueurs sont rendus dans le composant de carte,
/src/app/maps/map.component.ts
protected __showMarkers(): void
{
if (this.markers !== undefined && this.markers != null && this.markers.length > 0)
{
// Add markers
const icon = L.icon({
iconUrl: MapIconOptions.mapIcon,
iconSize: MapIconOptions.iconSize,
iconAnchor: MapIconOptions.iconAnchor,
shadowUrl: MapIconOptions.mapShadowIcon,
shadowSize: MapIconOptions.shadowSize,
shadowAnchor: MapIconOptions.shadowAnchor,
});
const n: number = this.markers.length;
let i: number;
let m: L.marker;
let x: number;
let y: number;
for (i = 0; i < n; ++i) {
x = this.markers.lat;
y = this.markers.long;
if (x !== undefined && !isNaN(x) && y !== undefined && !isNaN(y))
{
// okay to add the icon
m = L.marker(, {icon: icon}).addTo(this.map);
}
else
{
// implement your own error handling
console.log('MARKER ERROR, Marker number: ', (i+1), 'x: ', x, ' y: ', y);
}
}
}
}
Interactivité
Un couple de gestionnaires pour l’interactivité de base de la carte a été ajouté pour vous, à savoir le clic de souris et le déplacement de souris. Ce dernier met à jour la position actuelle de la carte (lat/long) en fonction de la position de la souris.
/src/app/maps/map.component.ts
protected __onMapMouseMove(evt: any): void
{
const lat: string = evt.latlng.lat.toFixed(3);
const long: string = evt.latlng.lng.toFixed(3);
this.mcText = `Latitude: ${lat} Longitude: ${long}`;
}
La chaîne reflétant la position actuelle est affichée dans le modèle du composant de carte,
/src/app/maps/map.component.html
<div class="mouse-coords">
<span></span>
<span ="mcText"></span>
</div>
Le résultat
Construisez l’application et vous devriez observer quelque chose de similaire à cette capture d’écran,