Flutter Maps: How to change Marker Icon on marker tap - google-maps

I'm trying to find a solution for changing the marker icon when the marker is tapped, in order to give visual feedback. Still, I haven't figured out how to do this. The first thought was accessing the marker by index but since markers are arranged in a Set<Markers> there is no change to access it in a proper way. then it would be easy to just exchange the old marker by the new one. Is there a common way to do this?
Edit:
Using Java, there are options like. setIcon for markers. This is not the chase for Flutter.
like so:
marker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.ic_selected_user_mark_icon));
Happy coding :)

I had the same problem. Google Maps API doesn't have this option. I resolved it manually.
You can create a flag and change the icon Marker in the Marker's onTap and GoogleMap's onTap properties.
int markerFlag = null;
List<Marker> _allMarkers = [];
Marker's onTap:
onTap: () {
if (markerFlag != idMarker) {
setState(() {
if (markerFlag != null) {
_allMarkers[markerFlag] = _allMarkers[markerFlag].copyWith(iconParam: defaultMarkerIcon);
}
_allMarkers[idMarker] = _allMarkers[idMarker].copyWith(iconParam: selectedMarkerIcon);
markerFlag = idMarker;
});
}
}
GoogleMap's onTap:
onTap: (value){
if (markerFlag != null) {
setState(() {
_allMarkers[markerFlag] = _allMarkers[markerFlag].copyWith(iconParam: defaultMarkerIcon);
markerFlag = null;
});
}
}

//onTap Marker:
widget.markersStation.add(
Marker(
markerId: MarkerId(markerId),
position: latLng,
icon: iconMarkerStationGrey,
onTap: () {
setState(() {
for (var j = 0; j < widget.markersStation.length; j++) {
widget.markersStation[j] = widget.markersStation[j]
.copyWith(iconParam: iconMarkerStationGrey);
}
if (idMarkerSelected != int.parse(markerId)) {
widget.markersStation[int.parse(markerId)] = widget
.markersStation[int.parse(markerId)]
.copyWith(iconParam: iconMarkerStationSelected);
idMarkerSelected = int.parse(markerId);
}
});
),)
//onTap Map :
markers: widget.markersStation.toSet(),
onTap: (position) async {
_customInfoWindowController.hideInfoWindow!();
BitmapDescriptor iconMarkerStationGrey =
await BitmapDescriptor.fromAssetImage(
const ImageConfiguration(size: Size(24, 24)),
"icons/marker_station_gris_17677#2x.png");
if (idMarkerSelected > -1) {
setState(() {
widget.markersStation[idMarkerSelected] = widget
.markersStation[idMarkerSelected]
.copyWith(iconParam: iconMarkerStationGrey);
idMarkerSelected = -1;
});
}
},

Related

Google Maps Marker Clusterer not showing cluster icons

I have a Google Map with a set of markers on. I'm trying to add MarkerClusterer to it to simplify the view. Overall, doing so should be pretty easy from what I've seen online but when I try it, my map doesn't show any markers, clusters or anything. My code is below:
function calculateCenter() {
center = map.getCenter()
}
let center
let worldCenter = new window.google.maps.LatLng(32.249974, 5.800781)
let mapOptions = {
center: worldCenter,
zoom: 3,
scrollwheel: false
}
let map = new google.maps.Map(document.querySelector('.banner--map--parks'), mapOptions)
let infoWindow = new google.maps.InfoWindow()
let markers = []
map.addListener('idle', _ => {
calculateCenter()
})
fetch('/api/parks.json')
.then(response => response.json())
.then(data => {
data.forEach(park => {
if (park.lat && park.lng) {
let title = park.name
let position = new google.maps.LatLng(park.lat, park.lng)
let icon = 'https://raw.githubusercontent.com/Concept211/Google-Maps-Markers/master/images/marker_blue.png'
let marker = new google.maps.Marker({
position: position
title: title,
icon: icon,
// map: map
})
let parkIWContent = '<div class="iw">' + park.name + '</div>'
marker.addListener('click', (function(marker, parkIWContent) {
return function() {
infoWindow.setContent(parkIWContent);
infoWindow.open(map, marker);
// map.panTo(new google.maps.LatLng(park.lat, park.lng))
};
})(marker, parkIWContent))
markers.push(marker)
}
})
})
console.log(markers)
let markerCluster = new MarkerClusterer(map, markers)
map.addListener("click", _ => {
infoWindow.close()
})
As you can see, at one point I did output the array of markers and they do all show up in the console but still fail to show on the map.
Can anyone spot any issues with the code that may be causing this?
Thanks
I've actually managed to solve this. I needed to move the Marker Clusterer inside the .then in order for it to work.

Flutter draw multiple polyline colors

I have a number of trips with each having a list of positions.
I need assign a color for each trip but what's happening is that as the lines are being drawn, instead of changing the new color for each trip, it changes for all polylines already drawn.
How can I do it for each trip?
getTrips() async {
await Future.forEach(trips, (element) async {
List positions = element['positions'] as List;
await Future.forEach(positions, (element) async {
latitude = element['coordinates'][0];
longitude = element['coordinates'][1];
direction = element['direction'] ?? 0;
//Current position
position = LatLng(latitude ?? 0, longitude ?? 0);
positionList.add(position);
addMarker();
});
});}
addMarker() async {
var m = markers.firstWhere((p) => p.markerId == MarkerId(equipmentId), orElse: () => null);
if (markers.isNotEmpty) {
markers.remove(m);
}
selectedMarker = MarkerId(equipmentId);
markers
..add(Marker(
visible: true,
markerId: selectedMarker,
position: position,
icon: customIcon,
anchor: Offset(0.5, 0.5),
rotation: direction,
));
_polyline.add(Polyline(
polylineId: PolylineId(_polylineIdCounter.toString()),
visible: true,
width: 6,
points: positionList,
color: tripColors[++colorIndex % tripColors.length],
));
_polylineIdCounter++;
cPosition = CameraPosition(
zoom: cameraZoom,
target: LatLng(position.latitude, position.longitude),
);
if (mounted) setState(() {});
_animateCamera(cPosition);
}
I know it's been a while but just in case there's someone out there who needs this then here you go.
Theoretically, the polyline property is a "Set{}" which means you can have more than one polyline, so for each trip create a different polyline with a different color and add all of them to the set.
I believe that should solve it.

Google Maps API Implementing click event to get the target element

Here's a question regarding Google maps API events:
marker.addListener('click', _.bind(function (e) {
}
In the above code I am unable to get e.target element on which I need to call a function.
Basically the situation is I have a list of markers and on click of one, the function needs to be triggered and on click of the same marker the function should unbind.
Can anyone help me with this?
Hello I worked with marker in past and kinda had same issue, I did some research and found this solution on SO and it worked.
you can created function like this
function createCallback(marker, callback) {
google.maps.event.addListener(marker, 'click', function () {
// do whatever you want to do
callback()
});
}
And after that call it like this
var markerPropertyLocation = new google.maps.Marker({
position: {markerPosition},
map : {map},
icon: new google.maps.MarkerImage(
{iconImage},
null, /* size is determined at runtime */
null, /* origin is 0,0 */
null, /* anchor is bottom center of the scaled image */
new google.maps.Size(20, 27))
});
createCallback(markerPropertyLocation, function(){
// callback funcation
});
Thanks
To be more precise with my requirements, I have restructured my queries and reposting the same:
On our Google maps implementation we have the following Requirements:
• Getting the target element that is clicked marker, out of all displayed on the screen
• Enlarge the marker once clicked and apply class only to that specific marker(clicked marker)
We have written the following code, but it doesn’t give the above results.
However, a similar interaction works on hover showing the tootip with details
Code Snippet:
ReferenceMap.prototype.showPoint = function showPoint(point, map) {
var location = point.get('location')
, marker = new google.maps.Marker({
store_id: point.get('internalid')
, icon: iconSrc
, map: map
, point: point
, title: point.get('internalid')
});
allMarker.push(marker);
marker.setPosition(new google.maps.LatLng(location.latitude, location.longitude));
marker.setVisible(true);
marker.addListener('mouseover', _.bind(function () {
this.showInfoWindowOnClick(marker, map);
}, this));
marker.addListener('mouseout', _.bind(function () {
hideShowInfoWindow();
}, this));
marker.addListener('click', _.bind(function (e) {
// var markerTitle = marker.title;
// var markerID = marker.store_id;
// var target = markerTitle == markerID;
// if(target == e.target) {
// $('.marker img').css('width',200);
// dealerDetailsLeftBlock();
// }
// else {
// $('.marker img').removeAttr('style');
// }
console.log(e.target);
areaMarkers.reset();
for (var i = 0; i < allMarker.length; i++) {
allMarker[i
].isClicked = 'F';
allMarker[i
].point.set('isClicked', 'F');
marker.set('isClicked', 'T');
if (map.getBounds().contains(allMarker[i
].getPosition())) {
//console.log(allMarker[i]);
if (allMarker[i
].isClicked == 'T') {
$('[title="' + marker.title + '"
]').addClass('marker - design');
console.log($('[title="' + marker.title + '"
]'))
// console.log(allMarker[i]);
allMarker[i
].point.set('isClicked', 'T');
}
areaMarkers.add(allMarker[i
].point);
}
};
//console.log(areaMarkers);
dealerDetailsLeftBlock();
this.trigger('getSideBar')
}, this));
if (this.markerCluster) {
this.markerCluster.addMarker(marker);
}
return marker;
};

Use Google Places Library to show all location markers for a predetermined keyword with Google Maps and Ionic

I want to display a marker for each Place search result on my map but it doesn't work.
---EDIT---
I have tried to debug figure out what I am missing and why the markers are not showing up, but I am not able to figure it out. Even if the answer is very obvious, I might not be able to see it with what I currently have. I had edited the code a little bit with the setMarker() function and the options of the marker to be displayed on the map created, but that does not seem to be the answer to why the markers wont show up. I shouldn't have to create markers at specific Latitudes and Longitudes but instead search the area around the current location using a predetermined search keyword which in this case would be "McDonalds" (this search keyword is being used for testing).
map.html:
<ion-header>
<ion-navbar>
<ion-title>
Map
</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<div id='map'>
</div>
<ion-fab bottom right id="locate">
<button ion-fab
(click)="locationClick()" color="white"><ion-icon ios="ios-locate-outline" md ="md-locate" color="primary"></ion-icon></button>
</ion-fab>
<ion-fab top left id="compass-fab">
<button ion-fab mini (click)="compassClick()" color="white" id="compass"></button>
</ion-fab>
<ion-fab top right id="layers">
<button ion-fab mini id="layers-button" color="white"><ion-icon name="SMAPP-layers"></ion-icon></button>
<ion-fab-list side="bottom">
<button ion-fab mini (click)="trafficClick()" id="traffic" color="white"></button>
<button ion-fab mini (click)="transitClick()" id="transit" color="white"></button>
<button ion-fab mini (click)="bicycleClick()" id="bicycle" color="white"></button>
</ion-fab-list>
</ion-fab>
</ion-content>
map.ts:
declare var google: any;
#Component({
selector: 'page-map',
templateUrl: 'map.html',
})
export class OfficeLocatorPage {
#ViewChild(Navbar) navBar: Navbar;
#ViewChild('map') mapElement: ElementRef;
map: any;
mapOptions:any;
infowindow: any;
trafficEnabled = false;
transitEnabled = false;
bicycleEnabled = false;
markers = [];
trafficLayer = new google.maps.TrafficLayer();
transitLayer = new google.maps.TransitLayer();
bicycleLayer = new google.maps.BicyclingLayer();
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.loadMap();
});
}
locationClick() {
this.geolocation.getCurrentPosition({ maximumAge: 3000, timeout: 5000, enableHighAccuracy: true }).then((resp) => {
let myLocation = new google.maps.LatLng(resp.coords.latitude,resp.coords.longitude);
this.map.setCenter(myLocation);
});
}
compassClick() {
this.map.animateCamera({
target: this.map.getCameraTarget(),
tilt: 0,
bearing: 0,
duration: 1000
});
}
trafficClick() {
if (this.transitEnabled == true) {
this.transitClick();
this.trafficEnabled = true;
this.trafficLayer.setMap(this.map);
} else if (this.trafficEnabled == false) {
this.trafficEnabled = true;
this.trafficLayer.setMap(this.map);
} else {
this.trafficEnabled = false;
this.trafficLayer.setMap(null);
}
}
transitClick() {
if (this.trafficEnabled == true) {
this.trafficClick();
this.transitEnabled = true;
this.transitLayer.setMap(this.map);
} else if (this.transitEnabled == false) {
this.transitEnabled = true;
this.transitLayer.setMap(this.map);
} else {
this.transitEnabled = false;
this.transitLayer.setMap(null);
}
}
bicycleClick() {
this.bicycleEnabled = !this.bicycleEnabled;
if (this.bicycleEnabled) {
this.bicycleLayer.setMap(this.map);
} else {
this.bicycleLayer.setMap(null);
}
}
loadMap() {
this.geolocation.getCurrentPosition({ maximumAge: 3000, timeout: 5000, enableHighAccuracy: true }).then((resp) => {
let myLocation = new google.maps.LatLng(resp.coords.latitude,resp.coords.longitude);
this.map = new google.maps.Map(document.getElementById('map'), {
zoom: 10,
center: myLocation,
disableDefaultUI: true
});
});
let watch = this.geolocation.watchPosition();
watch.subscribe((data) => {
this.deleteMarkers();
let updatelocation = new google.maps.LatLng(data.coords.latitude,data.coords.longitude);
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
};
this.addMarker(updatelocation, image);
this.setMapOnAll(this.map);
});
var request = {
query: 'McDonalds',
fields: ['photos', 'formatted_address', 'name', 'rating', 'opening_hours', 'geometry'],
};
let service = new google.maps.places.PlacesService(this.map);
service.findPlaceFromQuery(request, callback);
function callback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (let i = 0; i < results.length; i++) {
let placeLoc = results[i].geometry.location;
this.addMarker(placeLoc, 'red');
}
}
this.setMapOnAll(this.map);
}
}
addMarker(location, image) {
let marker = new google.maps.Marker({
position: location,
map: this.map,
icon: image
});
this.markers.push(marker);
}
setMapOnAll(map) {
for (var i = 0; i < this.markers.length; i++) {
this.markers[i].setMap(map);
}
}
clearMarkers() {
this.setMapOnAll(null);
}
deleteMarkers() {
this.clearMarkers();
this.markers = [];
}
}
I would recommend you to you use setMap(map);. There is a good example that you will utilize from. I used it couple month before and works perfect. Hope it helps.
I have figured out that the places library was not being accessed correctly but is now, I have also updated the code to show what needs to be done to gather information using the places function, findPlaceByQuery(). The Google JavaScript API Documentation is not accurate in how to display search results based on a query. Here is the updated code on how to do it, once you have the Places Library set up correctly in the script tag to be used (I have also included the code on how to correctly display the markers, scopeObj is the class object passed into the loadMap() function):
map.ts:
var request ={
locationBias: this.myLocation,
query: "McDonalds",
fields: ['photos', 'formatted_address', 'name', 'rating', 'opening_hours', 'geometry']
};
var callbackCount = 0;
function callback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (let i = 0; i < results.length; i++) {
let placeLoc = results[i].geometry.location;
scopeObj.addMarker(placeLoc, 'red');
}
}
callbackCount++;
};
let service = new google.maps.places.PlacesService(this.map);
service.findPlaceFromQuery(request, callback);
continueExec();
function continueExec() {
if (callbackCount < 1) {
setTimeout(continueExec, 1000);
return;
}
scopeObj.setMapOnAll(this.map);
}
}
addMarker(location, image) {
let marker = new google.maps.Marker({
position: location,
map: this.map,
icon: image
});
this.markers.push(marker);
}
setMapOnAll(map) {
for (var i = 0; i < this.markers.length; i++) {
var marker = new google.maps.Marker({
position: this.markers[i].getPosition(),
map: this.map,
title: 'Hello World!'
});
}
}
Places API script to be added into index.html above all other script tags in the tag:
<script src="https://maps.googleapis.com/maps/api/js?v=3&key=....&libraries=places" type="text/javascript"></script>

Filter google markers with knockout

I've been trying to solve this issues with no luck. I already check other posts and no luck. I think I have an error on my code, my goal is to be able to filter the list and show/hide only the markers on that list. A sample of the code is here: https://jsfiddle.net/rp2t3gyn/2/
Here is a sample of the code that is not working for some reason:
self.filteredPlaces = ko.computed(function() {
var filter = self.filter().toLowerCase();
if (!filter) {
ko.utils.arrayForEach(self.placeList(), function (placeItem) {
placeItem.marker.setVisible(true);
});
return self.placeList();
} else {
return ko.utils.arrayFilter(self.placeList(), function(placeItem) {
// set all markers visible (false)
var result = (placeItem.city.toLowerCase().search(filter) >= 0);
placeItem.marker.setVisible(result);
return result;
});
}
}, this);
Thanks
In order to filter the markers you need to do a few things.
Your first problem is this line:
placeItem.marker.setVisible(true);
Place item doesn't have a marker object based on your constructor. So, you have to add it. I changed the Place constructor to add a marker object (see below).
var Place = function(data, map, viewmodel) {
this.city = data.city;
this.lat = data.lat;
this.lng = data.lng;
var marker = new google.maps.Marker({
map: map,
position: {lat: data.lat, lng: data.lng},
city: data.city,
icon: {
path: google.maps.SymbolPath.BACKWARD_CLOSED_ARROW,
scale: 5
},
animation: google.maps.Animation.DROP,
});
marker.addListener('click', function() {
viewmodel.clickSelection(marker, viewmodel.largeInfoWindow);
});
this.marker = marker;
};
and used that to initialize your objects. Lastly, I changed your filteredPlaces function, it needs to subscribe to the query observable so that when you type in text the markers on the map adjust accordingly .
self.query.subscribe(function() {
var filter = self.query().toLowerCase();
if (!filter) {
ko.utils.arrayForEach(self.placeList(), function (placeItem) {
placeItem.marker.setMap(map);
});
return self.placeList();
} else {
ko.utils.arrayForEach(self.placeList(), function(placeItem) {
var result = (placeItem.city.toLowerCase().search(filter) >= 0);
if(result)
placeItem.marker.setMap(map);
else
placeItem.marker.setMap(null);
});
}
});
Working fiddle here.