Leaflet supports markers. These are indicators placed on the map that can contain information. This provides a way to highlight landmarks and destinations on a map.
Note: This is Part 2 of a 4-part series on using Angular and Leaflet.
In this tutorial, you will learn how to add markers to your map using a service to manage the marker logic.
To complete this tutorial, you will need:
This tutorial will plot GeoJSON data for the state capitals of the United States of America. It will also include some additional metadata for state names, capital names, and population.
Create a new data
subdirectory under the assets
directory:
- mkdir src/assets/data
Then, save the usa-capitals.geojson
file in this directory.
At this point, you should have a working implementation of Leaflet in an Angular application.
Use your terminal window to navigate to the project directory. Then, run the following command to generate a new service:
- npx @angular/cli generate service marker --skip-tests
This will create a new file: marker.service.ts
.
Next, you will add this new service as a provider in your app.module.ts
. You will also be loading the data from your assets
folder so you will need to include the HttpClientModule
.
Open app.module.ts
in your code editor and make the following changes:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { MarkerService } from './marker.service';
import { AppComponent } from './app.component';
import { MapComponent } from './map/map.component';
@NgModule({
declarations: [
AppComponent,
MapComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [
MarkerService
],
bootstrap: [AppComponent]
})
export class AppModule { }
Your application now supports your new MarkerService
.
Next, open your newly created marker.service.ts
in your code editor and add HttpClient
to the constructor:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class MarkerService {
capitals: string = '/assets/data/usa-capitals.geojson';
constructor(private http: HttpClient) { }
}
Create a new function that will load the GeoJSON data and create the markers. This function will take in a Leaflet map as a parameter.
Modify marker.service.ts
to import Leaflet and declare a makeCapitalMarkers
function:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import * as L from 'leaflet';
@Injectable({
providedIn: 'root'
})
export class MarkerService {
capitals: string = '/assets/data/usa-capitals.geojson';
constructor(private http: HttpClient) { }
makeCapitalMarkers(map: L.map): void { }
}
Using HttpClient
, get the data and subscribe
to the result.
Once you have the data, you will then loop through each feature, construct a marker, and add it to the map.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import * as L from 'leaflet';
@Injectable({
providedIn: 'root'
})
export class MarkerService {
capitals: string = '/assets/data/usa-capitals.geojson';
constructor(private http: HttpClient) {
}
makeCapitalMarkers(map: L.map): void {
this.http.get(this.capitals).subscribe((res: any) => {
for (const c of res.features) {
const lon = c.geometry.coordinates[0];
const lat = c.geometry.coordinates[1];
const marker = L.marker([lat, lon]);
marker.addTo(map);
}
});
}
}
This code handles the logic for loading and adding markers to the map.
Now, you will have to call this method from MapComponent
:
import { Component, AfterViewInit } from '@angular/core';
import * as L from 'leaflet';
import { MarkerService } from '../marker.service';
@Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements AfterViewInit {
private map;
private initMap(): void {
this.map = L.map('map', {
center: [ 39.8282, -98.5795 ],
zoom: 3
});
const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
minZoom: 3,
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
});
tiles.addTo(this.map);
}
constructor(private markerService: MarkerService) { }
ngAfterViewInit(): void {
this.initMap();
this.markerService.makeCapitalMarkers(this.map);
}
}
If you were to run your application at this point, you would encounter two errors in your console:
Outputmarker-icon-2x.png:1 GET http://localhost:4200/marker-icon-2x.png 404 (Not Found)
marker-shadow.png:1 GET http://localhost:4200/marker-shadow.png 404 (Not Found)
You will need to import Leaflet’s assets to your project to reference the marker-icon-2x.png
and marker-shadow.png
image files.
Open the angular.json
file and add the Leaflet images
directory:
{
// ...
"projects": {
"angular-leaflet-example": {
// ...
"architect": {
"build": {
// ...
"options": {
// ...
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*",
"input": "node_modules/leaflet/dist/images/",
"output": "./assets"
}
],
// ..
},
// ...
},
// ...
}
}},
"defaultProject": "angular-leaflet-example"
}
This code will copy Leaflet’s marker images locally.
Then, revisit the map.component.ts
and define the icon:
import { Component, AfterViewInit } from '@angular/core';
import * as L from 'leaflet';
import { MarkerService } from '../marker.service';
const iconRetinaUrl = 'assets/marker-icon-2x.png';
const iconUrl = 'assets/marker-icon.png';
const shadowUrl = 'assets/marker-shadow.png';
const iconDefault = L.icon({
iconRetinaUrl,
iconUrl,
shadowUrl,
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
tooltipAnchor: [16, -28],
shadowSize: [41, 41]
});
L.Marker.prototype.options.icon = iconDefault;
@Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements AfterViewInit {
private map;
constructor(private markerService: MarkerService) { }
private initMap(): void {
this.map = L.map('map', {
center: [ 39.8282, -98.5795 ],
zoom: 3
});
const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
minZoom: 3,
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
});
tiles.addTo(this.map);
}
ngAfterViewInit(): void {
this.initMap();
this.markerService.makeCapitalMarkers(this.map);
}
}
Save your changes. Then, stop your application and relaunch it. Open the application in your web browser (localhost:4200
) and observe the markers for the state capitals:
At this point, you have a map that supports default markers.
In this next step, you will change the markers from icons to circles. Then scale the size of the circles to reflect the population of the state capitol.
Open MarkerService
and create a makeCapitalCircleMarkers()
function. It will be very similar to the makrCapitalMarkers()
function. Instad of Leaflet’s marker
method, you will use the circleMarker
method:
makeCapitalCircleMarkers(map: L.map): void {
this.http.get(this.capitals).subscribe((res: any) => {
for (const c of res.features) {
const lon = c.geometry.coordinates[0];
const lat = c.geometry.coordinates[1];
const circle = L.circleMarker([lat, lon]);
circle.addTo(map);
}
});
}
Then, call this function in MapComponent
:
ngAfterViewInit(): void {
this.initMap();
// this.markerService.makeCapitalMarkers(this.map);
this.markerService.makeCapitalCircleMarkers(this.map);
}
Save these changes and open the application in your web browser (localhost:4200
):
The icons have now been replaced with circles.
circleMarker
accepts a third optional parameter. This object can contain a radius
property. In your MarkerService
, modify the makeCapitalCircleMarkers
function to use a radius of 20
:
const circle = L.circleMarker([lat, lon], { radius: 20 }).addTo(map);
This code sizes all radii to be the same value (20
).
Next, you will change the radius to reflect the population of the state capital:
static scaledRadius(val: number, maxVal: number): number {
return 20 * (val / maxVal);
}
This function takes in a value (population), a max value (maximum population), and returns a radius in the range [0 - 20].
You will use the spread-operator and map
to find the capital with the largest population:
const maxPop = Math.max(...res.features.map(x => x.properties.population), 0);
From the GeoJSON data, the largest population will be: “Phoenix, Arizona” (1626078
).
Finally, you will put it all together by using ScaledRadius
as the radius function.
Open MarkerService
in your code editor and make the following changes:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import * as L from 'leaflet';
@Injectable({
providedIn: 'root'
})
export class MarkerService {
capitals: string = '/assets/data/usa-capitals.geojson';
constructor(private http: HttpClient) { }
static scaledRadius(val: number, maxVal: number): number {
return 20 * (val / maxVal);
}
makeCapitalMarkers(map: L.map): void {
this.http.get(this.capitals).subscribe((res: any) => {
for (const c of res.features) {
const lon = c.geometry.coordinates[0];
const lat = c.geometry.coordinates[1];
const marker = L.marker([lat, lon]);
marker.addTo(map);
}
});
}
makeCapitalCircleMarkers(map: L.map): void {
this.http.get(this.capitals).subscribe((res: any) => {
const maxPop = Math.max(...res.features.map(x => x.properties.population), 0);
for (const c of res.features) {
const lon = c.geometry.coordinates[0];
const lat = c.geometry.coordinates[1];
const circle = L.circleMarker([lat, lon], {
radius: MarkerService.scaledRadius(c.properties.population, maxPop)
});
circle.addTo(map);
}
});
}
}
Save your changes. Then, stop your application and relaunch it. Open the application in your web browser (localhost:4200
) and observe the new scaled circle markers for state capitals:
You now have a map that supports markers.
In this post, you created a marker service that loads data and constructs markers. You learned how to create two types of markers: L.marker
and L.circleMarker
. Finally, you learned how to define the size of each circle marker by passing a function for the radius.
Continue to Part 3 of this series on using Angular and Leaflet.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
L.marker([lat, lon]).addTo(map);
should put the latitude first only longtitude
Where can I view the angular.json file containing the geojson?