Remove marker in google_maps_flutter - google-maps

I have added Google Maps for Flutter
i know how to add a marker as it is given clearly in there examples
MarkerOptions _options = new MarkerOptions(
position: LatLng(
driver_lat,
driver_lng,
),
infoWindowText:
const InfoWindowText('An interesting location', '*'));
Marker marker = new Marker('1', _options);
//Adding Marker
googleMapController.addMarker(_options);
And i am removing the marker like below
googleMapController.removeMarker(marker);
for adding the marker it is taking MarkerOptions object as a parameter but for removing the marker it is asking for Marker object as parameter and my removing marker code is not working.
i am getting the below error
Failed assertion: line 201 pos 12: '_markers[marker._id] == marker': is not true.

There are two ways to do this, one is via clearMarkers() Method
mapController.clearMarkers();
Another one is via targeting each marker returned by mapController.markers
mapController.markers.forEach((marker){
mapController.removeMarker(marker);
});

2020 answer:
.clearMarkers() has been deprecated as now each Marker is a Widget stored in a map. The correct way to clear all of the markers now on your Google Map is to set the state of of your marker Map to an empty map.
e.g.
...
onPressed: () {
setState(() {
gMapMarkers = {};
});
}
....

Use clearMarkers(). It will clear all markers in your map. So try googleMapController.clearMarkers();

I've came across this issue myself with the google_maps_library and the main cause of this issue '_markers[marker._id] == marker': is not true. is the fact that all GoogleMapsController methods return a Future, so this error is, let's say a concurrency issue since the method cals are async.
The correct way to add/remove a marker would be:
_testRemoveMarker() async {
Marker marker = await _mapController.addMarker(...markerOption..);
_mapController.removeMarker(marker);
}
_clearMarkersAndRead() async {
_mapController.clearMarkers().then((_) {
//TODO: add makrers as you whish;
});
}
So, if you do any operations with the markers add/remove/update, you should be sure that the previous operation that involved markers is completed.

If anyone still struggling with removing a specific marker try this method;
MarkerId id = MarkerId("Pickup");
//markers[id] = {} as Marker; clear all markers
markers.removeWhere((key, value) => key == id); //clear a specific marker

Related

Flutter update element create by ui.platformViewRegistry.registerViewFactory class

I am working with Google Map for flutter Website. As most tutorial said that, the map is displayed through ui.platformViewRegistry.registerViewFactory
below is the code example. As you see, i create the widget with the variable and tried to give the _lat and _lng from outside. But after i tried to give the value, the ui.platformViewRegistry.registerViewFactory is not triggered.
I just realize that the ui.platformViewRegistry.registerViewFactory objected to create the element so when the element was created, it will not executed again, but I cannot access the element via document.getElementById('map-canvas') either.
Anyone have idea about this?
Widget getMap(double _lat, double _lng) {
String htmlId = "map-canvas";
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
// final myLatLng = LatLng(-25.363882, 131.044922);
final mapOptions = new MapOptions()
..zoom = 8
..center = new LatLng(_lat, _lng)
..mapTypeControl = false;
final elem = DivElement()
..id = htmlId
..style.width = "100%"
..style.height = "100%"
..style.border = "none";
final map = new GMap(elem, mapOptions);
Marker(MarkerOptions()
..position = LatLng(_lat, _lng)
..map = map
..title = 'Green Energy');
return elem;
});
return HtmlElementView(
viewType: htmlId,
);
}
I found two ways to solve this problem.
It will update the whole view when you change the htmlId and reload.
Either determine how the htmlId should change or give it a new random number with every reload. String htmlId = Random().nextInt(1000).toString();
The disadvantage here is that you always reload the whole map.
This is the better solution as you dont have to reload the complete map. I also had the problem that i could change values inside the ui.platformViewRegistry.registerViewFactory function with data i give to the getMap widget.
So the first part of this solution is to create a global variable that you use inside the ui.platformViewRegistry.registerViewFactory function but update outside.
The second part is to use listener functions inside the ui.platformViewRegistry.registerViewFactory. For example map.onClick.listen((mapsMouseEvent) {}); or map.onZoomChanged.listen((_) {}); or a Stream ANY_STREAM_CONTROLLER.stream.listen((data) {}).
If you would like to add new markers when you zoom out you could do it something like this
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
map.onZoomChanged.listen((_) {
List<LatLng> marker_list = list_of_markers(map.zoom, map.center);
//Function that gives you a list of markers depending on your zoom level and center
marker_list.forEach((latlng){
Marker marker = Marker(MarkerOptions()
..position = latlng
..map = map
..title = myLatlng.toString()
..label = myLatlng.toString());
});
});
}
I hope the solution helps you

Error with Geocode API in Ionic 2

I’m using the Google Maps Geocode API to read an address and place a marker on the map. This is how I’m using it :
for(let marker of markers){
this.geocoder = new google.maps.Geocoder();
this.geocoder.geocode({
'address': marker["address"]
}, (results,status) => {
var position = new google.maps.LatLng(results[0].geometry.location.lat(),results[0].geometry.location.lng()); // error pointing to this line
this.personMarker = new google.maps.Marker({position: position, title: marker.name, markerInfo: marker, map : this.map , icon : marker.imageurl});
google.maps.event.addListener(this.personMarker, 'click', () => {
this.showCard = true;
this.org = marker.organization;
this.gig = marker.gig;
this.location = marker["address"];
this.image = marker.imageurl;
this.ngoData = marker;
this.ownerusername = marker.ownerusername;
});
});
}
I get this error: ‘Cannot read property ‘0’ of null’ pointing to the line I’ve shown with a comment in the code snippet.Just don’t know what’s causing that error because I’m accessing the ‘results’ parameter only inside the callback. console.log(results) sometimes returns 'null' for the first iteration of the loop, followed by proper results for the rest of the iterations, or proper results for all iterations. I don't understand this inconsistent behavior, but I do know that I get this error usually when the intervals between me clicking and loading the map again is short.
I know that all the addresses are valid because sometimes all my markers appear on the map and the error doesn't turn up

Google maps how to force marker on the nearest road

I am doing a vehicle traking project, i am getting coordiantes from the databases, and showing on the google maps.
here is my code.....!!
function get_coordinates(checkbox){
var v_id=checkbox.id;
if(checkbox.checked){
var hr = new XMLHttpRequest();
hr.open("POST", "fetch_coordinates.php", true);
hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
hr.onreadystatechange = function() {
if(hr.readyState == 4 && hr.status == 200) {
var data = JSON.parse(hr.responseText);
var lat=data.loc.lat;
var lon=data.loc.lon;
addmarker(lat,lon,v_id);
}
}
hr.send("id="+v_id);
} else{
var mark = markers[v_id]; // find the marker by given id
mark.setMap(null);
delete markers[v_id];
}
}
function addmarker(lat,lon,v_id){
var marker = new google.maps.Marker({
id: v_id,
position: new google.maps.LatLng(lat, lon),
zoom: 8,
map: map,
title: 'Vehicle No: '+v_id,
icon: 'live.gif',
optimized:false
});
markers[v_id] = marker;
bounds.extend(new google.maps.LatLng(lat,lon));
// map.setOptions({center:new google.maps.LatLng(lat,lon),zoom:8});
map.fitBounds(bounds);
}
But the problem is, sometimes i get GPS coordiantes which are 1,2 inches away from the road (possibly because of less precison of device or signal distortion etc)
How should I force my marker to automatically adjust on the road? Is there a some way, using direction rendering or any other hint ??? please help
In 2017 the right way to do it is the Roads API Snap to Roads service.
You can read about this web service on
https://developers.google.com/maps/documentation/roads/snap
The Google Maps Roads API takes up to 100 GPS points collected along a route, and returns a similar set of data, with the points snapped to the most likely roads the vehicle was traveling along. Optionally, you can request that the points be interpolated, resulting in a path that smoothly follows the geometry of the road.
https://developers.google.com/maps/documentation/directions/intro
You can use your desired url to get json request..
In the Coding section you can write this code to get the location on the road..
Marker liveloc;
JSONObject obj1=new JSONObject(s);
JSONArray arr1=obj1.getJSONArray("routes");
for (int i=0;i<arr1.length();i++){
JSONObject obj2=arr1.getJSONObject(i);
JSONArray arr2=obj2.getJSONArray("legs");
JSONObject obj3=arr2.getJSONObject(0);
JSONObject obj4=obj3.getJSONObject("start_location");
String k=obj4.getString("lat");
String k2=obj4.getString("lng");
double k3=Double.parseDouble(k);
double k4=Double.parseDouble(k2);
LatLng myloc=new LatLng(k3,k4);
if (liveloc !=null){
liveloc.remove();
}
liveloc=mMap.addMarker(new MarkerOptions().position(myloc));
}
You need to get the first start location from the json request..
Hope it helps...

Bring GoogleMaps InfoWindow to front

I have a GoogleMaps APIv3 application in which multiple InfoWindows can be open at any one time. I would like to be able to bring an obscured InfoWindow to the front of all other InfoWindows if any part of it is clicked - similar to the behaviour of windows in MS Windows OS.
I had thought to add an onclick event handler which increases the z-index of the InfoWindow, but the event handler does not appear to be firing.
ZIndex is a global variable that keeps increasing as InfoWindows are clicked - or thats the theory anyway.
Can anyone help ?
Here is my code:-
var ZIndex=1;
var iw = new google.maps.InfoWindow({ content:contentString });
google.maps.event.addListener(iw, 'click', handleInfoWindowClick(iw) );
function handleInfoWindowClick(infoWindow) {
return function() {
infoWindow.setZIndex(ZIndex++);
}
}
there is no click-event for an infoWindow, it's a little bit more difficult.
you'll need to use an element(not a string) as content for the infowindow, because you need a DOMListener instead a listener for the infowindow-object
when domready-fires, you must apply the click-DOMListener to the anchestor of this content-node that defines the infowindow
The following code will do this for you, add this to your page:
google.maps.InfoWindowZ=function(opts){
var GM = google.maps,
GE = GM.event,
iw = new GM.InfoWindow(),
ce;
if(!GM.InfoWindowZZ){
GM.InfoWindowZZ=Number(GM.Marker.MAX_ZINDEX);
}
GE.addListener(iw,'content_changed',function(){
if(typeof this.getContent()=='string'){
var n=document.createElement('div');
n.innerHTML=this.getContent();
this.setContent(n);
return;
}
GE.addListener(this,'domready',
function(){
var _this=this;
_this.setZIndex(++GM.InfoWindowZZ);
if(ce){
GM.event.removeListener(ce);
}
ce=GE.addDomListener(this.getContent().parentNode
.parentNode.parentNode,'click',
function(){
_this.setZIndex(++GM.InfoWindowZZ);
});
})
});
if(opts)iw.setOptions(opts);
return iw;
}
Instead of google.maps.InfoWindow() you must call now google.maps.InfoWindowZ()
It also returns a genuine InfoWindow, but with the mentioned listener applied to it. It also creates the node from the content when needed.
Demo: http://jsfiddle.net/doktormolle/tRwnE/
Updated version for visualRefresh(using mouseover instead of click) http://jsfiddle.net/doktormolle/uuLBb/

Google Maps v3 OverlayView.getProjection()

I cannot seem to figure out why the object returned by getProjection() is undefined. Here is my code:
// Handles the completion of the rectangle
var ne = recBounds.getNorthEast();
var sw = recBounds.getSouthWest();
$("#map_tools_selat").attr( 'value', sw.lat() );
$("#map_tools_nwlat").attr( 'value', ne.lat() );
$("#map_tools_selng").attr( 'value', ne.lng() );
$("#map_tools_nwlng").attr( 'value', sw.lng() );
// Set Zoom Level
$("#map_tools_zoomlevel").attr( 'value', HAR.map.getZoom()+1 );
document.getElementById("map_tools_centerLat").value = HAR.map.getCenter().lat();
document.getElementById("map_tools_centerLong").value = HAR.map.getCenter().lng();
// All this junk below is for getting pixel coordinates for a lat/lng =/
MyOverlay.prototype = new google.maps.OverlayView();
MyOverlay.prototype.onAdd = function() { }
MyOverlay.prototype.onRemove = function() { }
MyOverlay.prototype.draw = function() { }
function MyOverlay(map) { this.setMap(map); }
var overlay = new MyOverlay(HAR.map);
var projection = overlay.getProjection();
// END - all the junk
var p = projection.fromLatLngToContainerPixel(recBounds.getCenter());
alert(p.x+", "+p.y);
My error is: Cannot call method 'fromLatLngToContainerPixel' of undefined
Actually, i the reason why this happens is because the projection object is created after the map is idle after panning / zooming. So, a better solution is to listen on the idle event of the google.maps.Map object, and get a reference to the projection there:
// Create your map and overlay
var map;
MyOverlay.prototype = new google.maps.OverlayView();
MyOverlay.prototype.onAdd = function() { }
MyOverlay.prototype.onRemove = function() { }
MyOverlay.prototype.draw = function() { }
function MyOverlay(map) { this.setMap(map); }
var overlay = new MyOverlay(map);
var projection;
// Wait for idle map
google.maps.event.addListener(map, 'idle', function() {
// Get projection
projection = overlay.getProjection();
})
I kind of figured out what was going on. Even though it is still not crystal clear why this happens, I know that I had to instantiate the variable "overlay" right after instantiating my google map (HAR.map). So I practically moved that code snippet into my HAR class and now i use:
HAR.canvassOverlay.getProjection().fromLatLngToContainerPixel( recBounds.getCenter() );
So now, every time I create a map via my class "HAR" I also have a parallel OverlayView object within my class.
The Error could have been with losing scope of my class object, but I think it was more of the map event "projection_changed" not being fired. I got a hint from the map API docs for map class, under method getProjection():
"Returns the current Projection. If the map is not yet initialized (i.e. the mapType is still null) then the result is null. Listen to projection_changed and check its value to ensure it is not null."
If you are getting the similar issue, make sure that you assign your overlayView.setMAP( YOUR_MAP_OBJECT ) closely after instantiating the map object.