fivenine-zurich/UnifiedMaps

How to default map location to current user's position

Closed this issue · 15 comments

Hello,

I would like my map to be centered on the current user's position when it starts up. Using the Sample code as the core of my test app, I seem to be unable to issue 'Map.MoveToUserLocation(true)' at any time during 'startup'. Once the map is sitting there, I am able to issue a Command, which in turn tells the map to re-center itself. And it works fine. I have tried calling the code during _OnAppearing in the code-behind of the xaml page. The app crashes.

There does not appear to be an 'initial location' property, though I might just be missing something.

I imagine I could set a timer, such that 1 second after _OnAppearing I could issue a Command to MoveToUserLocationCommand...

Thoughts?

Thank you!

As a note to the above, I also have just tried using .MoveToRegion with two different options. First I set a region where the 'top left' and 'bottom right' positions were the same value, i.e., the user's location (hard-wired for now). The other was setting the 'center' to the user's position, with a 'width' and 'height' value of 500. Neither variation crashed, but neither one did the correct thing either. Instead the map was left with the view of 4 [default] pins as defined in the Sample code (I added them to the Pins collection during constructor so that I would have something to see right away).

Thanks again.

Hi, have you tried the property IsShowingUser? It should automatically center on the user's location.

Hi Jan,

Yes, IsShowingUser="True" in my xaml. Note that in my constructor for the viewmodel, I do add all of your points (from the Sample) to the Pins collection. This may be why the map starts out viewing them. I just tried commenting that out (so NO pins are 'on' the map when it starts out) and the map then default to the 'middle of the world', somewhere to the southwest of Equitorial Guinea (Africa). Upon 'adding a pin' the map moves to that location (Switzerland). Also manually moving to user location also works at that point. Thanks!

I see, but I don't think that it works what you are trying to do. As i understand you want the map to display the users location once its rendered?

At that point the users location might not even be available and since google maps uses its own location thingy, it can't be controlled. (Technically its just setting a property on the GoogleMap to true and wait for a callback to update our map)

What we do is use is Xam.Plugin.Geolocator and if position is available set a Pin with an empty spacer image and the coordinates. This way the map moves to that position while the map itself is resolving its own position, eventually showing the blue icon.

Ah ha! That makes sense... I was wondering what you were doing, and now i know. In the past I have used both MapQuest and Google APIs (from JavaScript and Flash) and both have a notion of 'Centering the map' or 'Defaulting Position' of the map to a given Location. Since there did not appear to be any similar method or property in UM, I assumed that the IsShowingUser might do that. I fully understand why 'MoveToUserLocation' does not work at initial render now. So, let me ask these questions:

  1. Is the 'last' pin that is defined the one that the map will center on? I have tried doing that and the map seems to center on somewhere south of Greenland. This is when I have your 4 pins plus my 1 pin in CO, USA. I am sure that the map is trying to show 'all the pins' and southern Greenland is mid-way between your pins and mine. I also tried this with and without calling MoveToRegion where the 'region' was my own location as well as setting the 'SelectedItem' to Pins.Last() and no combination seems to center the map on my location.
  2. I understand this might be a feature/enhancement request, but is there a notion of having a StartPostion property? Or maybe a .MoveToPosition(Position) method added to the product?

The map will try to fit all annotations. If you have set ZoomLevel it might not display all Pins. If you don't want that you can use the property AutoFitAllAnnotations

If you want a specific position/region to be visible you could try to use VisibleRegion property.

Setting SelectedItemshould technically work, but I could check that.

OK, yes, i understand trying to fit all annotations. I would like the map to be centered to whatever position I decide, such as 'current location'. So, I have tried the following inside of _OnAppearing:
Position userPos = new Position(40.5990640, -73.91061039); //some random pos in NYC area
MapRegion userRegion = new MapRegion(userPos,userPos);
userRegion = new MapRegion(userPos,500,500);
//Map.MoveToRegion(userRegion, true);
Map.VisibleRegion = userRegion;

Neither version of 'userRegion' works - map always centers south of Greenland. I have tried that with and without an MVVM assignment to SelectedItem.

I did have a zoomLevel set, of 6. I removed zoomLevel completely and now map centers at 'center of world' (south of Nigeria).

Thanks...

Hi davbaron,

Sorry for my late answer. There was indeed an issue with Maps not displaying VisibleRegion which was set before the map even displayed.

That should work now. You can bind the property to the map and set it up before rendering is completed and the map should show the location once it appears.

Hello Jan,

I have installed the latest version, 1.10.0 and re-tried my test code. I am still unable to 'pre-center' the map to a given location. I added a xaml property to the map,
VisibleRegion="{Binding InitialVisibleRegion}"

In my viewmodel (the binding context of the page), within the constructor, I set a local, _initialVisibleRegion to the following:
Position userPos = new Position(39.5990984, -104.9107224);
_initialVisibleRegion = new MapRegion(userPos, userPos);
//_initialVisibleRegion = new MapRegion(userPos, 1, 1);

The 'property' within the viewmodel is the following:
private MapRegion _initialVisibleRegion;
public MapRegion InitialVisibleRegion
{
get { return _initialVisibleRegion; }
set { SetProperty(ref _initialVisibleRegion, value); }
}

Even with no pins created at startup, the map centers itself on the equator, at 0,0.

I have even tried using a code-behind handler within the xaml.cs (_onAppearing) like such:
private void MapPage_OnAppearing(object sender, EventArgs e)
{
((MDChildViewModel)BindingContext).Map = Map;
//Map.MoveToUserLocation(true);
Position userPos = new Position(39.5990984, -104.9107224);
MapRegion userRegion = new MapRegion(userPos, userPos);
//userRegion = new MapRegion(userPos, 1, 1);
//Map.MoveToRegion(userRegion, true);
Map.VisibleRegion = userRegion;
}

Noting in the above code 'works', i.e., the map remains centered at 0,0

Other than a timer that somehow checks to see if the map has loaded, and if so, then call the Map.MoveToUserLocation(true), I do not know what to try. I think you had previously recommended me creating a single pin at the user's current location, but with a transparent, single pixel marker.

Thanks,

-David

That is strange. I will take another look then.

What worked for me was setting the Map.VisibleRegion in the OnAppearing override in the Code behind as well as binding to a property on the View Model, which was assigned in the constructor.

Could you please try the following:

ViewModel:

# In Constructor
VisibleRegion = new MapRegion(new Position(39.5990984, -104.9107224), 0.1, 0.1);

# Bindable Property
private MapRegion _visibleRegion;
public MapRegion VisibleRegion {
    get { return _visibleRegion; }
    set
    {
        _visibleRegion = value;
        OnPropertyChanged();
    }
}

View:

<unifiedMaps:UnifiedMap
        x:Name="Map"
        VisibleRegion="{Binding VisibleRegion}"
        VerticalOptions="FillAndExpand" >
</unifiedMaps:UnifiedMap>
  • No Codebehind
  • If possible remove other bindings.

I tested on a Nexus 5X API27.

Thanks

Hi Jan,

I tried many things today. First, within an 'empty' master-detail page, I added the map and code as you suggested. On my live LG G3, it does not do anything - the map 'defaults' to 0,0. I even added a bound 'label' to the page just to make sure the page was actually pulling data from the binding context. On the iOS simulator, it almost works, with the U.S.A. completely showing upon page load. The map is not 'centered' on the defined point, but, the entire 'region' is showing.

I also tried working more with the original page of my master-detail set. I did comment out the code-behind but left in all the other viewmodel code - and that page behaves exactly like the 'empty' page in the master-detail set.

On both platforms, if I add the 5 sample pins that you define in your sample, plus my own pin at my location, then the map loads and tries to show all pins.

The following might be an issue that needs it's own case: On the iOS simulator, because there are no native 'controls' (zoom) to show, it is impossible to zoom in and out. I tried the 'new' method of one-handed zooming: double-tapping-hold + drag up or down to zoom in or out, but that did not work within the simulator. I am using an iPhone 7 running iOS 11.2. Again, I am not suggesting that you manually overlay some fake zoom controls (though that would be cool), but I am asking about the 'one-handed-zoom' thing.

I am not sure what to tell you about the VisibleRegion thing. Might you instead consider a property called "InitialPosition" or "InitialCenter"? Both Android and iOS support the centering of the map at a given point. It seems like a somewhat standard feature to provide.

My LG is at version 6, api 23 if that helps. I would try it on my true phone, a OnePlus 3, but there is some [known] issue with running a Xamarin project on OnePlus devices. I've been trying now for several hours to get an 'accelerated' virtual device working, but so far cannot. I will try more tomorrow.

Thanks again,

-David

I don't know how I can help you further. Maybe you have a sample project so I can replicate your issue?

We are having the same issue on ANDRIOD, set the visibibleregion to a new position and then set the MoveToRegion but when the map displayes, it shows sort of the whole world with Africa being in the middle, and then animates to the given user position.

ViewModel.VisibleRegion = new MapRegion(new Position(-23.929013, 145.303858), 0.1, 0.1); MAP.VisibleRegion = ViewModel.VisibleRegion; MAP.MoveToRegion(ViewModel.VisibleRegion,false);

<unifiedMaps:UnifiedMap x:Name="MAP" Grid.Row="2" Grid.RowSpan="2" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" HasScrollEnabled="true" AutoFitAllAnnotations="false" HasZoomEnabled="true" IsShowingUser="false" MapType="Street" ShouldDisplayNativeControls="false" ShouldDeselectOnMapTouch="false" CanShowCalloutOnTap="false"> </unifiedMaps:UnifiedMap>

Any more info on how to get this to default to a location when showing the map the first time?

Found the culprit in my case, seems the onCameraChange is firing with coordinates, after I have set the Visible Region and MapToRegion, which have nothing todo with my coordinates. It is firing next to Africa in between me setting the VisibleRegions.

public void OnCameraChange(CameraPosition position) { if (_googleMap == null) { return; } var mapRegion = _googleMap.Projection.VisibleRegion.LatLngBounds; var region = new MapRegion(mapRegion.Southwest.Longitude, mapRegion.Northeast.Latitude, mapRegion.Northeast.Longitude, mapRegion.Southwest.Latitude); Map.VisibleRegion = region; }

Coordinates it has is: -1.5622886-05 -3.0653583949