Get the certain area in the map? - xamarin.forms

I am working on Xamarin Forms with Maps. I need to get a certain area within the map and check if the user are inside of this area. for example, There is a certain village that I want to get the whole area and coordinates? I only i tried pin location but there is only 2 coordinates and cannot really determine if the user is inside the area.
I am going to save the area in my database and I already figured how to do this. but I don't know how can I get the coordinates of the certain area. How can I achieve this in Xamarin Forms?
I added an image for example, How Can I the the boxed area?

First of all, Thank you #Jason for the provided links of answers.
I came up with this solution referencing to this question.
Checking if a longitude/latitude coordinate resides inside a complex polygon in an embedded device?
As I have mention on the comments above, I have a set of coordinates(lat/lng)
inside my database and draw it on my maps in android to form a rectangular shape using polyline.
To check if the user is inside the drawn rectangle. I have modified the answer of Drew Noakes in the provided link above of this answer.
CODE
/// <summary>
/// Check if the user's location is inside the rectangle
/// </summary>
/// <param name="location">Current user's location</param>
/// <param name="_vertices">Rectangular shape for checking</param>
/// <returns></returns>
private bool CheckUserLocation(Location location, Polyline _vertices)
{
var lastPoint = _vertices.Geopath[_vertices.Geopath.Count - 1];
var isInside = false;
var x = location.Longitude;
foreach (var point in _vertices.Geopath)
{
var x1 = lastPoint.Longitude;
var x2 = point.Longitude;
var dx = x2 - x1;
if (Math.Abs(dx) > 180.0)
{
if (x > 0)
{
while (x1 < 0)
x1 += 360;
while (x2 < 0)
x2 += 360;
}
else
{
while (x1 > 0)
x1 -= 360;
while (x2 > 0)
x2 -= 360;
}
dx = x2 - x1;
}
if ((x1 <= x && x2 > x) || (x1 >= x && x2 < x))
{
var grad = (point.Latitude - lastPoint.Latitude) / dx;
var intersectAtLat = lastPoint.Latitude + ((x - x1) * grad);
if (intersectAtLat > location.Latitude)
isInside = !isInside;
}
lastPoint = point;
}
return isInside;
}
If you are curious on how I tested it, here is what I did.
First, I retrieved my current location and manually draw a rectangle circulating on my location.
Second, Draw a second rectangle not so far away from my current location but outside the first drawn rectangle.
Third, I passed the first Polyline drawn on the function
CheckUserLocation(myCurrentLocation, firstRectangle) //THIS RETURNS TRUE
Another test is I passed the second rectangle to the function
CheckUserLocation(myCurrentLocation, secondRectangle) //THIS RETURNS FALSE
I will test it again if there is something wrong with this answer. But I hope someone will find this answer helpful.

Related

Gmap.net show only markers within polygon

I am currently working with gmap.net to create a certain radius with a polygon. I currently have made a polygon for the radius but now I come to the problem that I want to create multipule markers but only show the markers who are inside the polygon. Is this possible?
_polygonOverlay = new GMapOverlay("destination");
_gMap.Overlays.Add(_polygonOverlay);
private void CreateCircle(PointLatLng destination, double radius)
{
List<PointLatLng> radiusPoint = new List<PointLatLng>();
double seg = Math.PI * 2 / 40;
for (int i = 0; i < 40; i++)
{
double theta = seg * i;
double latitude = destination.Lat + Math.Cos(theta) * radius;
double longitude = destination.Lng + Math.Sin(theta) * radius;
PointLatLng cirlePoint = new PointLatLng(latitude, longitude);
radiusPoint.Add(cirlePoint);
}
GMapPolygon radiusCircle = new GMapPolygon(radiusPoint, "radius");
_polygonOverlay.Polygons.Add(radiusCircle);
}
private void CreateMarkers()
{
_polygonOverlay.Markers.Add(new GMarkerGoogle(new PointLatLng(xxx, xxx), GMarkerGoogleType.blue));
_polygonOverlay.Markers.Add(new GMarkerGoogle(new PointLatLng(xxx, xxx), GMarkerGoogleType.blue));
_polygonOverlay.Markers.Add(new GMarkerGoogle(new PointLatLng(xxx, xxx), GMarkerGoogleType.blue));
}
Here is a little sample of the code I have that create a circle (still needs some work on it) and some markers.
Already thanks is advance
Since you are dealing with a circle, you should be able to simply check the distance of your marker from the center of the circle. If the distance is greater than the radius, don't add it to the overlay.
GMap gives you access to the necessary methods to determine this information. Do something like this:
//Assuming p1 is your marker and p2 is your circle center coordinate
double markerDist = GMap.NET.MapProviders.EmptyProvider.Instance.Projection.GetDistance(p1.Position, p2);
if(markerDist <= circleRadius)
{
//Add the marker to the overlay
}
Assume you have a GMapPolygon with some Points, you could just use
bool inside = gMapPolygon.IsInside(point)
to check if the point of a GMarker is inside that GMapPolygon

Getting Correct Position of Image based on 2D Parallax Camera

My 2D engine is resolution independent and has a camera based on the article here: http://www.david-gouveia.com/portfolio/2d-camera-with-parallax-scrolling-in-xna/
I've implemented parallax scrolling into my camera class the same way the article above mentions. It works great, but it's screwed up my culling code and I'm struggling to figure out the math.
Each of my background images has a Rectangle I use to check if it's currently on screen. If it doesn't collide with my Camera rectangle, I don't draw it. The problem is that if the image is drawing as a parallax layer, the rectangle isn't being calculated where it really appears on the screen.
The transform matrix in my Camera class looks like this:
public static Matrix GetTransformMatrix(Vector2 parallax)
{
return Matrix.CreateTranslation(new Vector3(-position * parallax, 0)) * Matrix.CreateRotationZ(rotation) *
Matrix.CreateScale(new Vector3(zoom, zoom, 1)) * Matrix.CreateTranslation(new Vector3(Resolution.VirtualWidth
* 0.5f, Resolution.VirtualHeight * 0.5f, 0));
}
For each parallax layer, I call SpriteBatch.Begin() and pass it the above transform matrix with the correct parallax offset passed in depending on what layer we're drawing (foreground, background etc.)
I've successfully made a ScreenToWorld function that works for getting the position of where the mouse has been clicked. Note that I need to calculate both my resolution matrix and camera matrix for it to work.
public static Vector2 ScreenToWorld(Vector2 input, Vector2 parallax)
{
input.X -= Resolution.VirtualViewportX;
input.Y -= Resolution.VirtualViewportY;
Vector2 resPosition = Vector2.Transform(input, Matrix.Invert(Resolution.getTransformationMatrix()));
Vector2 finalPosition = Vector2.Transform(resPosition, Matrix.Invert(Camera.GetTransformMatrix(parallax)));
return finalPosition;
}
So I figured to calculate the correct Rectangle positions of my parallax layers I would need a WorldToScreen function... I tried this, but it isn't working:
public static Vector2 WorldToScreen(Vector2 input, Vector2 parallax) //I pass the same parallax value that is used in the Camera matrix function.
{
input.X -= Resolution.VirtualViewportX;
input.Y -= Resolution.VirtualViewportY;
Vector2 resPosition = Vector2.Transform(input, Resolution.getTransformationMatrix());
Vector2 finalPosition = Vector2.Transform(resPosition, Camera.GetTransformMatrix(parallax));
return finalPosition;
}
I'm guessing I'm on the right track but my math is wrong? I'm passing the above function the non-parallaxed Rectangle position with the hopes of making it update to where the parallax image is really being drawn. Thanks in advance if someone can help!
Ends up my math was right but I needed to calculate my Camera's screen rect based off the Camera's matrix. If you do this you don't need to touch the background rectangles at all. Just pass in the background's parallax value into this function then check against the rectangle it returns:
/// <summary>
/// Calculates the Camera's screenRect based on the parallax value passed in.
/// </summary>
public static Rectangle VisibleArea(Vector2 parallax)
{
Matrix inverseViewMatrix = Matrix.Invert(GetTransformMatrix(parallax));
Vector2 tl = Vector2.Transform(Vector2.Zero, inverseViewMatrix);
Vector2 tr = Vector2.Transform(new Vector2(Resolution.VirtualWidth, 0), inverseViewMatrix);
Vector2 bl = Vector2.Transform(new Vector2(0, Resolution.VirtualHeight), inverseViewMatrix);
Vector2 br = Vector2.Transform(new Vector2(Resolution.VirtualWidth, Resolution.VirtualHeight), inverseViewMatrix);
Vector2 min = new Vector2(
MathHelper.Min(tl.X, MathHelper.Min(tr.X, MathHelper.Min(bl.X, br.X))),
MathHelper.Min(tl.Y, MathHelper.Min(tr.Y, MathHelper.Min(bl.Y, br.Y))));
Vector2 max = new Vector2(
MathHelper.Max(tl.X, MathHelper.Max(tr.X, MathHelper.Max(bl.X, br.X))),
MathHelper.Max(tl.Y, MathHelper.Max(tr.Y, MathHelper.Max(bl.Y, br.Y))));
return new Rectangle((int)min.X, (int)min.Y, (int)(max.X - min.X), (int)(max.Y - min.Y));
}

How to calculate the blast area of a bomb?

I'm developing a game, let's say player a put a bomb in location x=100,y=100 and the explosion radius is 100 units... It is pretty easy for me to find all the "items" in the game that were hit by the blast of the bomb (just need to check that their distance from the bomb is lower then 100).
But now i want to take in consideration the obstacles i have in the game, the obstacles are squares, always 64*64 pixels, always aligned to the axis (not rotated).. i want to know if an item was "hidden" behind an obstacle to know he wasn't hit ...
something like this :
The dude on the right wasn't hit, but the dude on the bottom was hit, i filled in gray the hit area, and in green the area which is hidden...
My idea is :
1. find all the items in the scene that their distance from the bomb is lower then 100.
2. find all the obstacles in the scene that their distance from the bomb is lower then 100.
3. calculate the lines from the items to the center of the bomb .. then check if the lines intersect any obstacle , if no .. you were hit.
Finaly, the questions
1. Does any one has a better idea ?
2. Are there free opensource c# compatible engines that can help me ? Box2d can help me here ?
Thanks
It is quite simple, and as Jongware mention in the comments, you should use two lines of visibility.
You should compute the lines of visibility from each "side" of the items in the picture. The origin of each visibility line can be approximated by computing the line from the center of the bomb and get the direction normal to that vector. Your two visibility points are then located one radius out from the center of the item in the normal and negative normal direction. This circle approximation might not represent all possible shapes very well, but is generally a good enough approximation for simple games (and your items look circular in the drawing).
Java-isch pseudocode using 2D-vectors:
// bombCenter and itemCenter are 2D-vectors
bombDirectionVector = bombCenter.minus(itemCenter);
normal = bombDirectionVector.getNormal() // normal vector of length 1
viewPoint1 = itemCenter.plus(normal.times(itemRadius));
viewPoint2 = itemCenter.minus(normal.times(itemRadius));
// Check obstacle intersection with the lines from viewPoint{1,2} to bombCenter
// ...
The visibility lines will then go from the points on the sides of each item to the bomb center. So, for each item, you check if the two visibility lines intersect either the same obstacle or two connected obstacles.
There is no free open source C#-compatible engines I know of that does this, but the only part that can be a bit tricky is the obstacle intersection check. So if you just find something to help you with the intersection check, then the rest should be very straight forward to implement.
I hope this helps, and let me know if anything is unclear and I'll clarify the answer accordingly.
Here is a nice demo/write-up on the subject:
http://www.redblobgames.com/articles/visibility/
If it is a tile-based game and you know the tile coordinates of all of the objects, you could use Bresenham's Line Algorithm: http://roguebasin.roguelikedevelopment.org/index.php?title=Bresenham%27s_Line_Algorithm. Here is an excerpt:
// Author: Jason Morley (Source: http://www.morleydev.co.uk/blog/2010/11/18/generic-bresenhams-line-algorithm-in-visual-basic-net/)
using System;
namespace Bresenhams
{
/// <summary>
/// The Bresenham algorithm collection
/// </summary>
public static class Algorithms
{
private static void Swap<T>(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; }
/// <summary>
/// The plot function delegate
/// </summary>
/// <param name="x">The x co-ord being plotted</param>
/// <param name="y">The y co-ord being plotted</param>
/// <returns>True to continue, false to stop the algorithm</returns>
public delegate bool PlotFunction(int x, int y);
/// <summary>
/// Plot the line from (x0, y0) to (x1, y10
/// </summary>
/// <param name="x0">The start x</param>
/// <param name="y0">The start y</param>
/// <param name="x1">The end x</param>
/// <param name="y1">The end y</param>
/// <param name="plot">The plotting function (if this returns false, the algorithm stops early)</param>
public static void Line(int x0, int y0, int x1, int y1, PlotFunction plot)
{
bool steep = Math.Abs(y1 - y0) > Math.Abs(x1 - x0);
if (steep) { Swap<int>(ref x0, ref y0); Swap<int>(ref x1, ref y1); }
if (x0 > x1) { Swap<int>(ref x0, ref x1); Swap<int>(ref y0, ref y1); }
int dX = (x1 - x0), dY = Math.Abs(y1 - y0), err = (dX / 2), ystep = (y0 < y1 ? 1 : -1), y = y0;
for (int x = x0; x <= x1; ++x)
{
if (!(steep ? plot(y, x) : plot(x, y))) return;
err = err - dY;
if (err < 0) { y += ystep; err += dX; }
}
}
}
}

Random CGPoint(s)

How could I get a random CGPoint that is outside the screen boundaries (frame)?
Also, given that point, how could I find a symmetrical point to it to the middle of the screen- e.g. say I have the point (width+1,height+1). Now the symmetrical point is (-1,-1). Say I have (-1,height +1)- symmetrical would be (width+1,-1).
Hope this is clear, and thanks!
If I understand your question correctly, you can use the following method:
- (CGPoint) randomPointIn:(CGRect)inrect outsideOf:(CGRect)outrect
{
CGPoint p;
do {
p.x = inrect.origin.x + inrect.size.width * (float)arc4random()/(float)UINT32_MAX;
p.y = inrect.origin.y + inrect.size.height * (float)arc4random()/(float)UINT32_MAX;
} while (CGRectContainsPoint(outrect, p));
return p;
}
It returns a random point that is inside inrect, but outside of outrect.
(I have assumed that inrect is "considerably larger" than outrect,
otherwise many loop iterations might be necessary to find a valid point.)
In your case, you would use outrect = CGRectMake(0, 0, width, height),
and inrect would specify the allowed domain.
And the point symmetrical to (x, y) with respect to the middle of the screen
with size (width, height) is (width - x, height - y).
UPDATE: As I just found here: http://openradar.appspot.com/7684419,
CGRectContainsPoint will return false if you provide it a point that is on the boundary of the CGRect. That means that the above method returns a point that is outside of
or on the boundary of the given rectangle outrect. If that is not desired,
additional checks can be added.
I believe this should work.
//To get a random point
- (CGPoint)randomPointOutside:(CGRect)rect
{
// arc4random()%(int)rect.size.width
// This gets a random number within the width of the rectangle
//
// (arc4random()%2) ? rect.size.width : 0)
// This has a 50:50 to put the point in the q1 quadrant relative to the top right point of the rect
//
// q4 q1
// _____ +
// | |
// | q3 | q2
// |_____|
//
float x = arc4random()%(int)rect.size.width + ((arc4random()%2) ? rect.size.width : 0);
float y = arc4random()%(int)rect.size.height + ((arc4random()%2) ? rect.size.height : 0);
return CGPointMake(x, y);
}
//To get the symmetrical point
- (CGPoint)symmetricalPoint:(CGPoint)p around:(CGRect)rect
{
return CGPointMake((p.x-rect.size.width) * -1, (p.y-rect.size.height) * -1);
}

Creating a scaled map circle

Similar questions to this have been asked a number of times here, but none of them seem to give me exactly what I want. I am working with the Bing Map control on Windows Phone and I'd like to add an ellipse that scales properly with zoom changes. This can be done with poly lines and polygons, but there is no ellipse type derived from MapShapeBase. I've tried various ways of doing it, but they require playing around with pixel sizes and fudging the math to make it line up with geo coordinates. I want to create an Ellipse with a center and x/y sizes in meters and have the framework do the rest. It seems so simple. Have I missed it somewhere? My other approach is to draw 365 line segments in a poly line, but that seems horribly ugly, and since the center can move, I'd need to bind the Location of every segment. That seems very heavy-weight. Any other thoughts?
[To be specific, I want to add a "GPS Accuracy" indicator as a circle around the current location.]
Update
In Mango, the phone automatically shows such a circle.
Orginal Post
It's fairly easy. You just use a Pushpin control to do the drawing with.
1) Add a MapLayer to your control:
<maps:MapLayer>
<maps:MapPolygon Fill="Gray"
IsHitTestVisible="False"
Locations="{Binding AccuracyLocationCollection}"
Opacity="0.6"
Stroke="Black"
StrokeThickness="2" />
</maps:MapLayer>
2) Add the AccuracyLocationCollection property in your ViewModel
public LocationCollection AccuracyLocationCollection
{
get;
set;
}
3) In the GeoCoordinateWatcher_PositionChanged event handler, calculate the size of the circle, and set the value to the AccuracyLocationCollection
ViewModel.AccuracyLocationCollection = DrawMapsCircle(e.Position.Location);
4) The code for the DrawMapsCircle goes like this:
private static double ToRadian(double degrees)
{
return degrees * (Math.PI / 180);
}
private static double ToDegrees(double radians)
{
return radians * (180 / Math.PI);
}
public static LocationCollection DrawMapsCircle(GeoCoordinate location)
{
double earthRadiusInMeters = 6367.0 * 1000.0;
var lat = ToRadian(location.Latitude);
var lng = ToRadian(location.Longitude);
var d = location.HorizontalAccuracy / earthRadiusInMeters;
var locations = new LocationCollection();
for (var x = 0; x <= 360; x++)
{
var brng = ToRadian(x);
var latRadians = Math.Asin(Math.Sin(lat) * Math.Cos(d) + Math.Cos(lat) * Math.Sin(d) * Math.Cos(brng));
var lngRadians = lng + Math.Atan2(Math.Sin(brng) * Math.Sin(d) * Math.Cos(lat), Math.Cos(d) - Math.Sin(lat) * Math.Sin(latRadians));
locations.Add(new Location()
{
Latitude = ToDegrees(latRadians),
Longitude = ToDegrees(lngRadians)
});
}
return locations;
}
Result: (This is next to my home, I can confirm there's about 3 meters between the roads the grey circle is displaying between)

Resources