I'm building a windows phone 8.1 app that reads a file ( which holds GPS points, each point consists of e.g. latitude and longitude fields). For each point I have it so it's displayed with a little pushpin icon on the map control, based on its coordinated from the above file. Is there any way I can implement a click event on the push pin element and use it to e.g. to display another window where user can change/display specific point/pushpin info ( latitude or longitude) ? This is what I use to read my file and display on the map and it works fine:
info: notes is public ObservableCollection<Point> notes;
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
bool exist; // used to check if JSON file exists
// get current position and set the map to point at it
Geoposition position = await App.DataModel.GetCurrentPosition();
await MyMap.TrySetViewAsync(position.Coordinate.Point, 16D);
mySlider.Value = MyMap.ZoomLevel; // set it to 16D from previous line
exist = await App.DataModel.FileExistsAsync();
if (exist == true)
{
// read the file and load points into a list
await App.DataModel.ReadFile();
// put points on the map - little map icon
foreach (var point in App.DataModel.notes)
{
MapIcon myMapIcon = new MapIcon();
myMapIcon.Location = new Geopoint(new BasicGeoposition()
{
Latitude = point.Latitude,
Longitude = point.Longitude,
});
myMapIcon.NormalizedAnchorPoint = new Point(0.5, 1.0);
MyMap.MapElements.Add(myMapIcon);
}
}
}
Not as you have implemented, no. You will need to add XAML-based pushpins to the map using MyMap.Children.Add(myXamlControl) instead of using MyMap.MapElements.Add(myMapIcon) if you want any sort of end-user events. MapIcons get integrated into the map "image" instead of floating on top and cannot react to anything.
Edit: Here's how you set the point and anchor for a XAML control:
MyMap.SetLocation(myXamlControl, myPoint);
MyMap.SetNormalizedAnchorPoint(myXamlControl, new Point(0.5, 0.5));
Related
I'm using OxyPlot to show a data chart and to allow the user to select an interval of data he wants to do calculation on.
It looks like this:
Now, I would like the user to be able to set the data interval used for calculation himself by resizing the chart. For example, if he resized the chart on this particular interval it would only take the points located between the furthest left and the furthest right on screen.
I already found the event that triggers whenever the chart is resized :
plotModel.Updated += (s, e) =>
{
//reset interval used for calculation
};
What I couldn't find in OxyPlot documentation is a way to retrieve a certain set of points currently shown. It doesn't need to be points in particular, I could also use only the x components of each extremum.
You could use Series.GetScreenRectangle().Contains() method after transforming your points using the Transform() method to detect if the point is currently in display.
For example,
model.Updated += (s,e)=>
{
if(s is PlotModel plotModel)
{
var series = plotModel.Series.OfType<OxyPlot.Series.LineSeries>().Single();
var pointCurrentlyInDisplay = new List<DataPoint>();
foreach (var point in series.ItemsSource.OfType<DataPoint>())
{
if (series.GetScreenRectangle().Contains(series.Transform(point)))
{
pointCurrentlyInDisplay.Add(point);
}
}
}
};
You are iterating over the point collection and verifying if the Transformed point (Transform method transform DataPoint to screen point) falls within the Screen Rectangle used by the series.
Update
If you have used Series.Points.AddRange()/Add() for adding points instead of Series.ItemSource, use the following for retrieving points.
foreach (var point in series.Points.OfType<DataPoint>())
{
if (series.GetScreenRectangle().Contains(series.Transform(point)))
{
pointCurrentlyInDisplay.Add(point);
}
}
When I add a slew of Pushpins to a Bing Map, I want the zoom level to automagically update to the "best fit" - the "closest in" view that allows all of the Pushpins to be displayed.
I would imagine that it would be done by computing the zoom level required by discovering the latitude values that are the furthest north and southmost, and the longitude values that are furthest east and west, and then centering on the average value of those.
My idea is passing a List of GeoCoordinates to a method that would return the appropriate ZoomLevel, and possibly a separate method that would calculate the center point of the map.
These two values could then be used for the map's SetView(location, ZoomLevel) method.
Has this been done already, or is there a Bing Maps call that will handle this?
There is an overload of SetView which accepts a list of locations and set the view to the bounding rectangle of those locations. It ensures all the locations are visible; you also need to add a bit of extra margins to the view rectangle to make sure all the pushpins are visible.
Having pushpins on the map, means you already have a list of locations or if you don't have you can easily get the locations of all those pushpins.
Example
Assuming you have followed the steps on How can I add a Bing Maps Component to my C# Winforms app?, add a button to the form and handle its click event like this:
private void button1_Click(object sender, EventArgs e)
{
var map = this.userControl11.myMap;
//Locations
var locations = new[] {
new Location(47.6424, -122.3219),
new Location(47.8424, -122.1747),
new Location(47.67856, -122.130994)};
//Add Pushpins
locations.Select(x => new Pushpin() { Location = x })
.ToList().ForEach(x => { map.Children.Add(x); });
//Margin
var w = new Pushpin().Width;
var h = new Pushpin().Height;
var margin = new Thickness(w / 2, h, w / 2, 0);
//Set view
map.SetView(locations, margin, 0);
}
And this is the result:
Note: If you don't have the list of locations, but you have just a few pushpins on the map, then you can easily get locations like this:
var map = this.userControl11.myMap;
var locations = map.Children.OfType<Pushpin>().Select(x => x.Location);
After following the instructions in the link here How to create draggable pin in windows 10 to give pick location functionality?
I have a half working dragging pin.
Currently the map pin (Grid) starts in the correct place on the map, however, upon dragging the map pin the pin initially pops up to the top left of the screen (0,0) and starts dragging from here.
Upon dropping the pin it appears to have disconnected from the map element itself as you can then scroll the map and the pin stays in place on the screen.
I am using Xamarin.Forms with a custom map renderer.
Following the original example the map would still pan along with the pin dragging, I corrected this by disabling panning upon Grid_ManipuationStarted.
The issue seems to be with the Grid_ManipulationDelta function as this is what removes the Grid from the MapControl children.
I've uploaded a video of the issue onto YouTube. Found here: https://youtu.be/uUkB5Pi5MnA
My code is as follows:
void IMapControls.startDraggable(Location l) {
// Create the XAML
var grid = new Windows.UI.Xaml.Controls.Grid {
Height=50,
Width=32,
Background = new ImageBrush() { ImageSource= new BitmapImage(new Uri("ms-appx:///pin.png",UriKind.RelativeOrAbsolute)),Stretch = Stretch.Uniform },
};
grid.ManipulationMode = ManipulationModes.TranslateX|ManipulationModes.TranslateY;
grid.ManipulationCompleted += Grid_ManipulationCompleted;
grid.ManipulationStarted += Grid_ManipuationStarted;
grid.ManipulationDelta += Grid_ManipulationDelta;
// Set RenderTransform so not null later
CompositeTransform tran = new CompositeTransform();
grid.RenderTransform = tran;
// Add XAML to the map.
nativeMap.Children.Add(grid);
Geopoint snPoint = new Geopoint(new BasicGeoposition() { Latitude = l.lat,Longitude = l.lng });
MapControl.SetLocation(grid,snPoint);
MapControl.SetNormalizedAnchorPoint(grid,new Windows.Foundation.Point(0.5,1.0));
}
private void Grid_ManipuationStarted(Object sender,ManipulationStartedRoutedEventArgs e) {
nativeMap.PanInteractionMode=MapPanInteractionMode.Disabled;
}
private void Grid_ManipulationDelta(object sender,ManipulationDeltaRoutedEventArgs e) {
var grid = sender as Windows.UI.Xaml.Controls.Grid;
CompositeTransform xform = grid.RenderTransform as CompositeTransform;
xform.TranslateX += e.Delta.Translation.X;
xform.TranslateY += e.Delta.Translation.Y;
e.Handled = true;
}
private void Grid_ManipulationCompleted(object sender,ManipulationCompletedRoutedEventArgs e) {
nativeMap.PanInteractionMode=MapPanInteractionMode.Auto;
var grid = sender as Windows.UI.Xaml.Controls.Grid;
Rect point = grid.TransformToVisual(nativeMap).TransformBounds(new Rect(0,0,grid.Width,grid.Height));
Geopoint gPoint;
nativeMap.GetLocationFromOffset(new Windows.Foundation.Point(point.X,point.Y),out gPoint);
Debug.WriteLine(gPoint.Position.Latitude);
Debug.WriteLine(gPoint.Position.Longitude);
}
Solved this by creating a Grid and adding it to the map, then creating an Image and adding this to the Grid. You drag the Image using Delta.Translation as above and upon dropping the pin it moves the Grid using MapControl.SetLocation (Not TranslateX/Y) to where the pin drops, then TranslateX/Y the pin Image to 0,0 (relative to the parent Grid).
This works as the Grid only moves using SetLocation which seems to work fine and doesn't disconnect it from the map like before. The pin moves relative to the Grid and has no issues as before, ie. Moving to 0,0 upon start and disconnecting from the map object.
Cheers!
Can post code if anyone is interested but I think it is pretty self explanatory.
I'm using Gmap.Net on windows form, I want to draw track of an object when I receive its position, I use Routes for this. When I add points to a route, no line is seen on the map, but when I change the zoom of the map, they appear on the map. Also when I set the position of the map after adding a point to the route (gMapControl1.Position = new PointLatLng(...)), it works correctly and I see the route lines on the map, any idea? My code is like below.
void NewDataReceived(DeviceInfo deviceinf)
{
//---some codes
//----For the first time I add layer and route
if (deviceOverLay == null)
{
deviceOverLay = new GMapOverlay(deviceinf.DeviceId.ToString());
gMapControl1.Overlays.Add(deviceOverLay);
deviceRoute = new GMapRoute(new List<PointLatLng>(), deviceinf.DeviceName);
deviceOverLay.Routes.Add(deviceRoute);
//Add all your points here
deviceRoute.Points.Add(new PointLatLng(deviceinf.Latitude, deviceinf.Longitude));
deviceRoute.Tag = deviceinf;
}
else
{
deviceOverLay.Routes[0].Points.Add(new PointLatLng(deviceinf.Latitude, deviceinf.Longitude));
}
//if I call this line it works, but I don't want it
// gMapControl1.Position = new PointLatLng(deviceinf.Latitude, deviceinf.Longitude);
//---some codes
}
Try using
gMapControl1.UpdateRouteLocalPosition(deviceRoute);
This updates the local positions and does a redraw.
Sorry if this is a dumb question, I'm a beginner to windows phone 8.1 development.
I am using MapControl to display my current location on the map but as I move my position does not get updated automatically in realtime unless I click on a button and re-initialize the position of the pushpin which was earlier created. Is there a better way where this happens in the without the user having to push the button everytime he wants to see his current location.
private async Task setMyLocation()
{
try
{
var gl = new Geolocator() { DesiredAccuracy = PositionAccuracy.High };
Geoposition location = await gl.GetGeopositionAsync(TimeSpan.FromMinutes(5), TimeSpan.FromSeconds(5));
var pin = new MapIcon()
{
Location = location.Coordinate.Point,
Title = "You are here",
NormalizedAnchorPoint = new Point() { X = 0, Y = 0 },
};
myMapView.MapElements.Add(pin);
await myMapView.TrySetViewAsync(location.Coordinate.Point, 20);
}
catch
{
myMapView.Center = new Geopoint(App.centerPin);
myMapView.ZoomLevel = 20;
Debug.WriteLine("GPS NOT FOUND");
}
App.centerPin = myMapView.Center.Position;
}
Thanks in Advance!
Rather than running a timer and polling, handle the Geolocator.PositionChanged event. This will fire each time the position changes and will be significantly more efficient.
You can set the Geolocator.MovementThreshold property so it will only fire if the user has moved a given distance and not do anything if the user stands in the same place. The threshold you pick will probably depend on how far your map is zoomed in.
The way I would do it is to put a Timer that requests information from the server at a given interval.
Take a look onto this answer containing a code snippet : Stackoverflow answer