How to update the Pin properties on a map when button pressed?

I have implemented the "Customizing a Map Pin" example from xamarin website (sorry i can't put links), the only change i did to the code is that i get the pin properties from a database so the pins are dynamically layed in the map based on values from that database.
Those pins represent parking spots and there are sensors detecting the parking spot occupancy, so on the application side i take the values from the database and create each pin based on those values, the color of the pin changes based on one of those values, everything is working fine BUT what i need to do now is to update the map and more specifically the pins.

What i thought first was to create a button creating a new instance of the same class (MapPage), but the problem is that the region currently seen on the map changes when the button is clicked, so is not optimal to create a new instance of the class to update the pins,
So the question is, how can i update the pins?, i don't put my code here because is almost identical to the example, i repeat the only difference from that code and mine is that the pin properties are obtained from a database.

Any ideas will be much appreciated!

Answers

  • XnainaXnaina USMember ✭✭✭

    Example:

    pinSource.Type = PinType.Place;
    pinSource.Tag = Constant.Source;
    pinSource.Label = ViewModel.SourcePlaces.Name;
    pinSource.Address = ViewModel.SourcePlaces.Description;
    pinSource.Icon = BitmapDescriptorFactory.FromBundle("marker.png");
    pinSource.Position = new Position(ViewModel.SourcePlaces.Geometry.Location.Lat,ViewModel.SourcePlaces.Geometry.Location.Lng);
    map.Pins.Add(pinSource);

    For more use this sample. https://github.com/amay077/Xamarin.Forms.GoogleMaps/tree/master/XFGoogleMapSample/XFGoogleMapSample

  • martinmedinamartinmedina USMember

    thanks xnaina i'm able to create new pins but the pins are created on top of the old ones, i think i need to use clear(pin) somewhere, but where?

  • martinmedinamartinmedina USMember

    Here is my code

    This is my MapPage class

    <br /> using System.Collections.Generic;<br /> using Xamarin.Forms;<br /> using Xamarin.Forms.Maps;<br /> using Plugin.Geolocator;<br /> using Plugin.Permissions;<br /> using Plugin.Permissions.Abstractions;<br /> using Newtonsoft.Json.Linq;<br /> using System.Net.Http;</p> <p>namespace CustomRenderer<br /> {<br /> public partial class MapPage : ContentPage<br /> {<br /> string dataocupancy;</p> <pre><code> string getOcupancydata() { string url = "mywebservice"; try { using (HttpClient client = new HttpClient()) { using (HttpResponseMessage response = client.GetAsync(url).Result) { using (HttpContent content = response.Content) { dataocupancy = content.ReadAsStringAsync().Result; return dataocupancy; } } } } catch { return "Connection error"; } } public MapPage() { InitializeComponent(); var dataocupancy = getOcupancydata(); var dataResultOcupancy = JArray.Parse(dataocupancy); string estadoletra = ""; bool estadoBool; int counter = 0; var bUpdate = new Button() { Text = "UPDATE", VerticalOptions = LayoutOptions.Fill, BackgroundColor = Xamarin.Forms.Color.Red, Image = "update.png", BorderRadius = 15, BorderColor = Xamarin.Forms.Color.Black, BorderWidth = 3 }; var customMap = new CustomMap { MapType = MapType.Street, WidthRequest = App.ScreenWidth, //HeightRequest = App.ScreenHeight }; customMap.CustomPins = new List<CustomPin>(); bUpdate.Clicked += async (sender, e) => { customMap.Pins.Clear(); int contador=0; dataOcupancy = getOcupancydata(); dataResultOcupancy = JArray.Parse(dataOcupancy); foreach (var v in dataResultOcupancy) { if ((int)dataResultOcupancy[contador]["estado"] == 1) { estadoBool = false; estadoletra = "Ocupado"; } else { estadoBool = true; estadoletra = "Desocupado"; } var pin = new CustomPin() { Pin = new Pin() { Type = PinType.Place, Position = new Position((double)dataResultOcupancy[contador]["coordenadas_lat"], (double)dataResultOcupancy[contador]["coordenadas_lon"]), Label = estadoletra, Address = "Calle " + dataResultOcupancy[contador]["calle"].ToString() + " con " + dataResultOcupancy[contador]["interseccion1"].ToString() }, Id = "id: " + contador, Url = "", Estado = estadoBool, }; customMap.CustomPins.Add(pin); counter++; } foreach (var pin in customMap.CustomPins) { customMap.Pins.Add(pin.Pin); } }; foreach (var v in dataResultOcupancy) { if ((int)dataResultOcupancy[counter]["estado"] == 1) { estadoBool = false; estadoletra = "busy"; } else { estadoBool = true; estadoletra = "free"; } var pin = new CustomPin() { Pin = new Pin() { Type = PinType.Place, Position = new Position((double)dataResultOcupancy[counter]["coordenadas_lat"], (double) dataResultOcupancy [counter] ["coordenadas_lon"]), Label = estadoletra, Address = "Calle " + dataResultOcupancy [counter] ["calle"].ToString() + " con " + dataResultOcupancy [counter] ["interseccion1"].ToString() }, Id = "id: " + counter, Url = "", Estado = estadoBool }; customMap.CustomPins.Add(pin); counter++; } foreach (var pin in customMap.CustomPins) { customMap.Pins.Add(pin.Pin); } // dont delete below code ,they will save you if timer doesnt work . customMap.MoveToRegion(MapSpan.FromCenterAndRadius(customMap.CustomPins[0].Pin.Position, Distance.FromMiles(0.20))); Content = new StackLayout { Children = { bUpdate, customMap } }; } }

    }


    And here is my custom map renderer droid where i assign an icon based on a pin property


    <br /> using System;<br /> using System.Collections.Generic;<br /> using System.ComponentModel;<br /> using Android.Content;<br /> using Android.Gms.Maps;<br /> using Android.Gms.Maps.Model;<br /> using Android.Widget;<br /> using CustomRenderer;<br /> using CustomRenderer.Droid;<br /> using Xamarin.Forms;<br /> using Xamarin.Forms.Maps;<br /> using Xamarin.Forms.Maps.Android;</p> <p>[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]<br /> namespace CustomRenderer.Droid<br /> {<br /> public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter<br /> {<br /> List<CustomPin> customPins;<br /> bool isDrawn;</p> <pre><code> protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e) { base.OnElementChanged(e); if (e.OldElement != null) { NativeMap.InfoWindowClick -= OnInfoWindowClick; } if (e.NewElement != null) { var formsMap = (CustomMap)e.NewElement; customPins = formsMap.CustomPins; Control.GetMapAsync(this); } } protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if (e.PropertyName.Equals("VisibleRegion") && !isDrawn) { NativeMap.Clear(); NativeMap.InfoWindowClick += OnInfoWindowClick; NativeMap.SetInfoWindowAdapter(this); foreach (var pin in customPins) { var marker = new MarkerOptions(); marker.SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude)); marker.SetTitle(pin.Pin.Label); marker.SetSnippet(pin.Pin.Address); if (pin.Estado == true) { marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin)); } else { marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin2)); } NativeMap.AddMarker(marker); } isDrawn = true; } } protected override void OnLayout(bool changed, int l, int t, int r, int b) { base.OnLayout(changed, l, t, r, b); if (changed) { isDrawn = false; } } void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e) { var customPin = GetCustomPin(e.Marker); if (customPin == null) { throw new Exception("Custom pin not found"); } if (!string.IsNullOrWhiteSpace(customPin.Url)) { var url = Android.Net.Uri.Parse(customPin.Url); var intent = new Intent(Intent.ActionView, url); intent.AddFlags(ActivityFlags.NewTask); Android.App.Application.Context.StartActivity(intent); } } public Android.Views.View GetInfoContents(Marker marker) { var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as Android.Views.LayoutInflater; if (inflater != null) { Android.Views.View view; var customPin = GetCustomPin(marker); if (customPin == null) { throw new Exception("Custom pin not found"); } if (customPin.Id == "Xamarin") { view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null); } else { view = inflater.Inflate(Resource.Layout.MapInfoWindow, null); } var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle); var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle); if (infoTitle != null) { infoTitle.Text = marker.Title; } if (infoSubtitle != null) { infoSubtitle.Text = marker.Snippet; } return view; } return null; } public Android.Views.View GetInfoWindow(Marker marker) { return null; } CustomPin GetCustomPin(Marker annotation) { var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude); foreach (var pin in customPins) { if (pin.Pin.Position == position) { return pin; } } return null; } }

    }

  • By updating means you want to change pins on the same map, I would suggest to keep the same map object and change the pins collection.
    Hope this helps.

Sign In or Register to comment.