How to use Google Maps API with Typescript

In this post, I will show some sample code on how to use Google Maps API with Typescript in MVC application. I will not go into discussion and details about pre-requisite for using Google Maps API. You can read all about it on Google's API page. For discussion I will assume you have created your Google account and have API key to use in Web Browser.

Following screenshot shows end result of sample code I am going to talk about. This is view from our my iOS application Topikos application portal. It is used to track movement of a mobile device in real time.

Google Maps with Typescript

Initialize

Following code snippet shows how the map element is created and initialized on view.

    <div class="gmap" id="deviceTracksMap"></div>

    <script type="text/javascript">
        function initTracksMap() {
            var tracksView = new DeviceTacksView();
        }
    </script>
    <script src="https://maps.googleapis.com/maps/api/js?
              key=AI...............gS4IZ25x1c&callback=initTracksMap" async defer></script>

I have eliminated lot of peripheral HTML to keep this snippet simple. There are following important aspects of this initialization code.

  • Google Maps API javascript is directly downloaded from Google's server and is using async defer. I will definitely recommend using async download with call back function. I have experimented with synchronous download and noticed delays and errors that may creep up if you try to create map object and maps API javascript file is still being downloaded.
  • In the download URL, you have to specify API key that got when signing up as developer. If you don't provide a valid key, you will get blank box instead of map.
  • Only initialize map object in callback function. It ensures that all required components have been downloaded and ready to go. I am instantiating DeviceTracksView object in callback function. The creation of object takes care of initialization of maps object. I will show that implementation shortly.

Google Maps API Typings

Since we are going to use Typescript to build the application, make sure that you have installed Google Maps API typings NuGet package. Otherwise Visual Studio's Typescript compiler is not going to be very happy.

Implementation

Here is complete code that demonstrates following key aspects of using Google Maps API with Typescript.

  • Initialize map object in constructor
  • Add marker locations after data has been received from an ajax call
  • Use a custom symbol for one of the markers
  • How to pan to particular location on map
    class LocationTrackPointModel{
    Longitude:number;
    Latitude:number;
    DeviceName:string;
    VisitTime:number;
    Address:string;
    }
    
    class DeviceTracksRequestModel {
    DeviceId: number;
    DeviceUid:string;
    StartDate: number;
    EndDate: number;
    MaxRecords:number;
    }

class DeviceTacksView {
    mapElement:Element;
    tracksMap: google.maps.Map;
    deviceIdElement: JQuery;
    deviceUidElement:JQuery;
    lastLocationTime: number;
    latestLocation:LocationTrackPointModel;
    locations: Array<LocationTrackPointModel>

    constructor() {
        this.deviceIdElement = $("#deviceUid");
        this.deviceUidElement = $("#deviceUid");
        this.initializeMap();
    }

    private getTrackPoints() {
        const requestData = new DeviceTracksRequestModel();
        requestData.DeviceId = this.deviceIdElement.val();
        requestData.DeviceUid = this.deviceUidElement.val();
        requestData.StartDate = this.lastLocationTime;

        let apiUrl = CoreSettings.baseUrl + "MyApp/MyLocations";
        if (this.lastLocationTime != null) {
            apiUrl += `/${this.lastLocationTime}`;
        }
        try {
            $.ajax({
                url: apiUrl,
                dataType: "json",
                data:requestData
            })
                .done((data, statusText, jqXhr) => {
                    var apiResponse = <IApiResponse>data;
                    if (data.StatusCode === 200) {
                        //Process location list
                        var locations = JSON.parse(data.Data);
                        this.processLocationsData(locations);
                    } else {
                        // Handle api error
                    }
                })
                .always(() => {})
                .fail((jqXhr, statusText, err) => {
                    Notifier.ActionError("Error:", "Unable to get device's tracking data");
                });
        } catch (error) {
            //TODO: Log the error
        }
    }

    private processLocationsData(newlocations?:Array<LocationTrackPointModel>) {
        if (newlocations == null) return;
        // Sort locations
        newlocations.sort((a, b) => {
            return b.VisitTime - a.VisitTime;
        });
        this.lastLocationTime = newlocations[0].VisitTime;
        // only display latest 50 locations.
        const filteredList = _.first(newlocations, 50);
        this.addLocationsToMap(filteredList);
    }

    private addLocationsToMap(locations: Array<LocationTrackPointModel>) {
        // map location points to map data objects
        _.map(locations, (location, index) => {
            const position = new google.maps.LatLng(location.Latitude, location.Longitude);
            const marker = new google.maps.Marker();
            marker.setPosition(position);
            const title = moment(location.VisitTime).format("MMMM Do YYYY, h:mm:ss a");
            marker.setTitle(title);
            // for latest location, use special icon
            if (index === 0) {
                this.latestLocation = location;
                marker.setIcon(
                { path: google.maps.SymbolPath.CIRCLE, strokeColor: "#393", scale: 8 });
            }
            marker.setMap(this.tracksMap);
        });
        // Pan  map to last location.
        if (this.latestLocation != null) {
            this.tracksMap.panTo(
             new google.maps.LatLng(this.latestLocation.Latitude, this.latestLocation.Longitude));
        }
    }

    private initializeMap() {
        this.mapElement = $("#deviceTracksMap")[0];
        this.tracksMap = new google.maps.Map(this.mapElement, {
            center: { lat: -34.397, lng: 150.644 },
            zoom: 10
        });
        this.locations = [];
        this.getTrackPoints();
    }
}

This implementation uses libraries like moment.js and underscore.js to help with some book keeping work. I have kept this code to bare minimum for discussion. In subsequent posts I will show some more goodies with use of Typescript in your MVC application. Feel free to drop me a line for any questions and comments.

comments powered by Disqus

Search

Social

Weather

Monthly Posts