Skip to content

Instantly share code, notes, and snippets.

@sasha7
Last active February 21, 2022 12:44
Show Gist options
  • Save sasha7/0c32f3686eb49d44ccc8 to your computer and use it in GitHub Desktop.
Save sasha7/0c32f3686eb49d44ccc8 to your computer and use it in GitHub Desktop.
import {Injectable, provide} from 'angular2/core';
import {Observable} from 'rxjs';
const GEOLOCATION_ERRORS = {
'errors.location.unsupportedBrowser': 'Browser does not support location services',
'errors.location.permissionDenied': 'You have rejected access to your location',
'errors.location.positionUnavailable': 'Unable to determine your location',
'errors.location.timeout': 'Service timeout has been reached'
};
@Injectable()
export class GeolocationService {
/**
* Obtains the geographic position, in terms of latitude and longitude coordinates, of the device.
* @param {Object} [opts] An object literal to specify one or more of the following attributes and desired values:
* - enableHighAccuracy: Specify true to obtain the most accurate position possible, or false to optimize in favor of performance and power consumption.
* - timeout: An Integer value that indicates the time, in milliseconds, allowed for obtaining the position.
* If timeout is Infinity, (the default value) the location request will not time out.
* If timeout is zero (0) or negative, the results depend on the behavior of the location provider.
* - maximumAge: An Integer value indicating the maximum age, in milliseconds, of cached position information.
* If maximumAge is non-zero, and a cached position that is no older than maximumAge is available, the cached position is used instead of obtaining an updated location.
* If maximumAge is zero (0), watchPosition always tries to obtain an updated position, even if a cached position is already available.
* If maximumAge is Infinity, any cached position is used, regardless of its age, and watchPosition only tries to obtain an updated position if no cached position data exists.
* @returns {Observable} An observable sequence with the geographical location of the device running the client.
*/
public getLocation(opts): Observable<any> {
return Observable.create(observer => {
if (window.navigator && window.navigator.geolocation) {
window.navigator.geolocation.getCurrentPosition(
(position) => {
observer.next(position);
observer.complete();
},
(error) => {
switch (error.code) {
case 1:
observer.error(GEOLOCATION_ERRORS['errors.location.permissionDenied']);
break;
case 2:
observer.error(GEOLOCATION_ERRORS['errors.location.positionUnavailable']);
break;
case 3:
observer.error(GEOLOCATION_ERRORS['errors.location.timeout']);
break;
}
},
opts);
}
else {
observer.error(GEOLOCATION_ERRORS['errors.location.unsupportedBrowser']);
}
});
}
}
export var geolocationServiceInjectables: Array<any> = [
provide(GeolocationService, { useClass: GeolocationService })
];
@SpazzMarticus
Copy link

@mikepc You probably already found it out yourself: Yes you can substitute watchPosition for getCurrentPosition

@raviteja-avvari You have to subscribe to the observable returned by getLocation

this.instanceOfService.getLocation().subscribe(
    function(position) { ... },
    function(error) { ... },
    function(complete) { ... }
);

@FunnyGhost
Copy link

Thanks for this! I have a question, though. I have a method that gets data from a server based on the location and it returns an Observable.
Like this:

    getMessages(): Observable<IMessage[]> {
        return this._http.get(this.someUrl)
            .map((response: Response) => <IMessage[]>response.json())
            .do(data => console.log('All: ' + JSON.stringify(data)))
            .catch(this.handleError);
    }

The someUrl must contain the coordinates as well, so how can I make sure that the coordinates are gotten before calling the http and returning the result?

Thanks!

@photostu
Copy link

Thank you for this!
This code can be updated now to not use provide :)

@xtiannyeto
Copy link

Thanks

@Rchua72
Copy link

Rchua72 commented Jun 17, 2017

Hi..is this working in Angular 2? I am always getting 'Unable to determine your location' in Chrome.

@ravivit9
Copy link

ravivit9 commented Aug 2, 2017

Hi, I am able to get the location first, I am using watchPosition instead of getCurrentPosition. But I am getting the position only once and it's not watching anymore. Any thoughts here?

@suraj021
Copy link

suraj021 commented Aug 9, 2017

Thank you. Can this code be modified for use is Angular 4?

@gise88
Copy link

gise88 commented Sep 1, 2017

@ravivit9 you probably need to to delete the: observer.complete();

@jagdishmastek
Copy link

jagdishmastek commented Dec 6, 2017

`
Angular 5 Geolocation service

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';

const GEOLOCATION_ERRORS = {
'errors.location.unsupportedBrowser': 'Browser does not support location services',
'errors.location.permissionDenied': 'You have rejected access to your location',
'errors.location.positionUnavailable': 'Unable to determine your location',
'errors.location.timeout': 'Service timeout has been reached'
};

@Injectable()
export class GeoLocationService {

public getLocation(geoLocationOptions?: any): Observable {
geoLocationOptions = geoLocationOptions || { timeout: 5000 };

    return Observable.create(observer => {

      if (window.navigator && window.navigator.geolocation) {
        window.navigator.geolocation.getCurrentPosition(
          (position) => {
            observer.next(position);
            observer.complete();
          },
          (error) => {
            switch (error.code) {
              case 1:
                observer.error(GEOLOCATION_ERRORS['errors.location.permissionDenied']);
                break;
              case 2:
                observer.error(GEOLOCATION_ERRORS['errors.location.positionUnavailable']);
                break;
              case 3:
                observer.error(GEOLOCATION_ERRORS['errors.location.timeout']);
                break;
            }
          },
          geoLocationOptions);
    } else {
          observer.error(GEOLOCATION_ERRORS['errors.location.unsupportedBrowser']);
    }

    });



  }
}

export let geolocationServiceInjectables: Array<any> = [
  {provide: GeoLocationService, useClass: GeoLocationService }
];

`

@HarelM
Copy link

HarelM commented Jan 26, 2018

A different but similar approach if anyone is interested - this one is based on publishing events when the service is enabled:
Written with angular 5.
I'm not very strong with observables so the code might be shorter.

import { Injectable, EventEmitter } from "@angular/core";

declare type GeoLocationServiceState = "disabled" | "searching" | "tracking";

@Injectable()
export class GeoLocationService {

    private state: GeoLocationServiceState;
    private watchNumber: number;

    public positionChanged: EventEmitter<Position>;

    constructor() {
        this.watchNumber = -1;
        this.positionChanged = new EventEmitter();
        this.state = "disabled";
    }

    public getState(): GeoLocationServiceState {
        return this.state;
    }

    public enable() {
        switch (this.state) {
            case "disabled":
                this.startWatching();
                return;
            case "searching":
            case "tracking":
                return;

        }
    }
    public disable() {
        switch (this.state) {
        case "disabled":
            return;
        case "searching":
        case "tracking":
            this.stopWatching();
            return;
        }
    }

    private startWatching() {
        if (window.navigator && window.navigator.geolocation) {
            this.state = "searching";
            this.watchNumber = window.navigator.geolocation.watchPosition(
                (position) => {
                    this.state = "tracking";
                    this.positionChanged.next(position);
                },
                (error) => {
                    // sending error will terminate the stream
                    this.positionChanged.next(null);
                    this.disable();
                },
                {
                    enableHighAccuracy: true,
                    timeout: 5000
                });
        }
    }

    private stopWatching() {
        if (this.watchNumber !== -1) {
            window.navigator.geolocation.clearWatch(this.watchNumber);
            this.watchNumber = -1;
            this.state = "disabled";
        }
    }
}

@andreypelykh
Copy link

Thank you! I use rx-dom library for this. I think it can be more convenient.

@rmxakalogistik
Copy link

Hi @andreypelykh, did you update this with rx-dom? if yes please share.

@andreypelykh
Copy link

Hello, @rmxakalogistik! What do you mean? You can simply use an example from the link that I provided. https://github.com/Reactive-Extensions/RxJS-DOM/blob/master/doc/operators/watchposition.md#example

var source = Rx.DOM.geolocation.watchPosition();

var subscription = source.subscribe(
  function (pos) {
    console.log('Next:' + position.coords.latitude + ',' + position.coords.longitude);
  },
  function (err) {
    var message = '';
    switch (err.code) {
      case err.PERMISSION_DENIED:
        message = 'Permission denied';
        break;
      case err.POSITION_UNAVAILABLE:
        message = 'Position unavailable';
        break;
      case err.PERMISSION_DENIED_TIMEOUT:
        message = 'Position timeout';
        break;
    }
    console.log('Error: ' + message);
  },
  function () {
    console.log('Completed');
  });

@askuri
Copy link

askuri commented Apr 13, 2021

This library solved the problem and works with Angular 11: https://www.npmjs.com/package/@ng-web-apis/geolocation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment