I'm using Xamarin Forms and I'm using the Google Maps nuget for iOS and Android. I have a ListView with different places that I click to navigate to the MapPage to see the location of the place there.
Since the pins on the map are on different places I cannot set one zoom level for every location. I always want the 3 closest pins to be shown upon entering the page.
I have tried these solutions but I'm looking for a Forms solution for this problem.
LatLng marker1LatLng = new LatLng(marker1lat, marker1lng);
Latlng marker2LatLng = new LatLng(marker2lat, marker2lng);
LatLngBounds.Builder b = new LatLngBounds.Builder()
.Include(marker1LatLng)
.Include(marker2LatLng);
Also tried this but no luck:
var markers = [];//some array
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < markers.length; i++) {
bounds.extend(markers[i].getPosition());
}
map.fitBounds(bounds);
How to dynamically set the zoom level depending on the lat & lon of those 3 pins that I want to be visible.
I have a total of 10 pins for each place on the map but only wanna show closest 3. Once I zoom out i wanna be able to see all 10 tho.
Thanks for all the help in advance!
First, you need to find the three closest pins to the selected location, which I assume you will want to be the center of the map. Once you find the three closest pins, calculate how far the furthest of the 3 pins is and the use the
map.MoveToRegion (MapSpan.FromCenterAndRadius (
new Position (centerLatitude,centerLongitude), Distance.FromKilometers (distanceToThirdPin)));
where centerLatitude and centerLongitude are the coordinates of the selected location that will be at the center, and distanceToThirdPin is the calculated distance (in Km) to the farthest of the 3 pins.
Formula to calculate distance between a pair of latitudes and longitudes:
double R = 6371.0; // Earth's radius
var dLat = (Math.PI / 180) * (pinLatitude - centerLatitude);
var dLon = (Math.PI / 180) * (pinLongitude - centerLongitude);
var lat1 = (Math.PI / 180) * centerLatitude;
var lat2 = (Math.PI / 180) * pinLatitude;
var a = Math.Sin(dLat/2) * Math.Sin(dLat/2) + Math.Sin(dLon/2) * Math.Sin(dLon/2) * Math.Cos(lat1) * Math.Cos(lat2);
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1-a));
var d = R * c; // distance in Km.
So use the above formula to find the distances to all of your pins, then find the three closest. Once found, use the distance to the third closest pin as the distanceToThirdPin value when you call the map.MoveToRegion method.
Related
I'm trying to build a mini overview/orientation map that is synchronized with a full MapControl view, like this: (Full Screenshot)
I'm getting trouble trying to calculate the width, height and location of the little red rectangle that is inside the mini map according to the MapControl's size, location and zoom.
It should be synchronized with the MapControl's view, and a click on the small map should also change the CenterPoint of the MapControl.
The full map is a MapControl in UWP and a the mini-map is just a Border UIElement over a static image.
I'm using the following formulas. They works but not accurately. The error margin is pretty noticeable, specially for big zooms.
For calculating the location and the size of the red rectangle:
var positions = MapControl.GetVisibleRegion(MapVisibleRegionKind.Near).Positions.ToArray();
var topLeft = positions[0];
var bottomLeft = positions[1];
var topRigt = positions[2];
//Transfering the Longitude system from [-180, 180] to [0, 360]
var centerX = (MapControl.Center.Position.Longitude + 180) * (SmallMapWidth / 360);
//Transfering the Latitude system from [-90, 90] to [0, 180]
var centerY = (-MapControl.Center.Position.Latitude + 90) * (SmallMapHeight / 180);
var topLeftX = topLeft.Longitude + 180;
var topRightX = topRigt.Longitude + 180;
//MapControl wraparound by default. In that case, the topRightX might be smaller than topLeftX, as it will start from the 'beginning'.
var deltaX = Math.Abs(topLeftX - (topLeftX < topRightX ? topRightX : 360 - topRightX));
//The width of the red rectangle
SmallMapViewPortWidth = Math.Abs(deltaX) * (SmallMapWidth / 360);
//The height of the red rectangle
SmallMapViewPortHeight = Math.Abs(topLeft.Latitude - bottomLeft.Latitude) * (SmallMapHeight / 180);
//The center point of the red rectangle.
RedRectangleCenterPoint = (centerX - SmallMapViewPortWidth / 2, centerY - SmallMapViewPortHeight / 2);
The following is for navigating the MapControl to a point that was clicked on the overview map. X and Y are the point that was clicked relatively to the overview map.
var lon = 360 * x / SmallMapWidth - 180;
var lat = 90 - 180 * y / SmallMapHeight;
What is wrong with my calculations? Why there is a pretty noticeable error margin?
The issue was actually the image that I was using for the orientation map. It was resolved once I started using proper Bing Maps images.
It seems there is an official API for rendering images in different sizes based on Bing Maps' projection
https://learn.microsoft.com/en-us/bingmaps/rest-services/imagery/get-a-static-map
I've been searching for a while but haven't found exactly what I'm looking for.
I'm working on an app that will go in a race car. It will give the driver the ability to press a button to mark a Start/Finish line. It will also have a button to allow a driver to set segment times.
Keep in mind a track can be an oval which I'm working on first. It could be a road course or it could be an auto cross where the start and finish line aren't the exact same location. They could be with 50 feet of each other or so but the car never crosses where it starts.
I have my gps data coming in and I convert the NMea messages to my classes and I store Lat, Lon, Speed, Course etc. In my research I've ran across this which is interesting. The GPS will be mounted outside the roof for better signal. It generates 10 hits per second. (Garmin Glo)
http://www.drdobbs.com/windows/gps-programming-net/184405690?pgno=1
It's old but it talks about UTM and the Cartesian coordinate system. So using the DecDeg2UTM, I convert Lat & Lon to X & coordinates as well.
I've also been trying to use the Intersect formula I found Here I took the intersect and tried to convert it to C# which I'll post at the end. However, feeding coordinates of an oval track, it doesn't seem to be working. Also, I'm not sure exactly what it's supposed to be doing. But the coordinates it returns when it does somethign like -35.xxx & 98.xxxx which out in an ocean somewhere 1000's of miles from where the track is.
I looking for answers to the following.
I assume I need to take the location recorded when a button is pressed for Start/Finish or Segment and calculate a line perpendicular to the direction the car in able to be able to do some sort of Line Intersection calculation. The Cartesian coordinates seems to calculate the bearing fairly well. But the question here is how do you get the "left and right coordinates". Also, keep in mind, an oval track may be 60 feet wide. But as mentioned an auto cross track may only be 20 ft wide and part of the track may be with 50 ft. Note I'm fine with indicating to set the points, the car needs to be going slow or stopped at the points to get an accurate coordinate. Some tracks they will have to be set while walking the track.
Based on this, should I be trying to use decimal lat lon or would utilizing the Cartesian coordinate system based on UTM be a more accurate method for what I'm trying to do?
Either one is there a .Net library or C based library with source code that has methods for making these calculations?
How can this be accurately handled. (Not that great with Math, links to code samples would help tremendously.)
Next, after I have the lines or whatever is needed for start/finish and segments, as I get GPS sign from the car racing, I need to figure out the most accurate way to tell when a car has crossed those segments. again if I'm lucky I'll get 10 hits per second but it will probably be lower. Then the vehicle speeds could vary significantly depending on the type of track and vehicle. So the GPS hit could be many feet "left or right" of a segment. Also, it could be many feet before or after a segment.
Again, if there is a GIS library out there I can feed coordinates and all this is calculated, that's would work as well as long as it's performant. If not again I'm trying to decide if it's best to break down coordinates to X Y or some geometry formulas for coordinates in decimal format. Mods, I assume there is hard data to support an answer of either way and this isn't responses aren't fully subjective to opinions.
Here is the C# code I came up with from the Script page above. I'm starting to feel UTM and the Cartesian Coordinate system would be better for accuracy and performance. But again I'm open to evidence to the contrary if it exists.
Thanks
P.S. Note GeoCoordinate is from the .Net System.Device.Location assemble. GpsData is just a class I use to convert NMEA messages into Lat, Lon, Course, NumSats, DateTime etc.
The degree Radian methods are extensions as as follows.
public static double DegreeToRadians(this double angle)
{
return Math.PI * angle / 180.0;
}
public static double RadianToDegree(this double angle)
{
return angle * (180.0 / Math.PI);
}
}
public static GeoCoordinate CalculateIntersection(GpsData p1, double brng1, GpsData p2, double brng2)
{
// see http://williams.best.vwh.net/avform.htm#Intersection
// Not sure I need to use Cosine
double _p1LatRadians = p1.Latitude.DegreeToRadians();
double _p1LonToRadians = p1.Longitude.DegreeToRadians();
double _p2LatToRadians = p2.Latitude.DegreeToRadians();
double _p2LonToRadians = p2.Longitude.DegreeToRadians();
double _brng1ToRadians = brng1.DegreeToRadians();
double _brng2ToRadians = brng2.DegreeToRadians();
double _deltaLat = _p2LatToRadians - _p1LatRadians;
double _deltaLon = _p2LonToRadians - _p1LonToRadians;
var _var1 = 2 * Math.Asin(Math.Sqrt(Math.Sin(_deltaLat / 2) * Math.Sin(_deltaLat / 2)
+ Math.Cos(_p1LatRadians) * Math.Cos(_p2LatToRadians) * Math.Sin(_deltaLon / 2) * Math.Sin(_deltaLon / 2)));
if (_var1 == 0) return null;
// initial/final bearings between points
var _finalBrng = Math.Acos((Math.Sin(_p2LatToRadians) - Math.Sin(_p1LatRadians) * Math.Cos(_var1)) / (Math.Sin(_var1) * Math.Cos(_p1LatRadians)));
//if (isNaN(θa)) θa = 0; // protect against rounding
var θb = Math.Acos((Math.Sin(_p1LatRadians) - Math.Sin(_p2LatToRadians) * Math.Cos(_var1)) / (Math.Sin(_var1) * Math.Cos(_p2LatToRadians)));
var θ12 = Math.Sin(_p2LonToRadians - _p1LonToRadians) > 0 ? _finalBrng : 2 * Math.PI - _finalBrng;
var θ21 = Math.Sin(_p2LonToRadians - _p1LonToRadians) > 0 ? 2 * Math.PI - θb : θb;
var α1 = (_brng1ToRadians - θ12 + Math.PI) % (2 * Math.PI) - Math.PI; // angle 2-1-3
var α2 = (θ21 - _brng2ToRadians + Math.PI) % (2 * Math.PI) - Math.PI; // angle 1-2-3
if (Math.Sin(α1) == 0 && Math.Sin(α2) == 0) return null; // infinite intersections
if (Math.Sin(α1) * Math.Sin(α2) < 0) return null; // ambiguous intersection
α1 = Math.Abs(α1);
α2 = Math.Abs(α2);
// ... Ed Williams takes abs of α1/α2, but seems to break calculation?
var α3 = Math.Acos(-Math.Cos(α1) * Math.Cos(α2) + Math.Sin(α1) * Math.Sin(α2) * Math.Cos(_var1));
var δ13 = Math.Atan2(Math.Sin(_var1) * Math.Sin(α1) * Math.Sin(α2), Math.Cos(α2) + Math.Cos(α1) * Math.Cos(α3));
var _finalLatRadians = Math.Asin(Math.Sin(_p1LatRadians) * Math.Cos(δ13) + Math.Cos(_p1LatRadians) * Math.Sin(δ13) * Math.Cos(_brng1ToRadians));
var _lonBearing = Math.Atan2(Math.Sin(_brng1ToRadians) * Math.Sin(δ13) * Math.Cos(_p1LatRadians), Math.Cos(δ13) - Math.Sin(_p1LatRadians) * Math.Sin(_finalLatRadians));
var _finalLon = _p1LonToRadians + _lonBearing;
var _returnLat = _finalLatRadians.RadianToDegree();
var _latToDegree = _finalLon.RadianToDegree();
var _returnLon = ( _latToDegree + 540) % 360 - 180;
return new GeoCoordinate(_returnLat, _returnLon);
//return new LatLon(φ3.toDegrees(), (λ3.toDegrees() + 540) % 360 - 180); // normalise to −180..+180°
}
i want to render nice radial tree layout and a bit stumbled with curved edges. The problem is that with different angles between source and target points the edges are drawn differently. Provided pics are from the single graph so you can see how they're differ for different edge directions. I think the point is in beizer curve control points generation and i just can't understand how to fix them.
I want them to be drawn the same way no matter what's the direction of the edge.
How can i achieve this as in Pic1?
How can i achieve this as in Pic2?
Like here: https://bl.ocks.org/mbostock/4063550
Thank you!
Code:
//draw using DrawingContext of the DrawingVisual
//gen 2 control points
double dx = target.X - source.X, dy = target.Y - source.Y;
var pts = new[]
{
new Point(source.X + 2*dx/3, source.Y),
new Point(target.X - dx/8, target.Y - dy/8)
};
//get geometry
var geometry = new StreamGeometry { FillRule = FillRule.EvenOdd };
using (var ctx = geometry.Open())
{
ctx.BeginFigure(START_POINT, false /* is filled */, false /* is closed */);
ctx.BezierTo(pts[0], pts[1], END_POINT, true, false);
}
geometry.Freeze();
//draw it
dc.DrawGeometry(DrawingBrush, DrawingPen, geometry);
UPDATE 1:
I've got the angle between previous vertex and source in radians using the following formula: Math.Atan2(prev.Y - source.Y, source.X - prev.X);
But still i get the edges like in Pic.4.
UPDATE 2
The prev vertex pos for branchAngle calculation is inaccurate so i decided to take an average angle between all edges in a branch as the branchAngle. This approach fails when edges from one brach are around the 180 deg mark and branch can have edge angles like 175, 176.. -176!! I use this code to make them all positive:
var angle = Math.Atan2(point1.Y - point2.Y, point1.X - point2.X);
while (angle < 0d)
angle += Math.PI*2;
But now the angles can be 350, 359.. 2!!! Quite difficult to calc an average :) Can you please advice me how i can work this around?
Pic1
Pic2
Pic3
Pic4
Looking at the graph from the link you provided each branch in the tree has it's own angle, which is used to declare the control points of the branch. This branchAngle is the same as the one of the vector going from the first node to the previous one (every branch can spawn several branches in turn). The angle of the first branch (first node = previous node = center) seems to be around -60°.
Setting the type of curve can be done by compensating this branch angle (0°, -90°, -180°,...) for all branches in the tree. Resulting in the controlAngle used for laying out the control points.
Generating the control points while taking into account the angles:
//gen per branch
double branchAngle = 30 * Math.PI / 180; //e.g., calc vector angle here
double cosB = Math.Cos(branchAngle);
double sinB = Math.Sin(branchAngle);
//depending on the desired curve compensate -90°, -180°,...
double controlAngle = branchAngle - (90 * Math.PI / 180);
double cosA = Math.Cos(controlAngle);
double sinA = Math.Sin(controlAngle);
//gen 2 control points
//calculate dx dy after rotation with branchAngle
double dxbase = target.X - source.X, dybase = target.Y - source.Y;
double dx = dxbase*sinB - dybase*cosB
double dy = dxbase*cosB + dybase*sinB
//control points based on controlAngle
var pts = new[]
{
new Point(source.X + (2*dx/3)*cosA , source.Y + (2*dx/3)*sinA),
new Point(target.X - (dx/8)*cosA + (dy/8)*sinA, target.Y - (dx/8)*sinA - (dy/8)*cosA)
};
Quick check
branchAngle = 30° &
compensation = -90° ->
controlAngle = -60°
Hi I have a problem calculating the area of a polygon in Bing maps. I'm using this code to calculate area.
public static double PolygonArea(LocationCollection points, double resolution)
{
int n = points.Count;
var partialSum = 0.0;
var sum = 0.0;
for (int i = 0; i < n - 1; i++)
{
partialSum = (points[i].Longitude * points[i + 1].Latitude) -
(points[i + 1].Longitude * points[i].Latitude);
sum += partialSum;
}
var area = 0.5 * sum / Math.Pow(resolution, 2);
area = Math.Abs(area);
return area;
}
This is the resolution method
public static double Resolution(double latitude, double zoomLevel)
{
double groundResolution = Math.Cos(latitude * Math.PI / 180) *
2 * Math.PI * EARTH_RADIUS_METERS / (256 * Math.Pow(2, zoomLevel));
return groundResolution;
}
How can I trasform it in m^2?
EDIT1: I tried your answer but I noticed that area change if I change zoom level.
I try to explain my problem from another point of you. I have to make the porting of an iOS app that uses this algorithm to calculate area
-(long double)calcArea :(CLLocationCoordinate2D*) pastureCordinates :(long) count {
long double area = 0.0;
long double scale = 0.0;
for (int cnt = 1; cnt < count; cnt++) {
area += (MKMapPointForCoordinate(pastureCordinates[cnt - 1]).x)
* (MKMapPointForCoordinate(pastureCordinates[cnt]).y)
- (MKMapPointForCoordinate(pastureCordinates[cnt]).x)
* (MKMapPointForCoordinate(pastureCordinates[cnt - 1]).y);
}
area += (MKMapPointForCoordinate(pastureCordinates[count - 1]).x)
* (MKMapPointForCoordinate(pastureCordinates[0]).y)
- (MKMapPointForCoordinate(pastureCordinates[0]).x)
* (MKMapPointForCoordinate(pastureCordinates[count - 1]).y);
scale = MKMapPointsPerMeterAtLatitude(pastureCordinates[count -1].latitude);
area = (area / (long double)2) / pow(scale,2);
area = fabsl(area);
return area;
}
I used the functions found here: https://msdn.microsoft.com/en-us/library/bb259689.aspx to calculate the scale, the ground resolution but the results are different compared to the iOS solution.
Ok, I've played around with some code and put together a simple method that calculates the area fairly accurately without having to use really in-depth spatial mathematics.
private double CalculateArea(LocationCollection locs)
{
double area = 0;
for (var i = 0; i < locs.Count - 1; i++)
{
area += Math.Atan(
Math.Tan(Math.PI / 180 * (locs[i + 1].Longitude - locs[i].Longitude) / 2) *
Math.Sin(Math.PI / 180 * (locs[i + 1].Latitude + locs[i].Latitude) / 2) /
Math.Cos(Math.PI / 180 * (locs[i + 1].Latitude - locs[i].Latitude) / 2));
}
if (area < 0)
{
area *= -1;
}
return area * 2 * Math.Pow(6378137.0, 2);
}
Testing this with various polygons and comparing them to the calculated area in SQL, I found that in the worse case the difference was about 0.673% when using a ridiculously large polygon. When testing against a polygon that was about 0.5 sq KM in size, the difference was about 0.06%. Note that this method returns an area in sq meters.
Calculating the area of a polygon on a map is very complicated as the world is a sphere and you are actually trying to calculate the area of a polygon stretched on the surface of a sphere. Since you are using WPF I'd suggest to make things easy and make use of the spatial library available in SQL server. All the spatial functionalities in SQL server are available as a dll which you can use in your WPF application. You can easily use this library to calculate the area of a polygon accurately and do a lot of other really powerful things as well. To start off with, if you have SQL instelled you can find the SQL Spatial Library (Microsoft.SqlServer.Types) located in the C:\Program Files (x86)\Microsoft SQL Server\110\Shared directory. If you don't have SQL Server installed, don't worry, you don't have to install it, this library is available as a Nuget package here: https://www.nuget.org/packages/Microsoft.SqlServer.Types
Take a look at this hands on lab for information using SQL spatial tools in .NET: http://view.officeapps.live.com/op/view.aspx?src=http%3A%2F%2Fecn.channel9.msdn.com%2Fo9%2Flearn%2FSQL2008R2TrainingKit%2FLabs%2FUsingSpatialDataInManagedCode%2FLab.docx
Once you have this library you can create an SQL Geography object from your polygon. Once this is done you can then use the STArea method to calculate the area of the polygon. There is a ton of other spatial methods available as well which you can use to create a really powerful mapping application.
I am using a transform in Revit to show elevation views of individual beams (for the purpose of detailing). This works fine as long as the beam is flat (identical start and end offsets), but if I have a beam that is sloping, I am forced to "flatten" the endpoints.
I tried to define a unit vector along the actual start/end points, and a perpendicular to that vector on an XY plane running through the defined ".Origin" of the transform. I then used simple equations to define a normal to those two vectors...
double newx = first.Y * second.Z - first.Z * second.Y;
double newy = first.Z * second.X - first.X * second.Z;
double newz = first.X * second.Y - first.Y * second.X;
double vectlong = Math.Sqrt(newx * newx + newy * newy + newz * newz);
XYZ normal = new Autodesk.Revit.DB.XYZ(newx / vectlong, newy / vectlong, newz / vectlong);
I then used those three vectors as my ".set_Basis" 0, 1 & 2.
This code works as long as I've forced the beam's start and end points to be flat (which shows that the generated "normal" is valid), but when I remove the code to flatten and use the actual Z values of the endpoints of a sloping beam, the program fails when I try to use these values.
The SDK sample to generate a section through the middle of a beam (CreateViewSection) seems to have found the same problem, but the programmer gave up and simply forces the program to accept only beams that are already on the same XY plane, which is not really the "rule" for beams.
I exported the calculated values of my three vectors and verified that they were all unit length and orthonormal, which should be all that is required for the transform. Can anyone explain why these basis values fail?
Please use this code to set assembly transform. It will align assembly origin and axis properly so that assembly views are always aligned to XYZ axis!
var assyTransform = Transform.Identity;
var beamInst = mainElement as FamilyInstance;
if( beamInst != null )
{
assyTransform = beamInst.GetTransform();
assyTransform.Origin = ( assyInstance.Location as LocationPoint ).Point;
}
if ( !assyInstance.GetTransform()
.AlmostEqual( assyTransform ) )
{
assyInstance.SetTransform( assyTransform );
return true;
}