Integrering af Leaflet-kort i Angular-versionen 8
17. okt, 2019 – 6 min read
Denne artikel er orienteret mod begyndende Angular-udviklere, der ønsker at integrere Leaflet i deres applikationer. Integrationen af afhængigheder fra tredjeparter i Angular kan være alt fra triviel til problematisk. Mens Leaflet tenderer mere mod den trivielle side, kan kort, der flydende tilpasser sig med browserstørrelsen, præsentere nogle opsætningsproblemer i forhold til Angular-livscyklus.
Hvor du begynder dekonstruktionen, skal du pege din venlige, nærliggende browser til følgende Github.
Nu kan du følge med eller gafle projektet og bruge det som en base for dine egne applikationer.
Baggrund
Denne vejledning forudsætter en lille mængde forudgående kendskab til Leaflet, men ikke mere end det, der kommer til udtryk i Leaflet Quickstart Guide.
ArcGis bruges til tiling i den applikation, der er omfattet af denne artikel (esri-leaflet-pakken installeres fra npm).
Angular Setup
Der er meget lidt, der skal gøres efter den allestedsnærværende npm-installation. Nødvendige Leaflet-stilarter gøres tilgængelige via angular.json-filen,
"styles": ,
Jeg har haft blandede resultater med @types/leaflet, så denne applikation udnytter ikke typninger af hensyn til enkelheden. Leaflet- og ESRI-fliser importeres som vist nedenfor,
import * as esri from 'esri-leaflet';
import * as L from 'leaflet';
Et Leaflet Map, for eksempel, er midlertidigt typet som enhver; du er velkommen til at fastlægge typninger som en øvelse, når du har dekonstrueret tutorialen.
Koordinater for et kortets centrum er vigtige for kortinitialisering. Mange demoer hardcode disse oplysninger. Som et alternativ illustrerer denne tutorial, hvordan du injicerer koordinater for kortets centrum ved hjælp af Angular’s Injection Tokens.
Et koordinat er et todimensionelt punkt på et kort, repræsenteret ved bredde- og længdegrad. Et InjectionToken for et koordinat kan oprettes som illustreret i filen /src/app/tokens.ts,
import { InjectionToken } from '@angular/core';
export const INIT_COORDS = new InjectionToken<{lat: number, long: number}>('INIT_COORDS');
INIT_COORDS bruges i /src/app/app/app.module.ts-filen til at udføre den faktiske (lat/long) bestemmelse (eller definition),
providers: ,
Dette flytter definitionen af kortets centrum til det højeste niveau i Angular-applikationen (hvor det er let at se og næsten selvdokumenterende).
For at injicere denne koordinat i applikationen skal du importere INIT_COORDS fra tokens-filen og injicere fra en klassekonstruktør. Dette er illustreret i filen /src/app/map.component.ts (den primære kortkomponent i denne vejledning),
constructor( @Inject(INIT_COORDS) protected _initCoords: {lat: number, long: number} )
Kortkomponent
Den applikation, der er omfattet af denne artikel, viser et kort med fast højde med en bredde, der strækker sig over hele browservinduet. Kortet tegnes på ny, når vinduet ændres i størrelse. Nogle markører gengives også oven på kortfliserne.
Kortet er integreret i programmet via den primære app-komponentens skabelon, som vist nedenfor
/src/app/app/app.component.html
<app-map ="markers"></app-map>
og kortkomponentens kildekode findes i /src/app/maps/map.component.ts .
Kortet gengives i en container (DIV), der i øjeblikket er i kortkomponentens skabelon,
/src/app/maps/map/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>
Hvis bredden og højden af den indeholdende DIV er fast, kan kortfliserne gengives på et tidligt tidspunkt i komponentens livscyklus. Der er to mulige fremgangsmåder for overdragelse af variabel bredde og højde, outside-in og inside-out. Metoden outside-in definerer Angular Inputs for bredde og højde. Den overordnede komponent er ansvarlig for at kommunikere bredde og højde til kortkomponenten. Inside-out-metoden definerer bredde og højde internt i kortkomponenten og “videregiver” disse oplysninger til dens skabelon. De fleste programmer bruger den udvendigt indvendige tilgang. I denne vejledning anvendes inside-out-metoden af hensyn til illustrationen. Som en øvelse kan du ændre koden for at ændre videregivelsen af bredde og højde.
Variable bredde og højde medfører nogle subtile problemer i forbindelse med kortgengivelse. Det er fristende at udføre alt relateret til Leaflet-opsætning i on-init-handleren, og dette fungerer typisk med fast bredde og højde. Da kortbredden varierer med browserstørrelsen i denne vejledning, er det bedre at adskille kortinitialiseringsprocessen mellem on-init- og after-view-init-livscyklushandlerne. Dette illustreres i følgende kodesegmenter fra /src/app/maps/map/map.component.ts .
Først aflastes identifikationen af en indeholdende DIV til kortinitialisering til en ViewChild,
@ViewChild('primaryMap', {static: true}) protected mapDivRef: ElementRef;
protected mapDiv: HTMLDivElement;
Det meste af kortopsætningen udføres i 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();
}
Invalidering af kortstørrelsen udskydes til ngAfterViewInit, når den fulde bredde og højde, der er nødvendig for, at kortfliserne fungerer, er til rådighed. Map-variablen henviser til det Leaflet Map, der er oprettet i ngOnInit-håndteringen.
public ngAfterViewInit(): void
{
this.map.invalidateSize();
this.__initMapHandlers();
}
Bemærk, at håndteringen af opdatering af kortstørrelse genberegner klassens currentWidth- og currentHeight-variabler, som er bundet til de DIV-stilarter, der styrer bredden og højden af kort-DIV’en. Leaflet bruger disse bredde/højde-informationer til at flise og gengive kortet på ny.
Da kortets bredde/højde-definition og ændring af kortets størrelse ved vinduesændringer håndteres internt i kortkomponenten, tilføjes en simpel window-resize-handler.
@HostListener('window:resize', )
protected __onResize(event: any): void
{
this.__updateMapSize();
this.map.invalidateSize();
}
Dette kunne også håndteres af en ekstern tjeneste, der køres uden for Angular og debounceres. Hvis der er tilstrækkelig interesse, vil jeg levere en tutorial, der illustrerer teknikken.
Map-kontroller
Leaflet tillader brugerdefinerede kontroller, men du kan også bruge Angular-komponenter som map-kontroller. Opret Angular-komponenten, og brug CSS til positionering. Jeg har brugt dette i flere projekter til meget tilpassede zoomkontroller. Se mappen /src/app/maps/map-control for et skelet, der kan bruges som udgangspunkt.
Referer til /src/app/maps/maps/map.component.scss for relevant styling.
Hold øje med z-index
Hvis flisning eller et andet aspekt af kortvisningen ikke ser ud til at fungere, kan det være et problem med z-index. Tidligere har jeg været nødt til at lave en ny kortlægning af z-indeks blot ved at skifte fliseudbyder eller endda ved at migrere fra Angular 7 til Angular 8!
Kortmarkører
Markører med kendte koordinater kan tilføjes til et kort. Definer koordinaterne, og opret derefter et Leaflet-ikon for hver enkelt. Marker-koordinater for denne vejledning defineres i hoved-app-komponenten,
/src/app/app/app.component.ts
public markers: {lat: number, long: number}; // Map markers (relevance depends on map center)
constructor()
{
// some map markers
this.markers = ;
}
og videregives som et Angular Input til Map-komponenten,
/src/app/app.component.html
<app-map ="markers"></app-map>
Markører gengives i kortkomponenten,
/src/app/maps/map/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);
}
}
}
}
Interaktivitet
Der er tilføjet et par handlere til grundlæggende kortinteraktivitet for dig, nemlig museklik og musebevægelse. Sidstnævnte opdaterer den aktuelle kortposition (lat/long) baseret på musens position.
/src/app/maps/map/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}`;
}
Strengen, der afspejler den aktuelle position, vises i kortkomponentens skabelon,
/src/app/maps/map.component.html
<div class="mouse-coords">
<span></span>
<span ="mcText"></span>
</div>
Resultatet
Opbyg programmet, og du bør observere noget, der ligner dette skærmbillede,
Bevæg musen over kortet for at observere opdateringer af musens position i kortkoordinater.
Jeg håber, at dette får dig i gang med at arbejde med Angular og Leaflet. Disse to programmer fungerer meget godt sammen, og der er et næsten ubegrænset potentiale for meget interaktive, engagerende kort.
Godt held og lykke med dine Angular-indsatser!
For mere Angular-godhed skal du huske at tjekke den seneste episode af The Angular Show-podcast.
ng-conf: The Musical er en todages konference fra ng-conf-folkene, der kommer den 22. april & 23. april 2021. Tjek det ud på ng-conf.org