Google maps meet Angular. Having such a strong developer community and being used by thousands of developers around the world made Angular ideal for bringing most tools to the framework. bringing Google maps to Angular is one of them, it solved many problems for the Angular users. Manually adding Google maps to Angular is very difficult, so some people took it up upon themselves to make adding GMaps to your apps as easy as importing a module.
In this post, we will be looking at integrating Google Maps in your Angular application
This is the main library we will be using. The main library in the AGM is the @agm/core
library. @agm/core
contains solutions for the Google Maps JavaScript Core API.
To use this, we will first scaffold an Angular project:
ng new ng-maps
The above command will create a new Angular project.
Now, we install the @agm/core
library:
npm i @agm/core
We need a component where we can display the map, to do that we scaffold a component:
g c map
This command will create map.component.ts
in src/app/map/
folder.
We need to import the AgmModule
in our AppModule
:
...
import { AgmCoreModule } from '@agm/core';
@NgModule({
declarations: [
AppComponent,
MapComponent
],
imports: [
BrowserModule,
AgmCoreModule.forRoot({
apiKey: environment.keys.gmap,
libraries: ["places", "geometry"]
/* apiKey is required, unless you are a premium customer, in which case you can use clientId */
})
],
providers: [
...
],
bootstrap: [AppComponent]
})
export class AppModule { }
See, the AgmModule is imported and placed in the imports section. We need a GMaps key, to get it go to console.developers.google.com and follow the instructions.
After successfully obtaining the key, you can paste it here
...
AgmCoreModule.forRoot({
apiKey: "",
libraries: ["places", "geometry"]
/* apiKey is required, unless you are a premium customer, in which case you can use clientId */
})
...
I added mine to the environment.ts
file. For simplicity just add it to the apiKey
property above.
We are done now. To display the map, we open up our map.component.ts
and paste this:
<!-- map.component.html -->
<agm-map></agm-map>
// map.component.ts
@Component({
...
})
export class MapComponent {}
This will render the Google Map on our MapComponent
.
Run ng serve
, we will see it on our browser.
The agm-map
selector tag is used to render the Google Maps on our components. It has inputs and outputs which we will see in the below sections.
The above code does nothing just displays a Map. Let’s make it display a selected location from a provided latitude and longitude position.
Remember, latitude and longitude is what is used to locate a position on a map, It is triangulated in the x-y plane. The latitude is in the x-plane while longitude is in the y-plane.
So when given a latitude and longitude, we trace from the x-axis and the y-axis, till they meet. The intersection is the location/position of the object or place.
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude"></agm-map>
// map.component.ts
@Component({
...
})
export class MapComponent implements OnInit{
location: Location
ngOnInit() {
this.location = {
latitude: -28.68352,
longitude: -147.20785
}
}
}
interface Location {
latitude: number;
longitude: number
}
Google Maps have options on how we want the map view displayed.
street view : This will show the streets on an eye-level, we can navigate/walk the streets with the arrow buttons
satellite: Displays the amp as seen by a satellite.
normal: The default map showed us by Google.
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType"></agm-map>
// map.component.ts
@Component({
...
})
export class MapComponent implements OnInit{
location: Location
ngOnInit() {
this.location = {
latitude: -28.68352,
longitude: -147.20785,
mapType: "satelite"
}
}
}
interface Location {
latitude: number;
longitude: number;
mapType: ?string;
}
agm-map
has an option to zoom in or out in the map view. We use the [zoom] input to accomplish that.
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom"></agm-map>
// map.component.ts
@Component({
...
})
export class MapComponent implements OnInit{
location: Location
ngOnInit() {
this.location = {
latitude: -28.68352,
longitude: -147.20785,
mapType: "satelite",
zoom: 5
}
}
}
interface Location {
latitude: number;
longitude: number;
mapType: ?string;
zoom: ?number;
}
We set the zoom depth to 5
.
This has zoom levels:
Specifying a zoom level of 0-4
will show the map of the entire Earth. 5-9
will show the map in the continent at a higher resolution more than 0-5
. Zoom level from 11 - 14
will show the cities in the map location at a higher resolution than the previous zoom level. 15-19
will show the streets and 20-~
will show the buildings at an eye level.
So our above example will show the location on the map on the landmass/continent level.
To place an indicator/marker📍 on the map to show the precise location, we will use the agm-marker
. Markers are like pins on the map.
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<agm-marker [latitude]="location.marker.lat" [longitude]="location.marker.lng"></agm-marker>
</agm-map>
// map.component.ts
@Component({
...
})
export class MapComponent implements OnInit{
location: Location
ngOnInit() {
this.location = {
latitude: -28.68352,
longitude: -147.20785,
mapType: "satelite",
zoom: 5,
marker: {
lat: -28.68352,
lng: -147.20785
}
}
}
}
interface Marker {
lat: number;
lng: number;
}
interface Location {
latitude: number;
longitude: number;
mapType: ?string;
zoom: ?number;
marker: Marker;
}
A marker/pin will appear at the precise location of (-28.68352,-147.20785)
.
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<agm-marker *ngFor="let marker of location.markers" [latitude]="marker.lat" [longitude]="marker.lng"></agm-marker>
</agm-map>
// map.component.ts
@Component({
...
})
export class MapComponent implements OnInit{
location: Location
ngOnInit() {
this.location = {
latitude: -28.68352,
longitude: -147.20785,
mapType: "satelite",
zoom: 5,
markers: [
{
lat: -28.68352,
lng: -147.20785
}
]
}
}
addMarker(lat: number, lng: number) {
this.location.markers.push({
lat,
lng
})
}
}
interface Marker {
lat: number;
lng: number;
}
interface Location {
latitude: number;
longitude: number;
mapType: ?string;
zoom: ?number;
markers: Array<Marker>;
}
In this example, when a user clicks the map, it adds a new marker to the map. We hold the markers in an array, the agm-marker iterates over the array and places them on the map with a pin/marker. When we click on the map, a new marker is pushed to the markers
array. A pin/marker appears over the position clicked.
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<agm-marker *ngFor="let marker of location.markers" [latitude]="marker.lat" [longitude]="marker.lng" (markerClick)="selectMarker($event)"></agm-marker>
</agm-map>
<div>
Selected Marker is: Longitude {{selectedMarker.lat}} Latitude {{selectedMarker.lng}}
</div>
// map.component.ts
@Component({
...
})
export class MapComponent implements OnInit{
location: Location
selectedMarker: Marker
ngOnInit() {
this.location = {
latitude: -28.68352,
longitude: -147.20785,
mapType: "satelite",
zoom: 5,
markers: [
{
lat: -28.68352,
lng: -147.20785
}
]
}
}
addMarker(lat: number, lng: number) {
this.location.markers.push({
lat,
lng
})
}
selectMarker(event) {
this.selectedMarker = {
lat: event.latitude,
lng: event.longitude
}
}
}
interface Marker {
lat: number;
lng: number;
}
interface Location {
latitude: number;
longitude: number;
mapType: ?string;
zoom: ?number;
markers: Array<Marker>;
}
We can click, hold and drag a marker around.
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<agm-marker
*ngFor="let marker of location.markers" [latitude]="marker.lat"
[longitude]="marker.lng"
(markerClick)="selectMarker($event)" [markerDraggable]="true"
(dragEnd)="markerDragEnd(coords, $event)"
></agm-marker>
</agm-map>
<div>
Selected Marker is: Longitude {{selectedMarker.lat}} Latitude {{selectedMarker.lng}}
</div>
// map.component.ts
@Component({
...
})
export class MapComponent implements OnInit{
location: Location
selectedMarker: Marker
ngOnInit() {
this.location = {
latitude: -28.68352,
longitude: -147.20785,
mapType: "satelite",
zoom: 5,
markers: [
{
lat: -28.68352,
lng: -147.20785
}
]
}
}
addMarker(lat: number, lng: number) {
this.location.markers.push({
lat,
lng
})
}
selectMarker(event) {
this.selectedMarker = {
lat: event.latitude,
lng: event.longitude
}
}
markerDragEnd(coords: any, $event: MouseEvent) {
this.location.latitude = coords.latitude
this.location.longitude = coodrs.longitude
}
}
interface Marker {
lat: number;
lng: number;
}
interface Location {
latitude: number;
longitude: number;
mapType: ?string;
zoom: ?number;
markers: Array<Marker>;
}
We use the [markerDraggable]="true"
to make the marker draggable. Then, we have the event (dragEnd)="markerDragEnd(coords, $event)"
, this event is fired when the user stops dragging the marker.
We can label a marker, i.e attach a name or description to a marker. It will be a name hovering above the marker, to identify the marker.
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<agm-marker
*ngFor="let marker of location.markers" [latitude]="marker.lat"
[longitude]="marker.lng"
(markerClick)="selectMarker($event)" [markerDraggable]="true"
(dragEnd)="markerDragEnd(coords, $event)"
[label]="marker.label"
></agm-marker>
</agm-map>
<div>
Selected Marker is: Longitude {{selectedMarker.lat}} Latitude {{selectedMarker.lng}}
</div>
// map.component.ts
@Component({
...
})
export class MapComponent implements OnInit{
location: Location
selectedMarker: Marker
ngOnInit() {
this.location = {
latitude: -28.68352,
longitude: -147.20785,
mapType: "satelite",
zoom: 5,
markers: [
{
lat: -28.68352,
lng: -147.20785,
label: "new york"
}
]
}
}
addMarker(lat: number, lng: number) {
this.location.markers.push({
lat,
lng,
label: Date.now().toLocaleString()
})
}
...
}
interface Marker {
lat: number;
lng: number;
label: string;
}
interface Location {
latitude: number;
longitude: number;
mapType: ?string;
zoom: ?number;
markers: Array<Marker>;
}
We used the [label]
input to add label to the marker. We added a label property to the Marker interface, so we changed our code to include the new addition. Our first marker will display new york on top of the marker, others will have Date it was created on top of them.
Now, it would be nice if the names of the markers will be shown on their heads instead of dates they were created. We use geocoding.
We need to install googlemaps
library:
npm i googlemaps
We import it in our MapComponent
and declare a google
var:
import {} from "googlemaps"
declare var google: any;
...
Then, our code:
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<agm-marker
*ngFor="let marker of location.markers" [latitude]="marker.lat"
[longitude]="marker.lng"
(markerClick)="selectMarker($event)" [markerDraggable]="true"
(dragEnd)="markerDragEnd(coords, $event)"
[label]="marker.label"
></agm-marker>
</agm-map>
<div>
Selected Marker is: Longitude {{selectedMarker.lat}} Latitude {{selectedMarker.lng}}
</div>
// map.component.ts
...
@Component({
...
})
export class MapComponent implements OnInit {
location: Location
selectedMarker: Marker
ngOnInit() {
this.location = {
latitude: -28.68352,
longitude: -147.20785,
mapType: "satelite",
zoom: 5,
markers: [
{
lat: -28.68352,
lng: -147.20785,
label: this.getAddress(-28.68352, -147.20785)
}
]
}
}
addMarker(lat: number, lng: number) {
this.location.markers.push({
lat,
lng,
label: this.getAddress(lat, lng)
})
}
getAddress(lat, lng) {
const geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(lat, lng);
const request: google.maps.GeocoderRequest = {
location: latlng
};
geocoder.geocode(request, (results, status) => {
this.ngZone.run(() => {
const address = results[0].formatted_address;
return address
});
});
}
...
}
interface Marker {
lat: number;
lng: number;
label: string;
}
interface Location {
latitude: number;
longitude: number;
mapType: ?string;
zoom: ?number;
markers: Array<Marker>;
}
The getAddress
method uses the latitude and longitude passed to it to get the address of the place. See, we modified the addMarker
method, so it calls the getAddress
method to assign the marker address to the label
property.
We constructed a geocoder
object, then set a GeoCoder
request with the lat
and lng
passed, finally, we called the geocode
method in the geocoder
, and in the function callback we got the address of the location.
Here, let’s make it display our current position:
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<agm-marker
*ngFor="let marker of location.markers" [latitude]="marker.lat"
[longitude]="marker.lng"
(markerClick)="selectMarker($event)" [markerDraggable]="true"
(dragEnd)="markerDragEnd(coords, $event)"
[label]="marker.label"
></agm-marker>
</agm-map>
<div>
Selected Marker is: Longitude {{selectedMarker.lat}} Latitude {{selectedMarker.lng}}
</div>
// map.component.ts
...
@Component({
...
})
export class MapComponent implements OnInit {
location: Location
selectedMarker: Marker
ngOnInit() {
this.setCurrentPosition()
}
addMarker(lat: number, lng: number) {
this.location.markers.push({
lat,
lng,
label: this.getAddress(lat, lng)
})
}
setCurrentPosition() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(position => {
const {latitude, longitude} = position
this.location = {
latitude,
longitude,
mapType: "satelite",
zoom: 14,
markers: [
{
lat: longitude,
lng: latitude,
label: "My current position"
}
]
}
});
} else {
alert("Geolocation is not supported by this browser, please use google chrome.");
}
}
...
}
interface Marker {
lat: number;
lng: number;
label: string;
}
interface Location {
latitude: number;
longitude: number;
mapType: ?string;
zoom: ?number;
markers: ?Array<Marker>;
}
Now, we have set the component to display our current position on the map when loaded. How did we do it? Simple, we used the navigator object. The navigator has a geolocation object which we use to call the getCurrentPosition() function, this function accepts a function callback which it calls with the location object. See our function callback will receive the location in the position
parameter.
We can plot directions on a map. This we do with the help of agm-direction.
First, we install it:
npm i agm-direction
Next, we import the modules:
// app.module.ts
...
import { AgmDirectionModule } from 'agm-direction';
@NgModule({
...
imports: [
...
AgmDirectionModule
]
})
export class AppModule { }
So, now to plot direction from origin Taipei Main Station to destination Taiwan Presidential Office:
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<!-- ... -->
<agm-direction
[origin]="origin"
[destination]="destination"
></agm-direction>
</agm-map>
<!-- ... -->
// map.component.ts
...
@Component({
...
})
export class MapComponent implements OnInit {
...
origin: any;
destination: any;
ngOnInit() {
this.origin = {
lat: 24.799448,
lng: 120.979021
};
this.destination = {
lat: 24.799524,
lng: 120.975017
};
}
...
}
...
We can calculate the distance between points on the map.
To calculate the distance between Taipei Main Station and destination Taiwan Presidential Office, we do this:
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<!-- ... -->
<agm-direction
[origin]="origin"
[destination]="destination"
></agm-direction>
</agm-map>
<div>
Distance: {{distance}}
</div>
<!-- ... -->
// map.component.ts
...
@Component({
...
})
export class MapComponent implements OnInit {
...
origin: any;
destination: any;
distance: Number;
ngOnInit() {
this.origin = {
lat: 24.799448,
lng: 120.979021
};
this.destination = {
lat: 24.799524,
lng: 120.975017
};
this.distance = this.calculatedistance(this.origin, this.destination)
}
// calculate the distances from point1 to point2
calculateDistance(point1, point2) {
const p1 = new google.maps.LatLng(
point1.lat,
point1.lng
);
const p2 = new google.maps.LatLng(
point2.lat,
point2.lng
);
return (
google.maps.geometry.spherical.computeDistanceBetween(p1, p2)/1000
).toFixed(2);
}
...
}
...
We have a method calculateDistance
that computes the distance between two points passed to it. We construct LatLng objects of the two points and pass it to google.maps.geometry.spherical.computeDistanceBetween(...)
. This returns the distance between the two points.
We can place an info window over a marker. This is how we do it:
<!-- map.component.html -->
<agm-map [latitude]="location.latitude" [longitude]="location.longitude" [mapTypeId]="location.mapType" [zoom]="location.zoom" (mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<agm-marker
*ngFor="let marker of location.markers" [latitude]="marker.lat"
[longitude]="marker.lng"
(markerClick)="selectMarker($event)" [markerDraggable]="true"
(dragEnd)="markerDragEnd(coords, $event)"
[label]="marker.label"
>
<agm-info-window [disableAutoPan]="true">{{marker.label}}</agm-info-window>
</agm-marker>
<!-- ... -->
</agm-map>
<!-- ... -->
See it is placed inside the agm-marker.
To draw a circle over a location we use the agm-circle
tag.
It has the following inputs:
“latitude”: The latitude of the location
“longitude”: The longitude of the location
“clickable”: Sets the circle clickable or not
“draggable”: Sets the circle draggable or not
“editable”: Sets the circle editable or not
“fillColor”: The color of the circle
“fillOpacity”: The transparency of the circle
“radius”: The radius of the circle
<!-- map.component.html -->
<agm-map
[latitude]="location.latitude"
[longitude]="location.longitude"
[mapTypeId]="location.mapType"
[zoom]="location.zoom"
(mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<!-- ... -->
<agm-circle
[latitude]="location.latitude"
[longitude]="location.longitude" [clickable]="false"
[draggable]="true"
[editable]="false"
[fillColor]="green"
[fillOpacity]="0"
[radius]="circleRadius">
</agm-circle>
</agm-map>
<!-- ... -->
We use the agm-rectangle
. We must provide the bounds:
“north”: The north bound
“east”: The east bound
“south”: The south bound
“west”: The west bound
<!-- map.component.html -->
<agm-map
[latitude]="location.latitude"
[longitude]="location.longitude"
[mapTypeId]="location.mapType"
[zoom]="location.zoom"
(mapClick)="addMarker($event.coords.lat, $event.coords.lng)">
<!-- ... -->
<agm-rectangle
[north]="north"
[south]="south"
[east]="east"
[west]="west">
</agm-rectangle>
</agm-map>
<!-- ... -->
We pretty much exhausted Google Maps on Angular. There are still many more on this. Many inputs and outputs in the agm-circle
, agm-rectangle
, agm-info
, agm-marker
, agm-direction
and agm-map
. This should serve as a basic guide to Google Maps in Angular.
Write your comments below, if I missed a point or concept, spelling, grammar error, in short anything at all.
Thanks!!!
#angular #google-maps