Skip to content

WebNinjaDeveloper.com

Programming Tutorials




Menu
  • Home
  • Youtube Channel
  • Official Blog
  • Nearby Places Finder
  • Direction Route Finder
  • Distance & Time Calculator
Menu

Angular 14 Google Places Autocomplete API Example to Search & Place Markers on Maps in TypeScript

Posted on November 5, 2022

 

 

Welcome folks today in this blog post we will be displaying the google maps inside the browser and also we will be having the input field to search for google places and locations and also we will be placing markers inside the map in angular 14 using google places autocomplete api in typescript. All the full source code of the application is shown below.

 

 

Get Started

 

 

In order to get started you need to initialize a new angular project using the below command as shown below

 

 

ng new sampleapp

 

 

cd sampleapp

 

 

And now you need to install the below libraries as shown below

 

 

npm i @turf/turf

 

 

And now guys you need to first of all render the google map inside the angular app. For this you need to create a google cloud console account and enable the google places autocomplete api inside the console. And then you need to create a google api key and copy to clipboard as shown below

 

 

 

 

 

 

 

Directory Structure of Angular Project

 

 

Now we will be seeing the different files and folders present inside the angular project as shown below

 

 

 

 

Now first of all we will be defining the app.module.ts file and copy paste the below code

 

 

app.module.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
 
import { ApiService } from './api.service';
import { AppComponent } from './app.component';
 
@NgModule({
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [
    ApiService,
  ],
  declarations: [
    AppComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule {}

 

 

As you can see we are importing the formsModule into the imports array because we will be using the input field to get the user location which will be entered by the user. And also inside the providers array we are importing the API service where we will be writing the code which will communicate with google places autocomplete api to fetch the location and display it inside the google maps.

 

 

Writing the Google Autocomplete Service

 

 

Now we will be writing the google maps and autocomplete locations api service code. Just create the file called api.service.ts and copy paste the following code

 

`

api.service.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { Injectable } from '@angular/core';
 
const GOOGLE_MAPS_API_KEY = 'AIzaSyCaKbVhcX_22R_pRKDYuNA7vox-PtGaDkI';
 
export type Maps = typeof google.maps;
 
@Injectable()
export class ApiService {
 
  public readonly api = this.load();
 
  private load(): Promise<Maps> {
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.async = true;
    script.defer = true;
    // tslint:disable-next-line:no-bitwise
    const callbackName = `GooglePlaces_cb_` + ((Math.random() * 1e9) >>> 0);
    script.src = this.getScriptSrc(callbackName);
 
    interface MyWindow { [name: string]: Function; };
    const myWindow: MyWindow = window as any;
 
    const promise = new Promise((resolve, reject) => {
      myWindow[callbackName] = resolve;
      script.onerror = reject;
    });
    document.body.appendChild(script);
    return promise.then(() => google.maps);
  }
 
  private getScriptSrc(callback: string): string {
    interface QueryParams { [key: string]: string; };
    const query: QueryParams = {
      v: '3',
      callback,
      key: GOOGLE_MAPS_API_KEY,
      libraries: 'places',
    };
    const params = Object.keys(query).map(key => `${key}=${query[key]}`).join('&');
    return `//maps.googleapis.com/maps/api/js?${params}&language=fr`;
  }
 
}

 

 

As you can see we are first of all initializing the api_key of the google places autocomplete api and stored it inside the constant variable. Replace it with your own api key. And then we are communicating with the api and extracting the locations details such as city and country. And simply we are returning this data from the service.

 

 

Making the HTML5 Template in Angular

 

 

Now we will be making the app.component.html file and copy paste the following code. This will contain the actual data which will be return from the service

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<h3>Current definition</h3>
 
<dl>
  <dt>name</dt>
  <dd>
    use the <code>name</code>, if different from the computed
    <code>cityName</code>
  </dd>
  <dt>cityName</dt>
  <dd>
    <code>locality.long_name</code> if exists, otherwise
    <code>administrative_area_level_3.short_name</code>
  </dd>
  <dt>stateCode</dt>
  <dd>
    <code>administrative_area_level_1.short_name</code> if different from
    <code>administrative_area_level_1.long_name</code>, otherwise not set
  </dd>
  <dt>countryName</dt>
  <dd>
    <code>country.long_name</code>
  </dd>
  <dt>countryCode</dt>
  <dd>
    <code>country.short_name</code>
  </dd>
</dl>
 
<h3>Playground</h3>
 
<div>
  <input
    placeholder="search for location"
    autocorrect="off"
    autocapitalize="off"
    spellcheck="off"
    type="text"
    class="form-control"
    #search
  />
</div>
 
<div class="map" #map></div>
 
<div *ngFor="let entry of entries">
  <table *ngIf="entry.place" [style.borderColor]="entry.color">
    <thead>
      <tr>
        <th>
          <button (click)="remove(entry)">remove</button>
        </th>
        <th>short_name</th>
        <th>types</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td colspan="2">{{ entry.place.name }}</td>
        <td>name</td>
      </tr>
      <tr *ngFor="let component of entry.place.address_components">
        <td>{{ component.long_name }}</td>
        <td>{{ component.short_name }}</td>
        <td>{{ component.types }}</td>
      </tr>
      <tr>
        <th colspan="2">⇒ Parsed as</th>
        <th>field</th>
      </tr>
      <tr *ngFor="let locationField of locationFields">
        <td colspan="2">{{ entry.location[locationField] }}</td>
        <td>{{ locationField }}</td>
      </tr>
      <!--tr>
        <td colspan="3">
          <pre>{{ entry.place | json }}</pre>
        </td>
      </tr-->
    </tbody>
  </table>
</div>

 

 

As you can see we have the input field inside the html where we will be taking the user input to get the location and then we have the button to submit the form and then we have the area or section where we will be displaying the map. And we have attached the #map id to the map. And then we are displaying the location details inside the table.

 

 

 

 

Now we need to write the source code inside the app.component.ts file and copy paste the following code. Here we will be defining all the methods and importing the service also. And when the user submits the form by entering the location details then also we will be handling the form in this file as shown below. And also we need to define the geolib.ts file and copy paste the following code

 

geolib.ts

 

 

TypeScript
1
2
3
4
5
6
7
import * as x from 'geolib';
 
// @types/geolib is written in a way that `import * as geolib from 'geolib';` does not work
type geolibType = typeof geolib;
const theGeolib = (x as any).default as geolibType;
 
export { theGeolib as geolib };

 

 

 

app.component.ts

 

 

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
import {
  Component,
  Inject,
  ElementRef,
  ViewChild,
  NgZone,
} from '@angular/core';
import * as turf from '@turf/turf';
 
import { ApiService, Maps } from './api.service';
import { geolib } from './geolib';
 
const colors = [
  'red',
  'blue',
  'green',
  'yellow',
  'brown',
  'BurlyWood',
  'Cyan',
  'DarkGreen',
  'DarkOrchid',
  'DarkOliveGreen',
  'Fuchsia',
  'GoldenRod',
  'Indigo',
  'LightCoral',
  'MediumSlateBlue',
];
let colorIndex = 0;
 
const place = null as google.maps.places.PlaceResult;
type Components = typeof place.address_components;
 
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  @ViewChild('search')
  public searchElementRef: ElementRef;
 
  @ViewChild('map')
  public mapElementRef: ElementRef;
 
  public entries = [];
 
  public place: google.maps.places.PlaceResult;
 
  public locationFields = [
    'name',
    'cityName',
    'stateCode',
    'countryName',
    'countryCode',
  ];
 
  private map: google.maps.Map;
 
  constructor(apiService: ApiService, private ngZone: NgZone) {
    apiService.api.then((maps) => {
      this.initAutocomplete(maps);
      this.initMap(maps);
    });
  }
 
  initAutocomplete(maps: Maps) {
    let autocomplete = new maps.places.Autocomplete(
      this.searchElementRef.nativeElement
    );
    autocomplete.addListener('place_changed', () => {
      this.ngZone.run(() => {
        this.onPlaceChange(autocomplete.getPlace());
      });
    });
  }
 
  initMap(maps: Maps) {
    this.map = new maps.Map(this.mapElementRef.nativeElement, {
      zoom: 7,
    });
    this.map.addListener('click', (event) => {
      const ellipsePoints = toEllipse(this.entries[0].location.bounds);
      var line = turf.helpers.lineString(
        ellipsePoints.map((p) => [p.longitude, p.latitude])
      );
 
      const pointLatLng = event.latLng as google.maps.LatLng;
      var point = turf.helpers.point([pointLatLng.lng(), pointLatLng.lat()]);
      //point = turf.helpers.point([this.entries[0].location.coordinates.longitude, this.entries[0].location.coordinates.latitude]);
      const isInside = geolib.isPointInside(
        { latitude: pointLatLng.lat(), longitude: pointLatLng.lng() },
        ellipsePoints
      );
      const distance = isInside ? 0 : turf.pointToLineDistance(point, line);
      console.log('distance', distance * 1000);
    });
  }
 
  onPlaceChange(place: google.maps.places.PlaceResult) {
    this.map.setCenter(place.geometry.location);
 
    const color = colors[colorIndex++ % colors.length];
    const pin = this.pin(color);
 
    const marker = new google.maps.Marker({
      position: place.geometry.location,
      animation: google.maps.Animation.DROP,
      map: this.map,
      icon: this.pin(color),
    });
 
    const rectangle = new google.maps.Rectangle({
      strokeColor: color,
      strokeOpacity: 0.8,
      strokeWeight: 2,
      fillColor: color,
      fillOpacity: 0.35,
      map: this.map,
      bounds: place.geometry.viewport,
    });
 
    const expandedRectangle = new google.maps.Rectangle({
      strokeColor: color,
      strokeOpacity: 0.8,
      strokeWeight: 0.5,
      fillColor: color,
      fillOpacity: 0.2,
      map: this.map,
      bounds: expandBounds(place.geometry.viewport.toJSON(), 5000),
    });
 
    const location = this.locationFromPlace(place);
 
    const ellipse = new google.maps.Polygon({
      paths: toEllipse(location.bounds).map(
        ({ latitude, longitude }) => new google.maps.LatLng(latitude, longitude)
      ),
      strokeColor: color,
      strokeOpacity: 1,
      strokeWeight: 1,
      fillColor: color,
      fillOpacity: 0.3,
    });
    ellipse.setMap(this.map);
 
    this.entries.unshift({
      place,
      marker,
      rectangle,
      expandedRectangle,
      ellipse,
      color,
      location,
    });
  }
 
  remove(entry) {
    entry.marker.setMap(null);
    entry.rectangle.setMap(null);
    entry.expandedRectangle.setMap(null);
    entry.ellipse.setMap(null);
    this.entries = this.entries.filter((e) => e !== entry);
  }
 
  pin(color) {
    return {
      path: 'M 0,0 C -2,-20 -10,-22 -10,-30 A 10,10 0 1,1 10,-30 C 10,-22 2,-20 0,0 z M -2,-30 a 2,2 0 1,1 4,0 2,2 0 1,1 -4,0',
      fillColor: color,
      fillOpacity: 1,
      strokeColor: '#000',
      strokeWeight: 2,
      scale: 1,
    };
  }
 
  public locationFromPlace(place: google.maps.places.PlaceResult) {
    const components = place.address_components;
    if (components === undefined) {
      return null;
    }
 
    const areaLevel3 = getShort(components, 'administrative_area_level_3');
    const locality = getLong(components, 'locality');
 
    const cityName = locality || areaLevel3;
    const countryName = getLong(components, 'country');
    const countryCode = getShort(components, 'country');
    const stateCode = getShort(components, 'administrative_area_level_1');
    const name = place.name !== cityName ? place.name : null;
 
    const coordinates = {
      latitude: place.geometry.location.lat(),
      longitude: place.geometry.location.lng(),
    };
 
    const bounds = place.geometry.viewport.toJSON();
 
    // placeId is in place.place_id, if needed
    return {
      name,
      cityName,
      countryName,
      countryCode,
      stateCode,
      bounds,
      coordinates,
    };
  }
}
 
function getComponent(components: Components, name: string) {
  return components.filter((component) => component.types[0] === name)[0];
}
 
function getLong(components: Components, name: string) {
  const component = getComponent(components, name);
  return component && component.long_name;
}
 
function getShort(components: Components, name: string) {
  const component = getComponent(components, name);
  return component && component.short_name;
}
 
function toEllipse({ north, south, east, west }: cosmos.LatLngBoundsLiteral) {
  const latitude = (north + south) / 2;
  const longitude = (east + west) / 2;
  const r1 =
    geolib.getDistance(
      { latitude: north, longitude },
      { latitude: south, longitude }
    ) / 2;
  const r2 =
    geolib.getDistance(
      { latitude, longitude: west },
      { latitude, longitude: east }
    ) / 2;
 
  const center = { latitude, longitude };
  const latitudeConv =
    geolib.getDistance(center, { latitude: latitude + 0.1, longitude }) * 10;
  const longitudeCong =
    geolib.getDistance(center, { latitude, longitude: longitude + 0.1 }) * 10;
 
  const points: cosmos.Coordinates[] = [];
  const FULL = Math.PI * 2;
  for (let i = 0; i <= FULL + 0.0001; i += FULL / 8) {
    points.push({
      latitude: latitude + (r1 * Math.cos(i)) / latitudeConv,
      longitude: longitude + (r2 * Math.sin(i)) / longitudeCong,
    });
  }
  return points;
}
 
function expandBounds(bounds: cosmos.LatLngBoundsLiteral, meters: number) {
  const SQRT_2 = 1.4142135623730951;
  const { longitude: west, latitude: north } = geolib.computeDestinationPoint(
    {
      latitude: bounds.north,
      longitude: bounds.west,
    },
    SQRT_2 * meters,
    315
  );
  const { longitude: east, latitude: south } = geolib.computeDestinationPoint(
    {
      latitude: bounds.south,
      longitude: bounds.east,
    },
    SQRT_2 * meters,
    135
  );
  return { west, north, east, south };
}
 
namespace cosmos {
  export interface Coordinates {
    /**
     * Coordinates latitude.
     * @type {number}
     */
    latitude: number;
    /**
     * Coordinates longitude.
     * @type {number}
     */
    longitude: number;
  }
  export interface LatLngBoundsLiteral {
    /**
     * LatLngBoundsLiteral east.
     * @type {number}
     */
    east: number;
    /**
     * LatLngBoundsLiteral north.
     * @type {number}
     */
    north: number;
    /**
     * LatLngBoundsLiteral south.
     * @type {number}
     */
    south: number;
    /**
     * LatLngBoundsLiteral west.
     * @type {number}
     */
    west: number;
  }
}

 

 

And now if you run the angular app by running the below command as shown below

 

 

ng serve

 

 

 

 

 

As you can see we typed the live location and then it inserted the red marker inside the map and also displaying the location details inside the table as shown above.

 

 

Recent Posts

  • Android Java Project to Crop,Scale & Rotate Images Selected From Gallery and Save it inside SD Card
  • Android Kotlin Project to Load Image From URL into ImageView Widget
  • Android Java Project to Make HTTP Call to JSONPlaceholder API and Display Data in RecyclerView Using GSON & Volley Library
  • Android Java Project to Download Youtube Video Thumbnail From URL & Save it inside SD Card
  • Android Java Project to Embed Google Maps & Add Markers Using Maps SDK
  • Angular
  • Bunjs
  • C#
  • Deno
  • django
  • Electronjs
  • java
  • javascript
  • Koajs
  • kotlin
  • Laravel
  • meteorjs
  • Nestjs
  • Nextjs
  • Nodejs
  • PHP
  • Python
  • React
  • ReactNative
  • Svelte
  • Tutorials
  • Vuejs




©2023 WebNinjaDeveloper.com | Design: Newspaperly WordPress Theme