Skip to main content

How to use Google Maps in Angular

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

The AGM library

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.

Display a Selected Latitude and Longitude 🌍

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
}

Map Type 🌍

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;
}

Zooming 🔎

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:

  • 1: World
  • 5: Landmass/continent
  • 10: City
  • 15: Streets
  • 20: Buildings

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.

Placing a marker 📌

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).

Adding a 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"></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.

Selecting a 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)"></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>;
}

Dragging 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.

Labelling a 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.

Displaying our current position 🎌🌍

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.

Setting direction

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 
        };
    }
    ...
}
...

Calculating distance between points

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.

Rendering info window

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.

Drawing a circle on the map

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>
<!-- ... -->

Drawing a rectangle on the 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>
<!-- ... -->

Conclusion

We pretty much exhausted Google Maps on Angular. There are still many more on this. Many inputs and outputs in the agm-circleagm-rectangleagm-infoagm-markeragm-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.

Comments

Popular posts from this blog

4 Ways to Communicate Across Browser Tabs in Realtime

1. Local Storage Events You might have already used LocalStorage, which is accessible across Tabs within the same application origin. But do you know that it also supports events? You can use this feature to communicate across Browser Tabs, where other Tabs will receive the event once the storage is updated. For example, let’s say in one Tab, we execute the following JavaScript code. window.localStorage.setItem("loggedIn", "true"); The other Tabs which listen to the event will receive it, as shown below. window.addEventListener('storage', (event) => { if (event.storageArea != localStorage) return; if (event.key === 'loggedIn') { // Do something with event.newValue } }); 2. Broadcast Channel API The Broadcast Channel API allows communication between Tabs, Windows, Frames, Iframes, and  Web Workers . One Tab can create and post to a channel as follows. const channel = new BroadcastChannel('app-data'); channel.postMessage(data); And oth...

Certbot SSL configuration in ubuntu

  Introduction Let’s Encrypt is a Certificate Authority (CA) that provides an easy way to obtain and install free  TLS/SSL certificates , thereby enabling encrypted HTTPS on web servers. It simplifies the process by providing a software client, Certbot, that attempts to automate most (if not all) of the required steps. Currently, the entire process of obtaining and installing a certificate is fully automated on both Apache and Nginx. In this tutorial, you will use Certbot to obtain a free SSL certificate for Apache on Ubuntu 18.04 and set up your certificate to renew automatically. This tutorial will use a separate Apache virtual host file instead of the default configuration file.  We recommend  creating new Apache virtual host files for each domain because it helps to avoid common mistakes and maintains the default files as a fallback configuration. Prerequisites To follow this tutorial, you will need: One Ubuntu 18.04 server set up by following this  initial ...

Working with Node.js streams

  Introduction Streams are one of the major features that most Node.js applications rely on, especially when handling HTTP requests, reading/writing files, and making socket communications. Streams are very predictable since we can always expect data, error, and end events when using streams. This article will teach Node developers how to use streams to efficiently handle large amounts of data. This is a typical real-world challenge faced by Node developers when they have to deal with a large data source, and it may not be feasible to process this data all at once. This article will cover the following topics: Types of streams When to adopt Node.js streams Batching Composing streams in Node.js Transforming data with transform streams Piping streams Error handling Node.js streams Types of streams The following are four main types of streams in Node.js: Readable streams: The readable stream is responsible for reading data from a source file Writable streams: The writable stream is re...