Create a map with Location Intelligence components

Does it make sense to create a custom map for your website? Sure, a custom map brings a lot of benefits and advantages to your website. For instance, with your own map you are able to show the important locations to customers, highlight areas you want to pay attention to, and visualize data which could be potentially very interesting for both sites.

Geoapify helps you to create a map from the beginning and add Location Intelligence components into it. Read how to add a map to your website.

However, location visualizations are not only what people look for on a map. It’s also important to make the map interactive and “alive”. So the users can interact and “play” with it and make location decisions.

Make your custom map “alive”

But what does make a custom map “alive”? How to make the map interactive? The answer is simple: add components to your custom map, where a user can enter his data and analyze outcome.

This could be an input to search a location, popups opening on a click and giving some details or location analytics components like routing or isochrones.

Turn your map into Location Intelligence application

Geoapify offers APIs and map components which help to turn your map not just into the interactive map but into a Location Intelligence application of the full value.

Geocoding API to create a location input field

It’s always great to have a location input on your map. However, it’s forgotten by many map developers. The service which allows you to look for locations address details by search string is called Geocoding.

As a result, with Geoapify Geocoding API you are able to specify the search and retrieve accurate results. As well as implement autocomplete fields.

Location autocomplete input

Check our Geoacoding API in the Playground.

Reverce Geocoding API to get a location address by its coordinates

Another useful geospatial tool is Reverse Geocoding API. It returns a place address by its coordinates. With Reverse Geocoding API you are able to answer the question “What is located here?”, when a user clicks on the map.

Check our Reverse Geoacoding API in the Playground.

Routing API to build a path

Often people search locations on a map which are close to their route. Although route building is so popular, it’s present only on very several maps. By using Routing API together with Geocoding API, you can create a convenient routing tool for your map on your website.

Routing for your web site

Check our Routing API in the Playground.

Isolines API to analyse travel times

Can you show your customers what can the get from their living or working place? Sure! With Isolines API! By using the API you will be able to highlight areas on a map reachable within a given time. Together with driving and walking modes, our Isoline APIs work with transit and bicycle modes.

Isochrone for travel time analytics

Check our Isoline API in the Playground.

Places API to show amenities near by

Last but not least are amenities and points of interest. Show your customers places they interested in. Restaurants, fitness studios, schools, and other places.

Check our Places API in the Playground.

Use Geoapify APIs to create a map for your website

Read more about our APIs here.

Location autocomplete with Angular

Geoapify provides an API which allows searching a location by the query string. In this article, we provide you an example of how to create a location autocomplete field by using Geoapify Geocoding API.

In our example, we use Angular platform together with Angular Material framework.

Geoapify Geocoding API Playground contains a working example of the location autocomplete described in this article.

Pre-requirements

  • Angular Material installed. Read about installation here.

Step 1. Create a new component for a location autocomplete

Create a new component with Angular Cli command:

ng generate component Autocomplete

Step 2. Required imports

To make the component from this tutorial work, it’s required to import the following modules into your module:

@NgModule({
  declarations: [..., AutocompleteComponent],
  imports: [
    CommonModule,
    HttpClientModule,
    MatInputModule,
    MatFormFieldModule,
    FormsModule,
    ReactiveFormsModule,
    MatAutocompleteModule,
    MatTooltipModule,
    ...
  ],
  exports: [...]
})

Step 3. HTML template

We use the following components:

  • mat-form-input
  • mat-input
  • mat-autocomplete
  • mat-tooltip
  • formControl from ReactiveFormsModule.

Add the following code into your component html template:

<mat-form-field floatLabel="never">
  <input matInput type="text" [matAutocomplete]="auto" 
    [formControl]="inputFieldFormControl" placeholder="Enter location here" />

  <mat-autocomplete #auto="matAutocomplete" autoActiveFirstOption>
    <mat-option *ngFor="let option of searchOptions | async" [value]="option.shortAddress" 
      (onSelectionChange)="optionSelectionChange(option, $event)"
      [matTooltip]="option.fullAddress" matTooltipShowDelay="1000">
      <span class="mat-body">{{ option.shortAddress }}</span>
    </mat-option>
  </mat-autocomplete>
</mat-form-field>

Here is an idea of how the location autocomplete will work:

  • The string value will be stored in the “inputFieldFormControl” reactive form field.
  • The “inputFieldFormControl” field fires an event when its value was changed.
  • When an event fired we send an HTTP Get request to Geocoding API to retrieve place suggestions and store them in “searchOptions”.
  • mat-autocomplete loads options from the “searchOptions” asynchronously.

Step 4. AutocompleteComponent class

Here is the code of the AutocompleteComponent class:

import { Component, Output, EventEmitter, OnDestroy } from '@angular/core';
import { MatOptionSelectionChange } from '@angular/material';
import { Subject, Subscription } from 'rxjs';
import { FormControl } from '@angular/forms';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-autocomplete',
  templateUrl: './autocomplete.component.html',
  styleUrls: ['./autocomplete.component.scss']
})
export class AutocompleteComponent implements OnDestroy {
  @Output()
  locationChange: EventEmitter<PlaceSuggestion> = new EventEmitter<PlaceSuggestion>();

  searchOptions: Subject<PlaceSuggestion[]> = new Subject<PlaceSuggestion[]>();
  inputFieldFormControl: FormControl = new FormControl();

  private valueChangesSub: Subscription;
  private choosenOption: PlaceSuggestion;

  private userInputTimeout: number;
  private requestSub: Subscription;

  constructor(private http: HttpClient) {
    this.valueChangesSub = this.inputFieldFormControl.valueChanges.subscribe((value) => {
      if (this.userInputTimeout) {
        window.clearTimeout(this.userInputTimeout);
      }

      if (this.choosenOption && this.choosenOption.shortAddress === value) {
        this.searchOptions.next(null);
        return;
      }

      if (!value || value.length < 3) {
        // do not need suggestions until for less than 3 letters
        this.searchOptions.next(null);
        return;
      }

      this.userInputTimeout = window.setTimeout(() => {
        this.generateSuggestions(value);
      }, 300);
    });
  }

  ngOnDestroy() {
    this.valueChangesSub.unsubscribe();
  }

  private generateSuggestions(text: string) {
    const url = `https://api.geoapify.com/v1/geocode/autocomplete?text=${text}&limit=5&apiKey=${YOUR_API_KEY}`;

    if (this.requestSub) {
      this.requestSub.unsubscribe();
    }

    this.requestSub = this.http.get(url).subscribe((data: GeoJSON.FeatureCollection) => {
      const placeSuggestions = data.features.map(feature => {
        const properties: GeocodingFeatureProperties = (feature.properties as GeocodingFeatureProperties);

        return {
          shortAddress: this.generateShortAddress(properties),
          fullAddress: this.generateFullAddress(properties),
          data: properties
        }
      });

      this.searchOptions.next(placeSuggestions.length ? placeSuggestions : null);
    }, err => {
      console.log(err);
    });
  }

  private generateShortAddress(properties: GeocodingFeatureProperties): string {
    let shortAddress = properties.name;

    if (!shortAddress && properties.street && properties.housenumber) {
      // name is not set for buildings
      shortAddress = `${properties.street} ${properties.housenumber}`;
    }

    shortAddress += (properties.postcode && properties.city) ? `, ${properties.postcode}-${properties.city}`: '';
    shortAddress += (!properties.postcode && properties.city && properties.city  !== properties.name) ? `, ${properties.city}`: '';
    shortAddress += (properties.country && properties.country !== properties.name) ? `, ${properties.country}` : '';

    return shortAddress;
  }

  private generateFullAddress(properties: GeocodingFeatureProperties): string {
    let fullAddress = properties.name;
    fullAddress += properties.street ? `, ${properties.street}` : '';
    fullAddress += properties.housenumber ? ` ${properties.housenumber}` : '';
    fullAddress += (properties.postcode && properties.city) ? `, ${properties.postcode}-${properties.city}`: '';
    fullAddress += (!properties.postcode && properties.city && properties.city  !== properties.name) ? `, ${properties.city}`: '';
    fullAddress += properties.state ? `, ${properties.state}`: '';
    fullAddress += (properties.country && properties.country !== properties.name) ? `, ${properties.country}` : '';
    return fullAddress;
  }

  public optionSelectionChange(option: PlaceSuggestion, event: MatOptionSelectionChange) {
    if (event.isUserInput) {
      this.choosenOption = option;
      this.locationChange.emit(option);
    }
  }
}

export interface PlaceSuggestion {
  shortAddress: string;
  fullAddress: string;
  data: GeocodingFeatureProperties;
}

interface GeocodingFeatureProperties {
  name: string;
  country: string;
  state: string;
  postcode: string;
  city: string;
  street: string;
  housenumber: string;
}

inputFieldFormControl

As described above, the field holds the value of the search string, which is observed by this.inputFieldFormControl.valueChanges.subscribe().

To keep the code clean we save the created subscription in the variable valueChangesSub and unsubscribe on destroy.

userInputTimeout

To avoid too many unnecessary requests and decrease the application load, we perform HTTP request only when a user stops to type.

This implemented by using userInputTimeout, which sets every time when the user enters a new value.

searchOptions

Contain the values returned by Geocoding API and displayed by autocomplete control.

When we set this.searchOptions.next(null), the autocomplete control is hidden.

PlaceSuggestion & GeocodingFeatureProperties

We use the interfaces to simplify work with JSON object returned by the Geocoding API. We export PlaceSuggestion to be able to use the interface in other components, services, and modules.

locationChange

Is an Output() of the Autocomplete component. We this.locationChange.emit(option) when a new place suggestion was selected.

generateShortAddress() & generateFullAddress()

As Geocoding API returns value with address components, but not a formatted address, we need to generate an address string of required format. generateShortAddress() & generateFullAddress() are examples of how an address string could be generated.

Step 5. Add the Location Autocomplete component into your code

The new component could be added into your code in the following way:

<app-autocomplete (locationChange)="autocompleteChanged($event)"></app-autocomplete>

When a new value was chosen in the location autocomplete, the event is fired:

autocompleteChanged(value: PlaceSuggestion) {}

How to call HTTP Get request

All Geoapify APIs work via HTTP Get requests. In this article, we would like to highlight some examples, how to implement HTTP request in your application.

JavaScript: Get request with XMLHttpRequest object

Firstly, one HTTP request example with the XMLHttpRequest object. The XMLHttpRequest object is classical and proved by time way to build HTTP request in JavaScript.

Do not be confused by word “XML” in the name. XMLHttpRequest is very flexible in nature and allows to operate with any data type. Of cause, the method perfectly works with JSON objects as well.

For example, the implementation could look like:

var xmlHttp = new XMLHttpRequest();
xmlHttp.responseType = 'json';
var url = "https://api.geoapify.com/v1/isoline?lat=47.68388118858141&lon=8.614278188654232&type=time&mode=drive&range=2700&api_key=YOUR_API_KEY";
xmlHttp.onreadystatechange = () => {
    if (xmlHttp.readyState === 4) {
        if (xmlHttp.status === 200) {
            // check xmlHttp.responseText here;
            console.log(xmlHttp.response);
        } else {
            console.log(xmlHttp.response);
        }
    }
};
xmlHttp.open("GET", url, true); // true for asynchronous 
xmlHttp.send(null);

JavaScript: fetch() to build HTTP request

Another we-known way to build an HTTP request in JavaScript application is using fetch(). Fetch() provides similar functionality as XMLHttpRequest, but instead of callbacks and a bit complicated API, fetch() returns a promise. Moreover, you can define URI parameters separately as an object. So fetch() allows making your code cleaner and smaller.

For instance, fetch() could look that way in your code:

var url = new URL('https://api.geoapify.com/v1/isoline');

var params = [['lat', '47.68388118858141'], ['lon', '8.614278188654232'], ['mode', 'drive'], ['type', 'time'], ['range', '2700'], ['api_key', 'YOUR_API_KEY']];
url.search = new URLSearchParams(params);

fetch(url).then(response => response.json()).then(data => console.log(data)).catch(err => console.log(err));

Angular: HttpClient

Angular brings new standards into our code and life. The common way to implement HTTP request in Angular is by using HTTPClient.

You just add HttpClient to a class constructor and call get(). However, don’t forget to import HttpClientModule in your module file. The data returned is already in JSON format, you do not need to convert it separately.

// import BrowserModule and HttpClientModule in a module file
constructor(private httpClient: HttpClient, ...) {

}

getData() {
  const url = "https://api.geoapify.com/v1/isoline?lat=47.68388118858141&lon=8.614278188654232&type=time&mode=drive&range=2700&api_key=YOUR_API_KEY";

  this.httpClient.get(url).subscribe(data => {
    // check for returned data
  }, err => {
    // check for error
  });
}

HTTP Request in Terminal

Sometimes it’s useful to call HTTP requests from terminal to check if the functionality works independently from a platform or framework. You can do it with curl, for example:

curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET "https://api.geoapify.com/v1/isoline?lat=47.68388118858141&lon=8.614278188654232&type=time&mode=drive&range=2700&api_key=YOUR_API_KEY"

Geoapify APIs to create a map

Our APIs help to create an interactive map with Location Analytics features. For example, your mapping could contain a search field, routing or travel time maps. As our APIs work through HTTP Get requests, they are independent of the framework you use. Register and start using our APIs now, read more about APIs or try them out in Playground.