Dit artikel is gericht op beginnende Angular ontwikkelaars die Leaflet willen integreren in hun applicaties. De integratie van afhankelijkheden van derden in Angular kan variëren van triviaal tot problematisch. Terwijl Leaflet meer naar de triviale kant neigt, kunnen kaarten die zich vloeiend aanpassen aan de grootte van de browser enkele installatieproblemen opleveren met betrekking tot de Angular-levenscyclus.
Voordat je begint met de deconstructie, wijs je vriendelijke, naburige browser naar de volgende Github.
Nu kun je het project volgen of forken en het gebruiken als basis voor je eigen applicaties.
Achtergrond
Deze tutorial veronderstelt een kleine hoeveelheid voorafgaande Leaflet kennis, maar niet meer dan wordt uitgedrukt in de Leaflet Quickstart Guide.
ArcGis wordt gebruikt voor tiling in de applicatie die in dit artikel wordt behandeld (het esri-leaflet pakket wordt geïnstalleerd vanuit npm).
Angular Setup
Er hoeft maar weinig te worden gedaan na de alomtegenwoordige npm installatie. Noodzakelijke Leaflet stijlen worden beschikbaar gemaakt via het angular.json bestand,
"styles": ,
Ik heb gemengde resultaten gehad met @types/leaflet, dus deze applicatie maakt geen gebruik van typings omwille van de eenvoud. Leaflet en ESRI tiles worden geïmporteerd zoals hieronder getoond,
import * as esri from 'esri-leaflet';
import * as L from 'leaflet';
Een Leaflet Map, bijvoorbeeld, wordt tijdelijk getypt als elke; voel je vrij om typeringen te verstevigen als een oefening nadat je de tutorial hebt gedeconstrueerd.
Coordinaten van het centrum van een kaart zijn belangrijk voor kaart-initialisatie. Veel demo’s hardcoden deze informatie. Als alternatief illustreert deze tutorial hoe je kaart-centrum coördinaten injecteert met behulp van Angular’s Injection Tokens.
Een coördinaat is een tweedimensionaal punt op een kaart, weergegeven door breedtegraad en lengtegraad. Een InjectionToken voor een coördinaat kan worden gemaakt zoals geïllustreerd in het bestand /src/app/tokens.ts,
import { InjectionToken } from '@angular/core';
export const INIT_COORDS = new InjectionToken<{lat: number, long: number}>('INIT_COORDS');
INIT_COORDS wordt gebruikt in het bestand /src/app/app.module.ts bestand om de feitelijke (lat/long) bepaling (of definitie) uit te voeren,
providers: ,
Dit verplaatst de definitie van het kaartcentrum naar het hoogste niveau in de Angular applicatie (waar het gemakkelijk te zien en bijna zelf-documenterend is).
Om dit coördinaat in de applicatie te injecteren, importeer INIT_COORDS uit het tokens bestand en injecteer vanuit een klasse constructor. Dit wordt geïllustreerd in het bestand /src/app/map.component.ts (de primaire kaartcomponent in deze tutorial),
constructor( @Inject(INIT_COORDS) protected _initCoords: {lat: number, long: number} )
Map Component
De toepassing die in dit artikel wordt behandeld, toont een kaart met een vaste hoogte en een breedte die zich uitstrekt over het hele browservenster. De kaart wordt opnieuw getekend als het venster wordt verkleind. Sommige markeringen worden ook weergegeven bovenop de kaarttegels.
De kaart is geïntegreerd in de toepassing via het sjabloon van de hoofdapp-component, zoals hieronder getoond
/src/app/app.component.html
<app-map ="markers"></app-map>
en de broncode van de kaartcomponent is te vinden in /src/app/maps/map.component.ts .
De kaart wordt gerenderd in een container (DIV) die momenteel in het sjabloon van de kaartcomponent staat,
/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>
Als de breedte en hoogte van de bevattende DIV vast zijn, dan kunnen kaarttegels worden gerenderd op een vroeg punt in de levenscyclus van de component. Twee benaderingen zijn mogelijk voor het doorgeven van variabele breedte en hoogte, outside-in en inside-out. De outside-in methode definieert Angular Inputs voor breedte en hoogte. De parent component is verantwoordelijk voor het doorgeven van breedte en hoogte aan de map component. De inside-out benadering definieert breedte en hoogte intern bij de kaartcomponent en ‘geeft’ die informatie door aan zijn sjabloon. De meeste toepassingen gebruiken de outside-in benadering. In deze handleiding wordt ter illustratie de inside-out methode gebruikt. Pas de code aan om het doorgeven van breedte en hoogte te veranderen als oefening.
Variabele breedte en hoogte introduceren enkele subtiele problemen met betrekking tot kaartrendering. Het is verleidelijk om alles met betrekking tot Leaflet setup uit te voeren in de on-init handler, en dit werkt meestal met vaste breedte en hoogte. Aangezien de kaartbreedte varieert met de grootte van de browser in deze tutorial, is het beter om het kaart-initialisatieproces te scheiden tussen de on-init en after-view-init lifecycle handlers. Dit wordt geïllustreerd in de volgende code segmenten uit /src/app/maps/map.component.ts .
Eerst wordt de identificatie van een containing DIV voor kaart initialisatie uitbesteed aan een ViewChild,
@ViewChild('primaryMap', {static: true}) protected mapDivRef: ElementRef;
protected mapDiv: HTMLDivElement;
Het grootste deel van de kaart setup wordt uitgevoerd in 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();
}
Het ongeldig maken van de kaart grootte wordt uitgesteld tot ngAfterViewInit, wanneer de volledige breedte en hoogte die nodig zijn om de kaart tegels te laten werken beschikbaar is. De kaart variabele verwijst naar de Leaflet kaart gemaakt in de ngOnInit handler.
public ngAfterViewInit(): void
{
this.map.invalidateSize();
this.__initMapHandlers();
}
Merk op dat de update map size handler herberekent de klasse currentWidth en currentHeight variabelen, die zijn gebonden aan de DIV stijlen die de breedte en hoogte van de kaart DIV regelen. Leaflet gebruikt die breedte/hoogte informatie om de kaart opnieuw te tekenen en opnieuw te renderen.
Omdat de kaart breedte/hoogte definitie en het resizen van de kaart met venster veranderingen intern worden afgehandeld in de kaart component, is een eenvoudige window-resize handler toegevoegd.
@HostListener('window:resize', )
protected __onResize(event: any): void
{
this.__updateMapSize();
this.map.invalidateSize();
}
Dit zou ook kunnen worden afgehandeld door een service van buitenaf, uitgevoerd buiten Angular, en debounced. Als er voldoende belangstelling is, zal ik een tutorial verzorgen die de techniek illustreert.
Map Controls
Leaflet staat wel custom controls toe, maar je kunt ook Angular componenten gebruiken als map controls. Maak de Angular component en gebruik CSS voor positionering. Ik heb dit in verschillende projecten gebruikt voor sterk aangepaste zoom controls. Zie de /src/app/maps/map-control folder voor een skelet dat als uitgangspunt kan worden gebruikt.
Refer to /src/app/maps/map.component.scss for relevant styling.
Waarschuw de z-index
Als tiling of een ander aspect van kaartweergave niet lijkt te werken, kan het een probleem zijn met z-index. In het verleden heb ik z-indexen opnieuw moeten instellen door simpelweg van tegelprovider te veranderen of zelfs door van Angular 7 naar Angular 8 te migreren!
Map Markers
Markers met bekende coördinaten kunnen aan een kaart worden toegevoegd. Definieer de coördinaten en maak dan een folder icoon voor elk van hen. Markeer coördinaten voor deze tutorial zijn gedefinieerd in de hoofd app component,
/src/app/app.component.ts
public markers: {lat: number, long: number}; // Map markers (relevance depends on map center)
constructor()
{
// some map markers
this.markers = ;
}
en worden doorgegeven als een Angular Input aan de Map Component,
/src/app/app.component.html
<app-map ="markers"></app-map>
Markers worden gerenderd in de kaartcomponent,
/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);
}
}
}
}
Interactiviteit
Er zijn een paar handlers voor elementaire kaartinteractiviteit toegevoegd, namelijk muis-klik en muis-beweging. De laatste werkt de huidige kaartpositie bij (lat/long) op basis van de muispositie.
/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}`;
}
De string die de huidige positie weergeeft, wordt weergegeven in het sjabloon van de kaartcomponent,
/src/app/maps/map.component.html
<div class="mouse-coords">
<span></span>
<span ="mcText"></span>
</div>
Het resultaat
Bouw de toepassing en u zou iets moeten zien dat lijkt op deze schermafbeelding,