I'm trying to get coordinates of my device location using the cordova geolocation plugin.
When running the app on android (10), I get prompted for location permissions which I set to always allow and after that my getLocation function gets executed but I don't get any of the additional feedback which I programmed in to suggest that it has actually received coordinates.
For the moment don't focus on the loadMap component, my focus currently is to retrieve coordinates,
I already did the import in app.module.ts
This is my home.page.ts
(For debug purposes I chained together the loadMap function and getLocation function to make sure the getLocation function gets executed (which it does judging by the begin location flow message I receive)
import { Component, ViewChild } from '#angular/core';
import { Geolocation} from '#ionic-native/geolocation/ngx';
// Import classes from maps module
import {
GoogleMaps,
GoogleMap,
GoogleMapsEvent,
LatLng,
MarkerOptions,
Marker
} from '#ionic-native/google-maps';
import { Platform, NavController } from '#ionic/angular';
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
lat;
lng;
constructor( public platform: Platform, public nav: NavController, private geolocation: Geolocation ) {
}
ngAfterViewInit() {
this.platform.ready().then( () => {
this.loadMap();
});
this.getLocation();
}
loadMap() {
console.log('map render start');
let map = GoogleMaps.create( 'map' );
map.one( GoogleMapsEvent.MAP_READY ).then( ( data: any ) => {
let coordinates: LatLng = new LatLng( 50.7783, 119.4179 );
let position = {
target: coordinates,
zoom: 14
};
map.animateCamera( position );
let markerOptions: MarkerOptions = {
position: coordinates,
icon: "assets/images/marker.png",
title: 'Hello California'
};
const marker = map.addMarker( markerOptions )
.then( ( marker: Marker ) => {
marker.showInfoWindow();
});
});
this.getLocation();
}
getLocation() {
console.log('begin location flow');
this.geolocation.getCurrentPosition().then((resp) => {
// resp.coords.latitude
// resp.coords.longitude
this.lat = resp.coords.latitude;
this.lng = resp.coords.longitude;
alert('lat' + this.lat + 'lon' + this.lng);
console.log('location succes');
}).catch((error) => {
console.log('Error getting location', error);
});
}
}
you need to move the method your calling that makes use of the native plugin into the platform ready promise call back. Native plugins cannot be used until this fires.
this.platform.ready().then(() => {
this.getLocation();
})
Based on your code above, you're calling this.getLocation() twice, as you pointed out, however, the first/initial this.getLocation() call gets run probably before this.platform.ready() (see ngAfterViewInit() routine).
Could this cause a conflict?
Related
I am working on something where I need to track background location if the app is in background and also if the device is asleep. I currently have it working for app in background but it stops tracking when the device is asleep. I am using Expo for the app and using Expo Task Manager alongside Expo Location to fetch location in background.
Anyone have any idea how to fetch location while app is in background and device is in sleep mode ?
Here's the code
import { StatusBar } from 'expo-status-bar';
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import * as Location from 'expo-location';
import * as TaskManager from 'expo-task-manager';
const App = () => {
useEffect(() => {
(async () => await _askForLocationPermission())();
});
this.backgroundLocationFetch = async () => {
const { status } = await Location.requestBackgroundPermissionsAsync();
if (status === 'granted') {
console.log('cmon dance with me!')
await Location.startLocationUpdatesAsync('FetchLocationInBackground', {
accuracy: Location.Accuracy.Balanced,
timeInterval: 3000,
distanceInterval: 1,
foregroundService: {
notificationTitle: 'Live Tracker',
notificationBody: 'Live Tracker is on.'
}
});
}
}
const _askForLocationPermission = async () => {
(async () => {
let { status } = await Location.requestBackgroundPermissionsAsync();
if (status !== 'granted') {
setgpsErrorMsg('Permission to access location was denied');
}
})();
};
return(
<View>
<Text></Text>
</View>
)
};
TaskManager.defineTask('FetchLocationInBackground', ({ data, error }) => {
if (error) {
console.log("Error bg", error)
return;
}
if (data) {
const { locations } = data;
console.log("BGGGG->", locations[0].coords.latitude, locations[0].coords.longitude);
}
});
export default App;
I had precisely the same problem and solved this by using and EventEmitter to dispatch the location updates to the UI component.
Top of file:
import EventEmitter from 'EventEmitter'
const locationEmitter = new EventEmitter();
didMount:
locationEmitter.on(LOCATION_UPDATE, (locationData) => {
console.log('locationEmitter locationUpdate fired! locationData: ', locationData);
let coordinatesAmount = locationData.newRouteCoordinates.length - 1;
this.setState({
latitude: locationData.newRouteCoordinates[coordinatesAmount - 1].latitude,
longitude: locationData.newRouteCoordinates[coordinatesAmount - 1].longitude,
routeCoordinates: this.state.routeCoordinates.concat(locationData.newRouteCoordinates)
})
})
componentWillUnmount:
locationEmitter.off(LOCATION_UPDATE);
inside background task definition:
locationEmitter.emit(LOCATION_UPDATE, locationData)
This succesfully sends the location data from the background task, but I'm still stuck in the problem of how to make the background task send location update batches more often. My related post is here.
I have a problem with google maps and IONIC V4. I created an app using IONIC, Firebase and google maps. In my home view I have a google maps view in which I add markers that I have store in Firebase. I do not why when I query my firebase firestore and there are more than 5 places, the app crashes when I call the addMarkerSync function.
import { Component, ViewChild } from '#angular/core';
import { MenuController, Platform, LoadingController, ToastController } from '#ionic/angular';
import { AuthService } from '../services/auth.service';
import { Router } from '#angular/router';
import { CompartirService } from '../services/compartir.service';
import { Compartir } from '../models/compartir';
import { AndroidPermissions } from '#ionic-native/android-permissions/ngx';
import { environment } from '../../environments/environment';
import {
GoogleMaps,
GoogleMap,
GoogleMapsEvent,
Marker,
MyLocation,
LatLng
} from '#ionic-native/google-maps';
import { AngularFirestore } from 'angularfire2/firestore';
import { GoogleMapsAnimation } from '#ionic-native/google-maps/ngx';
import { Observable } from 'rxjs';
declare global {
interface Window { my: any; }
}
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
usuario : Observable<any>;;
map: GoogleMap;
loading: any;
latLong: any;
//Lista donde se almacenan las historias
stories: any[]=[];
constructor(public loadingCtrl: LoadingController,public menuController: MenuController,public authService:AuthService,
private router: Router,public compartirService:CompartirService,private db: AngularFirestore,
private platform: Platform,
public toastCtrl: ToastController,
private androidPermissions: AndroidPermissions){
this.menuController.enable(true);
this.authService.user.subscribe(user => {
if(user){
console.log(user);
this.usuario= this.db.doc("/usuarios/"+user.uid).valueChanges();
}
});
}
async ngOnInit() {
//Carg mis historias de forma local
await this.platform.ready();
await this.loadMap();
this.cargarMisHistorias()
}
loadMap() {
console.log("Loading map")
this.map = GoogleMaps.create('map_canvas', {
camera: {
target: {
lat: 4.6028611,
lng: -74.0657429
},
zoom: 18,
tilt: 30
}
});
this.loadCurrentPosition()
}
async loadCurrentPosition() {
//this.map.clear();
this.loading = await this.loadingCtrl.create({
message: 'Localizando...'
});
await this.loading.present();
// Get the location of you
this.map.getMyLocation().then((location: MyLocation) => {
this.loading.dismiss();
console.log(JSON.stringify(location, null ,2));
this.latLong = location.latLng;
console.log(this.latLong);
// Move the map camera to the location with animation
this.map.animateCamera({
target: location.latLng,
zoom: 17,
tilt: 30
});
})
.catch(err => {
this.loading.dismiss();
this.showToast(err.error_message);
});
}
async showToast(message: string) {
let toast = await this.toastCtrl.create({
message: message,
duration: 2000,
position: 'middle'
});
toast.present();
}
//retorna la lat y long.
getLatLong() {
return this.latLong;
}
cargarMisHistorias(){
this.map.clear();
this.stories = [];
this.db.collection('historias', ref => ref.where('userID', '==', this.authService.userDetails.uid)).get().subscribe( (querySnapshot) => {
//querySnapshot.forEach((doc) => {
for(var i=0;i<querySnapshot.size;i++){
var doc = querySnapshot.docs[i];
console.log(doc.data());
console.log(doc.data().geoposition.geopoint);
console.log(doc.data().geoposition.geopoint._lat);
console.log(doc.data().geoposition.geopoint._long);
var story = {
id: doc.id,
name: doc.data().name,
pic: doc.data().imagen,
geoposition: {
Latitude: doc.data().geoposition.geopoint.latitude,
Longitude: doc.data().geoposition.geopoint.longitude
}
}
this.stories.push(story);
}
console.log("pintar marcadores");
//Pintar marcadores
this.pintarMarcadores();
});
}
pintarMarcadores(){
this.map.clear();
console.log(this.stories);
this.stories.forEach((story) => {
console.log("Add marker");
console.log(this.map);
console.log(story);
var marker=this.map.addMarkerSync({
title: story.name,
icon: { url : story.pic ,size: {
width: 40,
height: 40
}},
id:story.id,
position: new LatLng(story.geoposition.Latitude,story.geoposition.Longitude),
animation: GoogleMapsAnimation.BOUNCE,
draggable:true
});
marker.on(GoogleMapsEvent.INFO_CLICK).subscribe((params: any) => {
console.log(params);
let marker: Marker = <Marker>params[1];
this.router.navigateByUrl('/stories/'+marker.get("id"));
});
});
}
}
Any idea why my app is closing without any reason?
I realized that when I have to add multiple markers in different locations inside Google Maps in Ionic I have to use a cluster of markers. To add a cluster with custom markers you have to have an icons array and a data array. I created to arrays for that purpose:
var icons = [];
var data = [];
In the array storiesPint I have the information of the markers I want to add to the map. I iterated over my storiesPint array and I add the information I need in each array.
for(var i=0;i<storiesPint.length;i++)
{
var story = storiesPint[i];
icons.push({ url : story.pic ,size: {
width: 40,
height: 40
}})
data.push({
title: story.name,
icon: { url : story.pic ,size: {
width: 40,
height: 40
}},
id:story.id,
position: new LatLng(story.geoposition.Latitude,story.geoposition.Longitude),
animation: GoogleMapsAnimation.BOUNCE,
draggable:false
});
}
Finally I add the marker cluster in my google maps map and I started listening for MARKER_CLICK events in my markerCluster.
if(this.map){
var markerCluster = this.map.addMarkerClusterSync({
markers: data,
icons: icons
});
markerCluster.on(GoogleMapsEvent.MARKER_CLICK).subscribe((params) => {
let marker: Marker = params[1];
marker.setTitle(marker.get("title"));
marker.showInfoWindow();
marker.on(GoogleMapsEvent.INFO_CLICK).subscribe((params: any) => {
let marker: Marker = <Marker>params[1];
this.router.navigateByUrl('/stories/detail/'+marker.get("id"));
});
});
}
else{
console.log("Map is null");
}
With this solution the app is not crashing anymore.
I have found on android that using geolocation from Google Maps JavaScript API and from the Ionic Framework has caused issues of being very slow or not working at all. I then found a second geolocation module for Ionic called LocationService, from this documentation: https://github.com/ionic-team/ionic-native-google-maps/blob/master/documents/locationservice/README.md. The issue that I am having is although this Location module is working for Android, it is not working on ios or by serving the app on localhost. Below is the error that I am getting and my code that sets the map's center. I am editing this.
map.html:
<ion-header>
<ion-navbar>
<ion-title>
Map
</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<div id='map'></div>
</ion-content>
map.ts:
import { Component, ViewChild, ElementRef } from '#angular/core';
import { NavController, Platform, Navbar } from 'ionic-angular';
import { Geolocation } from '#ionic-native/geolocation';
import {
LocationService,
GoogleMap,
GoogleMapOptions,
MyLocation,
GoogleMaps
} from '#ionic-native/google-maps';
declare var google: any;
#Component({
selector: 'page-map',
templateUrl: 'map.html',
})
export class OfficeLocatorPage {
#ViewChild(Navbar) navBar: Navbar;
map: any;
mapOptions:any;
trafficEnabled = false;
transitEnabled = false;
bicycleEnabled = false;
markers = [];
places = [];
trafficLayer = new google.maps.TrafficLayer();
transitLayer = new google.maps.TransitLayer();
bicycleLayer = new google.maps.BicyclingLayer();
myLocation: any;
infoWindow: any;
isInfoWindowShown: boolean = false;
constructor(private navCtrl: NavController, private platform: Platform, private geolocation: Geolocation) {
}
ionViewDidLoad() {
this.navBar.backButtonClick = (e:UIEvent)=>{
this.navCtrl.pop({animate: true, animation: "transition", direction: "left", duration: 300});
};
}
ionViewDidEnter() {
this.platform.ready().then(() => {
this.places = [];
this.initMap(this);
});
}
initMap(scopeObj) {
LocationService.getMyLocation({enableHighAccuracy: true}).then((location: MyLocation) => {
this.setLocation(location.latLng);
}).catch((error: any) => {
// Can not get location, permission refused, and so on...
console.log(error);
});
}
setLocation(location) {
this.map = new google.maps.Map(document.getElementById('map'), {
center: location,
zoom: 10,
disableDefaultUI: true
});
let image = {
url: "assets/icon/blue_dot.png", // url
scaledSize: new google.maps.Size(25, 33), // scaled size
origin: new google.maps.Point(0,0), // origin
anchor: new google.maps.Point(0, 0) // ancho
};
let marker = new google.maps.Marker({
position: location,
map: this.map,
icon: image
});
this.myLocation = new google.maps.LatLng(location.lat, location.lng);
}
Error log:
Error: Uncaught (in promise): TypeError: Cannot read property 'LocationService' of null
TypeError: Cannot read property 'LocationService' of null
at http://localhost:8100/build/vendor.js:78338:35
at new t (http://localhost:8100/build/polyfills.js:3:21506)
at Function.LocationService.getMyLocation
The error seems to only show when serving the app in a web browser, Xcode just needed to be cleaned and rebuilt, so the map shows up and works now. I don't believe that it will work on the browser when running the command "ionic serve", because LocationService is an ionic native module and can't be used unless the app is installed on Android or iOS.
I am using Ionic together with the native Geolocation plugin to retrieve user position and sort a list of position by closest to the user.
The Geolocation plugin works perfectly using ionic serve or ionic lab as well as iOS devices but it does not work on Android devices (nor simulator).
What other solution can I use to retrieve longitude and latitude of the user?
I'll attach the class where I use the Geolocation plugin here.
The Location class I access has a public static variable where I store the userLocation since will be modified in more classes.
this.Location.load just uses the user position to call a method in Location class to sort the list of places.
import { Component } from '#angular/core';
import { ModalController, NavController, NavParams } from 'ionic-angular';
import { SharePopup } from '../share-popup/share-popup';
import { InAppBrowser } from 'ionic-native';
import { CamhsPage } from '../camhs-page/camhs-page';
import { Locations } from '../../providers/locations';
import { Platform } from 'ionic-angular';
import { Geolocation } from 'ionic-native';
#Component({
selector: 'contact',
templateUrl: 'contact.html'
})
export class Contact {
userPosition = [0, 0];
constructor(public navCtrl: NavController, public navParams: NavParams,
public modalCtrl: ModalController, public locations: Locations,public platform: Platform) {
}
openCamhsPage(){
this.platform.ready().then(() => {
let options = {
timeout: 10000,
enableHighAccuracy: true
};
Geolocation.getCurrentPosition(options).then((data) => {
Locations.userPosition[0] = Math.round(data.coords.latitude * 100)/100;
Locations.userPosition[1] = Math.round(data.coords.longitude * 100)/100;
// console.log("CONTACT "+Locations.userPosition);
});
});
this.locations.load();
this.navCtrl.push(CamhsPage);
console.log("CONTACT "+Locations.userPosition);
}
//Open WebPage
openPage(url) {
new InAppBrowser(url, '_system');
}
}
Prerequisite : Check whether you have switch ON your GPS service in Android.
Also it is good to have Success and Error Callbacks to identify the actual Error. Something like below :
..........
// Success callback for get geo coordinates
var onSuccess = function (data) {
Locations.userPosition[0] = Math.round(data.coords.latitude * 100)/100;
Locations.userPosition[1] = Math.round(data.coords.longitude * 100)/100;
}
var onError = function (data) {
console.log("Not Able to get Geolocation");
}
openCamhsPage(){
this.platform.ready().then(() => {
Geolocation.getCurrentPosition(onSuccess, onError, { enableHighAccuracy: true });
this.locations.load();
this.navCtrl.push(CamhsPage);
console.log("CONTACT "+Locations.userPosition);
}
.......
.......
I have this provider:
import {Injectable} from '#angular/core';
import {CommonService} from "./common-service";
import { Storage } from '#ionic/storage';
import 'rxjs/add/operator/filter';
declare var google;
#Injectable()
export class Maps{
load_map(selector:any,zoom:number,center_coords:any = {lat: this.lat ,lng: this.lng}){
this.lat = center_coords.lat;
this.lng = center_coords.lng;
return new Promise((resolve,reject) => {
let latLng = new google.maps.LatLng(center_coords.lat, center_coords.lng);
let mapOptions = {zoom: zoom};
this.map = new google.maps.Map(selector, mapOptions);
if(this.map){
return resolve(this.map);
}else{
return reject({error:"can not load maps"});
}
})
}
set_center(lat :number = this.lat,lng :number = this.lng){
let latLng = new google.maps.LatLng(lat, lng);
this.map.setCenter(latLng);
}
}
i use a factory in my app module:
{
provide: 'Maps',
useFactory: () =>
new Maps(),
deps: []
},
In my page i import the service:
#Component({
selector: 'page-report',
templateUrl: 'report.html'
})
export class ReportPage{
public map:any;
#ViewChild('reportmap') mapElement :ElementRef;
construct(#Inject('Maps') mapsfactory){
this.map = mapsfactory;//this is a new instance always
this.map.load_map(mapElement,16,{lat:123,lng:123});
this.map.set_center(234,234);
}
}
other page:
#Component({
selector: 'other-page',
templateUrl: 'other-page.html'
})
export class OtherPage{
public map:any;
#ViewChild('map') mapElement :ElementRef;//diferent input
construct(#Inject('Maps') mapsfactory){
this.map = mapsfactory;
this.map.load_map(mapElement,16,{lat:123,lng:123});
}
}
In app init works fine, but if change the center in second instance (OtherPage) using a click or other event to change center affects to ReportPage map, i try reset center to original position but not render correctly, use the factory to create two diferent instances of maps but doesn't work, this problem only appear when change center of map. elementRef are diferents id and input attribute, all is diferent between the instances! on browser it´s ok, this problem only appear in android device, i newbie using angular 2 and ionic 2, i think android optimizes maps by creating a single instance, Any idea or similar problem to resolve together?
I think in a bad solution but I could not do it; reload tab 0 (reportpage) after finish work in tab 1 (otherPage) and reload after click on tab 0.