navigator.geolocation.watchPosition返回位置不可用

时间:2016-08-31 15:06:50

标签: angular typescript geolocation rxjs

我有监控设备位置的服务:

getLocation(opts): Observable<any> {
    return Observable.create(observer => {
        if (window.navigator && window.navigator.geolocation) {
            window.navigator.geolocation.watchPosition((position) => {
                observer.next(position);
            }, (error) => {
                switch (error.code) {
                    case 1:
                        observer.error('errors.location.permissionDenied');
                        break;
                    case 2:
                        observer.error('errors.location.positionUnavailable');
                        break;
                    case 3:
                        observer.error('errors.location.timeout');
                        break;
                }
            }, opts);
        } else {
            observer.error('errors.location.unsupportedBrowser');
        }
    });
}

然后检索lat,long in component:

ngOnInit() {
    var source = this.locationService.getLocation({enableHighAccuracy:true, maximumAge:30000, timeout:27000});
    source.subscribe(pos => {
        this.lat = pos.coords.latitude;
        this.long = pos.coords.longitude;
    }, err => {
        this.err = err;
        console.log(err);
    });
}

此代码在macbook和iphone上的浏览器中正常工作,即它可以在设备移动时检索和更新位置。

但是在我的ipad上(wifi只有没有gps)它可以在第一时间获得位置,然后几秒钟后,服务返回错误代码2,即位置不可用和浏览器停止更新位置。我不确定它是否停止工作或它仍然在运行,但总是返回错误代码2。

我的问题是:

  1. watchPosition需要gps才能运行吗?但是我的macbook也没有gps。
  2. 如果发生错误并从observable返回,我是否需要重新订阅才能再次获取数据(位置)或等到错误消失。

2 个答案:

答案 0 :(得分:0)

我在某些浏览器中使用navigator.geolocation.watchPosition在使用RxJS,Meteor和其他一些东西的Angular2服务中遇到了一个奇怪的错误。不确定原因是什么,但是在第一次回调之后“this”超出了范围,并且在将来的回调中无法解决。修复是为当前类创建一个本地范围的引用:

startTracking() {
    let that = this; //The fix            
    let opts = {maximumAge: 60000, timeout: 30000}
    this.watchId = navigator.geolocation.watchPosition((position) =>
        {
            console.log(position);
            that.locationTrackingActive = true;
            that.reactiveDeviceLocation.next(position.coords);
        },
        (err) =>
        {
            console.log(err);
            that.locationTrackingActive = false;
        },
        opts);

}

您不需要在发生错误后重新订阅watchPosition,它会继续尝试,直到它能够获得信号。对于您的observable,使用BehaviorSubject(上面的reactiveDeviceLocation)允许您设置默认的起始位置,然后仅在成功时更新它...这样当新客户端订阅时,它们将被赋予默认(或最新)位置并且可以可以使用它,直到watchPosition成功更新主题。

我按如下方式设置(在同一服务中)

reactiveDeviceLocation: Rx.BehaviorSubject<Object> = new Rx.BehaviorSubject<Object>({latitude:37.5, longitude:122});

getReactiveDeviceLocation() {
    return this.reactiveDeviceLocation;
}

然后,该服务的任何消费者都可以致电

MyService.getReactiveDeviceLocation.subscribe((pos) => {
   console.log(pos); //Last known location.
})

答案 1 :(得分:0)

在这里,我正在使用watchPosition函数从用户那里获取位置信息。创建具有默认位置的主题(可选)。加载组件后,调用getLocation,并在组件内使用异步管道显示数据。

Live Demo

location.component.html

<div *ngIf="coordindates | async">
  <p>
    {{coordindates | async | json}}
  </p>
</div>

location.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import {
    LocationService
} from '@app/core';
import { Observable } from 'rxjs';

@Component({
    selector: 'app-location',
    templateUrl: './location.component.html',
    styleUrls: ['./location.component.scss']
})
export class LocationComponent implements OnInit {
    coordindates: Observable<Object>;
    constructor(private locationService: LocationService) { }

    ngOnInit(): void {
        this.coordindates = this.locationService.getLocation();
    }
}

location.service.ts

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
@Injectable({
    providedIn: 'root'
})
export class LocationService {
    public watchId: number;
    public locationTrackingActive = false;
    public currentLoation: { latitude: number, longitude: number } = { latitude: undefined, longitude: undefined };
    private reactiveDeviceLocation$: Subject<Object>;

    constructor() {
        this.reactiveDeviceLocation$ = new BehaviorSubject<Object>(this.currentLoation);
    }

    getLocation(): Observable<Object> {
        const opts = { enableHighAccuracy: true, maximumAge: 60000, timeout: 30000 };
        this.watchId = navigator.geolocation.watchPosition((position) => {
            this.locationTrackingActive = true;
            this.currentLoation = { latitude: position.coords.latitude, longitude: position.coords.longitude };
            this.reactiveDeviceLocation$.next(this.currentLoation);
        },
        (err) => {
            console.log(err);
            this.locationTrackingActive = false;
        },
        opts);
        return this.reactiveDeviceLocation$;
    }
}