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.
Related
I am trying to get navigation directions using mapbox-sdk for react-native from npm package:
"#mapbox/mapbox-sdk": "^0.11.0"
And for rendering the directions returned by mapbox-sdk I am using the below npm package:
"#react-native-mapbox-gl/maps": "^8.1.0-rc.8",
Code I am using for retrieving directions:
import MapboxGL from '#react-native-mapbox-gl/maps'
// Mapbox SDK related package
import MapboxDirectionsFactory from '#mapbox/mapbox-sdk/services/directions'
import { lineString as makeLineString } from '#turf/helpers'
import GeoLocationService from '../../services/geolocation/GeoLocationService';
import GeoLocationCore from '#react-native-community/geolocation'
const accessToken = "ACESS_TOKEN_FROM_MAPBOX_API_DASHBOARD"
const directionsClient = MapboxDirectionsFactory({accessToken})
constructor(props) {
super(props);
this.state = {
longitude: 0,
latitude: 0,
orderLongitude: 0,
orderLatitude: 0,
route: null,
};
}
async componentDidMount() {
const {route} = this.props
// Lets say route.params contains the below object:
// { "longitude": "33.981982", "latitude": "-6.851599"}
console.log("Params from other screen: ", route.params)
MapboxGL.setAccessToken(accessToken)
MapboxGL.setConnected(true);
MapboxGL.setTelemetryEnabled(true);
const permission = await MapboxGL.requestAndroidLocationPermissions();
let latitude, longitude;
if(Platform.OS == "android") {
GeoLocationService.requestLocationPermission().then(() => {
GeoLocationCore.getCurrentPosition(
info => {
const { coords } = info
latitude = coords.latitude
longitude = coords.longitude
//this.setState({longitude: coords.longitude, latitude: coords.latitude})
this.setState({longitude: -6.873795, latitude: 33.990777, orderLongitude: route.params.longitude, orderLatitude: route.params.latitude})
console.log("your lon: ", longitude)
console.log("your lat", latitude)
this.getDirections([-6.873795, 33.990777], [route.params.longitude, route.params.latitude])
},
error => console.log(error),
{
enableHighAccuracy: false,
//timeout: 2000,
maximumAge: 3600000
}
)
})
}
}
getDirections = async (startLoc, destLoc) => {
const reqOptions = {
waypoints: [
{coordinates: startLoc},
{coordinates: destLoc},
],
profile: 'driving',
geometries: 'geojson',
};
const res = await directionsClient.getDirections(reqOptions).send()
//const route = makeLineString(res.body.routes[0].geometry.coordinates)
const route = makeLineString(res.body.routes[0].geometry.coordinates)
console.log("Route: ", JSON.stringify(route))
this.setState({route: route})
}
Code I am using for rendering road directions fetched by mapbox-sdk:
renderRoadDirections = () => {
const { route } = this.state
return route ? (
<MapboxGL.ShapeSource id="routeSource" shape={route.geometry}>
<MapboxGL.LineLayer id="routeFill" aboveLayerID="customerAnnotation" style={{lineColor: "#ff8109", lineWidth: 3.2, lineCap: MapboxGL.LineJoin.Round, lineOpacity: 1.84}} />
</MapboxGL.ShapeSource>
) : null;
};
Code I am using for rendering map and directions:
render() {
return (
<View style={{ flex: 1 }}>
<MapboxGL.MapView
ref={(c) => this._map = c}
style={{flex: 1, zIndex: -10}}
styleURL={MapboxGL.StyleURL.Street}
zoomLevel={10}
showUserLocation={true}
userTrackingMode={1}
centerCoordinate={[this.state.longitude, this.state.latitude]}
logoEnabled={true}
>
{this.renderRoadDirections()}
<MapboxGL.Camera
zoomLevel={10}
centerCoordinate={[this.state.longitude, this.state.latitude]}
animationMode="flyTo"
animationDuration={1200}
/>
</MapboxGL.MapView>
</View>
)
}
Now when I try to render the GeoJson retreived the road directions line not showing on map, so I thought maybe something wrong with my GeoJson and tested it from here but it looks fine:
https://geojsonlint.com/
GeoJson that I tested and looks okay:
{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-6.880611,33.9916],[-6.882194,33.990166],[-6.882439,33.99015],[-6.882492,33.990028],[-6.882405,33.98991],[-6.878006,33.990299],[-6.87153,33.990978],[-6.871386,33.990925],[-6.871235,33.991016],[-6.869793,33.991165],[-6.870523,33.990292]]}}
Example of what I am trying to achieve:
What could be wrong in my code that makes road directions line not showing on map?
Found what was causing the <LineLayer/> not showing on map, removing the the attribute aboveLayerID from the following line:
<MapboxGL.LineLayer id="routeFill" aboveLayerID="customerAnnotation" style={{lineColor: "#ff8109", lineWidth: 3.2, lineCap: MapboxGL.LineJoin.Round, lineOpacity: 1.84}} />
So it becomes:
<MapboxGL.LineLayer id="routeFill" style={{lineColor: "#ff8109", lineWidth: 3.2, lineCap: MapboxGL.LineJoin.Round, lineOpacity: 1.84}} />
Result:
I am trying to display Google Maps in my Ionic app & center on my current location.
When I run this on my laptop, it displays as expected.
But when I try to run it on my mobile device, the map isn't rendering.
Here is the code:
ngAfterViewInit() {
this.locateUser();
console.log('My Coords', this.coordinates);
this.getGoogleMaps().then(googleMaps => {
const mapEl = this.mapElementRef.nativeElement;
const map = new googleMaps.Map(mapEl, {
center: this.coordinates,
zoom: 16
});
googleMaps.event.addListenerOnce(map, 'idle', () => {
this.renderer.addClass(mapEl, 'visible');
});
}).catch(err => {
console.log('Google Maps error:', err);
});
}
private getGoogleMaps() {
const win = window as any;
const googleModule = win.google;
if (googleModule && googleModule.maps) {
return Promise.resolve(googleModule.maps);
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://maps.googleapis.com/maps/api/js?key=myAPIKey';
script.async = true;
script.defer = true;
document.body.appendChild(script);
script.onload = () => {
const loadedGoogleModule = win.google;
if (loadedGoogleModule && loadedGoogleModule.maps) {
resolve(loadedGoogleModule.maps);
} else {
reject('Google Maps SDK not available.');
}
};
});
}
private locateUser() {
if (!Capacitor.isPluginAvailable('Geolocation')) {
this.showErrorAlert();
return;
}
Plugins.Geolocation.getCurrentPosition()
.then(geoPosition => {
this.coordinates = {
lat: geoPosition.coords.latitude,
lng: geoPosition.coords.longitude
};
console.log(this.coordinates);
})
.catch(err => {
this.showErrorAlert();
});
}
Can someone please tell me why this is working on my laptop, but not on my mobile?
Also, here are the steps I'm taking to run on the mobile:
ionic build
ionic capacitor run android
Then I run the app in Android Studio.
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?
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 have created the basic ionic app which can launch camera. I installed all the required plugins and also i have used the right version of cordova . I am not getting any errors and also able to create apk for that . When using in apk in the android mobile, camera is not getting launched.
this is the home.html code adding camera module
<button ion-button (click)="takePhoto()">camera</button>
<p align="center"><img src="{{ myphoto }}"></p>
this is the home.ts file
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { Camera, CameraOptions } from '#ionic-native/camera';
import { Database } from '../../providers/database/database';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
tabBarElement: any;
splash = true;
myphoto:any;
public hasTrees : boolean = false;
public trees : any;
constructor(public navCtrl: NavController, private camera:Camera,
public DB : Database) {
this.tabBarElement = document.querySelector('.tabbar');
}
takePhoto(){
const options: CameraOptions = {
quality: 70,
destinationType: this.camera.DestinationType.DATA_URL,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE
}
this.camera.getPicture(options).then((imageData) => {
// imageData is either a base64 encoded string or a file URI
// If it's base64:
this.myphoto = 'data:image/jpeg;base64,' + imageData;
}, (err) => {
// Handle error
});
}
ionViewDidLoad() {
this.tabBarElement.style.display = 'none';
setTimeout(() => {
this.splash = false;
this.tabBarElement.style.display = 'flex';
}, 4000);
}
ionViewWillEnter()
{
this.displayTrees();
}
displayTrees()
{
this.DB.retrieveTrees().then((data)=>
{
let existingData = Object.keys(data).length;
if(existingData !== 0)
{
this.hasTrees = true;
this.trees = data;
}
else
{
console.log("we get nada!");
}
});
}
addSpecies()
{
this.navCtrl.push('Add');
}
viewSpecies(param)
{
this.navCtrl.push('Add', param);
}
}