nativescript-google-maps-sdk
nativescript-google-maps-sdk copied to clipboard
Auto-center map with multiple markers
How can I use fitBounds and google.maps.LatLngBounds()?
What I'm looking for is a way to avoid having to "manually" find the center of the map with center: new google.maps.LatLng(41.923, 12.513). Is there a way to automatically have the map centered on the 3 coordinates?
@Yamilquery i need this can you update here if you found solution
This is how I made it work, hope it helps!
Android: Once all the markers are on the map:
var builder = new com.google.android.gms.maps.model.LatLngBounds.Builder();
mapView.findMarker(function (marker) { builder.include(marker.android.getPosition()); });
var bounds = builder.build();
var cu = com.google.android.gms.maps.CameraUpdateFactory.newLatLngBounds(bounds,padding);
mapView.gMap.animateCamera(cu);
iOS:
Before start the placing the markers:
var bounds = GMSCoordinateBounds.alloc().init();
While placing the markers (in the loop):
bounds = bounds.includingCoordinate(marker[i].position);
After placing the markers:
var update = GMSCameraUpdate.fitBoundsWithPadding(bounds, padding); mapView.gMap.moveCamera(update);
@vtrend I got JS: ERROR TypeError: Cannot read property 'maps' of undefined
trying to implementing your solution...
Any suggestion?
can you share part of your code ?
Here is the reference for google maps on android as I assume is where you are having issues.
https://developers.google.com/android/reference/com/google/android/gms/maps/package-summary
@vtrend the problem is on my workstation maybe...
declare var com:any;
console.log('------------------');
console.log(com.android);
console.log(com.android.gms);
console.log('------------------');
And in my console I have...
JS: ------------------
JS: [object Object]
JS: undefined
JS: ------------------
As you can see gms
is not there.
I installed tns-platform-declarations
to have autocompletion and is super nice but gms
just doesn't appear...
I need to install something to have gms
?
Before going deeper this is what I would do.
remove node_modules and npm install, then:
tns platform remove android
tns platform add android
if that doesn't work:
npm cache clean
npm remove -g nativescript
npm remove -g nativescript (yes twice)
npm install -g nativescript
Side note, I was having some issues with android 3.0.0 and I downgraded it to 2.5.0 to make it work.
@vtrend My problem was really silly. I post it in the {N} forum and was solved.
I was doing com.android.gms
instead com.google.android.gms
.
I can implement your gist on {N}-Angular application, but I had to change some things to make it happen.
/** Platforms */
import {isAndroid, isIOS} from "platform";
/** Google Maps */
import * as mapsModule from "nativescript-google-maps-sdk";
declare var com:any;
/**
* Variable to store the mapView object
*/
private _mapView: mapsModule.MapView;
/**
* Funnction will trigger when the map is ready
* @param args
*/
onMapReady(args: any) {
this._mapView = args.object as mapsModule.MapView;
}
/**
* Do the zoom to include points
*/
private _doZoom() {
if( isAndroid ) {
let builder = new com.google.android.gms.maps.model.LatLngBounds.Builder();
this._mapView.findMarker( (marker) => {
builder.include(marker.android.getPosition());
return false;
});
let bounds = builder.build();
let padding: number = 100;
let cu = com.google.android.gms.maps.CameraUpdateFactory.newLatLngBounds(bounds, padding);
this._mapView.gMap.animateCamera(cu);
}
}
So you need to call _doZoom()
when your makers are set.
Example
I'm using the lasted {N} version... 3.0.1 Nice work!
I have no environment to test your IOS gist for now... When I have it I will put the result
Anyone know how to get this working on iOS? I can't figure out how to access GMSCoordinateBounds or GMSCameraUpdate.
You can just access directly like:
let bounds = GMSCoordinateBounds.alloc().init();
In each position:
bounds = bounds.includingCoordinate(position);
And then:
let update = GMSCameraUpdate.fitBoundsWithPadding(bounds, padding); mapView.gMap.moveCamera(update);
@vtrend solution worked(IOS) but for some reason it zooms in to maximum level when the camera position is updated.
I used setTarget instead and seems to work fine.
declare const GMSCameraUpdate: any;
//CLLocationCoordinate2DMake is available from 'tns-platform-declarations' let locationToSet = CLLocationCoordinate2DMake(latitude, longitude); let updateLocationCamera = GMSCameraUpdate.setTarget(locationToSet);
this.mapView.gMap.animateWithCameraUpdate(updateLocationCamera); or this.mapView.gMap.moveCamera(updateLocationCamera);
Which import I need for GMSCoordinateBounds or CLLocationCoordinate2DMake ? I don't find it. Thanks.
You don't need to import it, it's available for you already from this file objc!CoreLocation.d.ts which is located in tns-platform-declarations -> ios -> objc-i386
Late to the party, but if it helps anyone, to get the viewport animation smooth on iOS you can use
var update = GMSCameraUpdate.fitBoundsWithPadding(bounds, padding); mapView.gMap.animateWithCameraUpdate(update);
@dimitriospafos It sounds like you got a solution for your problem, but I was running into your original problem of the map zooming out until I realized that it's because the padding
parameter infitBoundsWithPadding
has to be a number and not an array like the default [40, 40, 40, 40]
that the example uses. My code looks like this:
let update = GMSCameraUpdate.fitBoundsWithPadding(this.iosMapBounds, 40);
this.mapView.gMap.animateWithCameraUpdate(update);
How can we control the speed of the camera movement?
The camera movement is smooth on Android, but on iOS, even after using the animateWithCameraUpdate
method, the camera moves relatively faster than on an Android.
I'm having issues with zooming to bounds on iOS. When I try the two above examples for iOS i get the message "Cannot find name CLLocationCoordinate2DMake / GMSCoordinateBounds". What am I missing?
I'm having issues with zooming to bounds on iOS. When I try the two above examples for iOS i get the message "Cannot find name CLLocationCoordinate2DMake / GMSCoordinateBounds". What am I missing?
Did you declare those variables outside of your map component? I have something like this:
declare var GMSCoordinateBounds: any;
@Component({
...
Thanks a lot @lin-brian-l ! I did not know how to work with native classes, and could not find anything about it in the docs. But how do i center on more than one marker? My code looks like this:
var bounds = GMSCoordinateBounds.alloc().init();
mapView.findMarker((marker) => {
bounds = bounds.includingCoordinate(marker.position);
return false;
});
var update = GMSCameraUpdate.fitBoundsWithPadding(bounds, padding);
mapView.gMap.moveCamera(update);
}
It centers on the maps with one makrer but not with maps with more.
Hey @Kraften, if you want to center the map around plotted markers, I suggest using bounds.includingCoordinate()
as they're plotted and not after they're plotted; to my knowledge, the mapView
's array of markers is not a public attribute, so you cannot access it. I'm doing something like this:
this.mapView.removeAllMarkers(); // only if you need to constantly plot and re-plot markers
if (isIOS) this.iosMapBounds = GMSCoordinateBounds.alloc().init(); // initialize ios map boundary
markerDataArray.forEach(markerData => {
let marker = new Marker();
marker.position = Position.positionFromLatLng(data.latitude, data.longitude);
// define marker in ios map
if (this.iosMapBounds) {
let locationToSet = CLLocationCoordinate2DMake(marker.position.ios.latitude, marker.position.ios.longitude);
this.iosMapBounds = this.iosMapBounds.includingCoordinate(locationToSet);
}
this.mapView.addMarker(marker);
});
let update = GMSCameraUpdate.fitBoundsWithPadding(this.iosMapBounds, 40);
this.mapView.gMap.animateWithCameraUpdate(update);
Thanks again for helping me @lin-brian-l. I tried to implement you suggestion but it did not work. I might be doing things a bit different than you? This is what my whole class looks like.
export class MapComponent {
@Input() public widget: MapWidget;
private mapView$ = new BehaviorSubject<MapView | null>(null);
constructor(private readonly meterMetaDataService: MeterMetaDataService) {
this.mapView$.pipe(filter(mapView => mapView != null), withLatestFrom(this.meterMetaDataService.getMeters()), first())
.subscribe(([mapView, meters]) => {
const markers = Object.keys(this.widget.settings.colors)
.filter(meterId => meters[meterId] && meters[meterId].latitude && meters[meterId].longitude)
.map(meterId => {
const mapMarker: MapMarker = {
id: meters[meterId].id,
name: meters[meterId].name,
long: meters[meterId].longitude,
lat: meters[meterId].latitude,
color: this.widget.settings.colors[meterId]
};
return mapMarker;
})
this.addMarkerToMap(mapView, markers);
this.zoomCameraToBounds(mapView);
});
}
public loadWhenMapIsReady(event: any): void {
const mapView = event.object;
// mapView.setStyle(MAP_STLE_JSON);
this.mapView$.next(mapView);
}
private zoomCameraToBounds(mapView: MapView): void {
const padding = 100;
if (isAndroid) {
const builder = new com.google.android.gms.maps.model.LatLngBounds.Builder();
mapView.findMarker((marker) => {
builder.include(marker.android.getPosition());
return false;
});
const androidBounds = builder.build();
const cameraUpdate = com.google.android.gms.maps.CameraUpdateFactory.newLatLngBounds(androidBounds, padding);
mapView.gMap.animateCamera(cameraUpdate);
} else if (isIOS) {
var iosMapBounds = GMSCoordinateBounds.alloc().init();
mapView.findMarker(marker => {
if (iosMapBounds) {
let locationToSet = CLLocationCoordinate2DMake(marker.position.ios.latitude, marker.position.ios.longitude);
iosMapBounds = iosMapBounds.includingCoordinate(locationToSet);
}
return false
})
var update = GMSCameraUpdate.fitBoundsWithPadding(iosMapBounds, padding);
mapView.gMap.animateWithCameraUpdate(update);
}
}
private addMarkerToMap(mapView: MapView, mapMarkers: Array<MapMarker>): void {
mapMarkers.map(mapMarker => {
const marker = new Marker();
marker.position = Position.positionFromLatLng(mapMarker.lat, mapMarker.long);
marker.title = mapMarker.name;
marker.color = mapMarker.color;
mapView.addMarker(marker);
});
}
}
interface MapMarker {
id: string;
name: string;
long: number;
lat: number;
color: string;
}
Hey @Kraften, it looks like you're defining the iosMapBounds
after you have plotted the marker; what went wrong when you tried adding the markers to iosMapBounds
as you add the marker to the map (like inside addMarkerToMap()
?
If i change the method addMarkerToMap()
so that i define the iosMapBounds before plotting the markers i get the map fully zoomed out with the markers in the middle but i also want it to zoom in as much as possible.
private addMarkerToMap(mapView: MapView, mapMarkers: Array<MapMarker>): void {
this.iosMapBounds = GMSCoordinateBounds.alloc().init();
mapMarkers.map(mapMarker => {
const marker = new Marker();
marker.position = Position.positionFromLatLng(mapMarker.lat, mapMarker.long);
marker.title = mapMarker.name;
marker.color = mapMarker.color;
let locationToSet = CLLocationCoordinate2DMake(marker.position.ios.latitude, marker.position.ios.longitude);
this.iosMapBounds = this.iosMapBounds.includingCoordinate(locationToSet);
mapView.addMarker(marker);
});
var update = GMSCameraUpdate.fitBoundsWithPadding(this.iosMapBounds, 40);
mapView.gMap.animateWithCameraUpdate(update);
}
This is the result:
Has anyone figured out the zooming issue? I can get it to view the bounds but it zooms all the way out. If you add minZoom it will zoom out to the minZoom, but thats still not very helpful.
edit: I am using padding but its still zoomed all the way out
let update = GMSCameraUpdate.fitBoundsWithPadding(bounds, 40);
mapView.gMap.moveCamera(update);
Has anyone figured out the zooming issue? I can get it to view the bounds but it zooms all the way out. If you add minZoom it will zoom out to the minZoom, but thats still not very helpful.
edit: I am using padding but its still zoomed all the way out
let update = GMSCameraUpdate.fitBoundsWithPadding(bounds, 40); mapView.gMap.moveCamera(update);
A workaround that "solves" this issue is the following:
const updateBounds = GMSCameraUpdate.fitBounds(boundsIOS);
this.mapView.gMap.moveCamera(updateBounds);
const updateZoom = GMSCameraUpdate.zoomTo(this.zoom);
this.mapView.gMap.moveCamera(updateZoom);
I found that I had to put the moveCamera()
call in setTimeout
for it to take effect if the map is being draw in a page being navigated to:
const update = GMSCameraUpdate.fitBounds(bounds)
setTimeout(() => {
this.mapView.gMap.moveCamera(update)
}, 100)
in Ios, if i use this method when tap a cluster to see the markers inside this, it doesn't center the map between the markers, it centers in the cluster tapped. Does anyone know how to fix it?
var bounds = GMSCoordinateBounds()
locations.forEach { (clusterItem) in
bounds = bounds.includingCoordinate(marker.position)
}
let update = GMSCameraUpdate.fit(bounds, withPadding: 80)
self.mapView.animate(with: update)
I have errors when trying to follow the examples above. NS 7.0.11 Angular CLI: 10.2.0 Node: 12.19.0 OS: darwin x64 Angular: 10.1.6 Initially I had Class constructor View cannot be invoked without 'new' I tried this soln #433 (comment) unzip map.zip and replace the same files in /node_modules/nativescript-google-maps-sdk But then I have problem in the next stage when looking to "Auto-center map with multiple markers" with bound()
ERROR in src/app/monitor/monitor.component.ts:136:50 - error TS2339: Property 'gms' does not exist on type 'typeof android'.
136 let builder = new com.google.android.gms.maps.model.LatLngBounds.Builder();
src/app/monitor/monitor.component.ts:143:41 - error TS2339: Property 'gms' does not exist on type 'typeof android'.
143 let cu = com.google.android.gms.maps.CameraUpdateFactory.newLatLngBounds(bounds, padding);
src/app/monitor/monitor.component.ts:146:32 - error TS2304: Cannot find name 'GMSCoordinateBounds'.
146 var iosMapBounds = GMSCoordinateBounds.alloc().init();
src/app/monitor/monitor.component.ts:154:26 - error TS2304: Cannot find name 'GMSCameraUpdate'.
154 var update = GMSCameraUpdate.fitBoundsWithPadding(iosMapBounds, padding);
Surely something must be very generic ... as it is seem that everyone got it passed this stage... easily
I can view map, place marker no problem except it complained 'gms' does not exist on type 'typeof android' ..
"dependencies": {
"@angular/animations": "~10.1.0",
"@angular/common": "~10.1.0",
"@angular/compiler": "~10.1.0",
"@angular/core": "~10.1.0",
"@angular/forms": "~10.1.0",
"@angular/platform-browser": "~10.1.0",
"@angular/platform-browser-dynamic": "~10.1.0",
"@angular/router": "~10.1.0",
"@nativescript/angular": "~10.1.0",
"@nativescript/background-http": "file:../ns-plugins/dist/packages/background-http",
"nativescript-google-maps-sdk": "file:./src/app/nativescript-google-maps-sdk",
"@nativescript/core": "~7.0.0",
"@nativescript/geolocation": "^7.0.0",
"@nativescript/theme": "~2.3.0",
"@nstudio/nativescript-checkbox": "^2.0.3",
"@nstudio/nativescript-snackbar": "^2.0.2",
"moment": "^2.29.1",
"moment-timezone": "^0.5.31",
"nativescript-audio": "^6.1.0",
"nativescript-background-http": "^4.2.1",
"nativescript-barcodescanner": "^4.0.1",
"nativescript-exit": "^1.0.1",
"nativescript-mediafilepicker": "^4.0.1",
"nativescript-permissions": "^1.3.9",
"nativescript-qr-generator": "^2.0.0",
"nativescript-ui-autocomplete": "^7.0.2",
"nativescript-ui-chart": "^8.0.2",
"nativescript-ui-listview": "~9.0.4",
"nativescript-ui-sidedrawer": "^9.0.3",
"nativescript-videoplayer": "^5.0.1",
"nativescript-websockets": "^1.5.3",
"reflect-metadata": "~0.1.12",
"rxjs": "^6.6.0",
"zone.js": "~0.11.1"
},
"devDependencies": {
"@angular/compiler-cli": "~10.1.0",
"@nativescript/android": "7.0.0",
"@nativescript/ios": "7.0.1",
"@nativescript/types": "~7.0.0",
"@nativescript/webpack": "~3.0.0",
"@ngtools/webpack": "~10.1.0",
"node-sass": "^4.7.1",
"tslint": "~5.19.0",
"typescript": "~3.9.0"
},
@ebizcoAU are declaring com and GMSCameraUpdate ? if not add the below two lines outside your component code:
e.g :
import { View } from "ui/core/view";
declare const com: any; declare const GMSCameraUpdate: any;
export class MonitorComponent {
}
Awesome!!! Thanks dimitriospafos - After adding .. import { View } from "tns-core-modules/ui/core/view"; declare const com: any; declare const GMSCameraUpdate: any; declare const GMSCoordinateBounds : any;
It works... great !!
@ebizcoAU Great, glad it worked!