Detect Pushpins Inside a VE Shape on a Bing Map - c#

I have a map which populates pushpins from a database. And a circle. I want to be able to figure out what other pushpins are within the VE Shape circles radius and create a list of their addresses.
function addPushPin(lat, long) {
//var point = new Microsoft.Maps.Point(e.getX(), e.getY());
//var pushpinLocation = new(lat, long);
var s = new VEShape(VEShapeType.Pushpin, new VELatLong(lat,long));
var customIcon = new VECustomIconSpecification();
//customIcon.TextContent = "yo bana4na4";
customIcon.Image = "http://universidadescancun.com/wp-content/themes/GeoUnis3/images/pinpoint-fiesta.png";
//customIcon.ImageOffset = new VEPixel();
s.SetTitle("First Pin <span style=color:red>Demo Title<>/span>");
s.SetDescription("This comes to the <b>description</b> of pushpin.");
s.SetMoreInfoURL("http://dotnetricks.blogspot.com");
s.SetPhotoURL("http://upload.wikimedia.org/wikipedia/commons/8/8a/Banana-Single.jpg");
s.SetCustomIcon(customIcon);
s.SetIconAnchor(new VELatLong(lat, long));
map.AddShape(s);
}
I also have my map populate a circle around one of these pushpins. It is populated like so.
function getCircleNow(lat, long) {
var latin = lat;
var lonin = long;
var latlong = new VELatLong(latin, lonin);
centerPoint = (latlong);
//var center = map.GetCenter().toString();
//alert(center);
//Get initial circle points
var circlePoints = buildCircle(centerPoint.Latitude, centerPoint.Longitude, 50.74);
//Build circle
circle = new VEShape(VEShapeType.Polygon, circlePoints);
circle.HideIcon();
circle.SetLineWidth(2);
map.AddShape(circle);
//Build mask
circleMask = new VEShape(VEShapeType.Polygon, circlePoints);
circleMask.HideIcon();
circleMask.Hide();
circleMask.SetLineColor(new VEColor(0, 0, 0, 0.5));
circleMask.SetFillColor(new VEColor(0, 0, 0, 0.0));
circleMask.Primitives[0].symbol.stroke_dashstyle = "Dash";
map.AddShape(circleMask);
}
And here is the buildCircle() function it calls.
function buildCircle(latin, lonin, radius) {
var locs = new Array();
var lat1 = latin * Math.PI / 180.0;
var lon1 = lonin * Math.PI / 180.0;
var d = radius / 3956;
var x;
for (x = 0; x <= 360; x += 10) {
var tc = (x / 90) * Math.PI / 2;
var lat = Math.asin(Math.sin(lat1) * Math.cos(d) + Math.cos(lat1) * Math.sin(d) * Math.cos(tc));
lat = 180.0 * lat / Math.PI;
var lon;
if (Math.cos(lat1) == 0) {
lon = lonin; // endpoint a pole
}
else {
lon = ((lon1 - Math.asin(Math.sin(tc) * Math.sin(d) / Math.cos(lat1)) + Math.PI) % (2 * Math.PI)) - Math.PI;
}
lon = 180.0 * lon / Math.PI;
var loc = new VELatLong(lat, lon);
locs.push(loc);
}
return locs;
}
This is what my map looks like after I have added the pushpins and I have populated the circle on the map, with it centered around one of the locations and with a 50 mile radius.
WHAT I WANT TO BE ABLE TO DO
I want to be able to use either JavaScript or C# to detect what other pushpins, besides the one the circle is centered around, are within the radius of the center location. Or more simply, what other locations/pushpins are in the circle, and populate a list with values of each address. I can't think of any special methods that can do this so any help would be appreciated. This is Bing maps That I'm using.

This is fairly easy to do. Take the center point and radius of the circle do the following:
Loop through all pushpins and measure their distance from the center point using the Haversine formula: http://rbrundritt.wordpress.com/2008/10/14/calculate-distance-between-two-coordinates/
If the distance is less than or equal to the circles radius then the pushpin is inside the circle.
That's it. I also wrote an MSDN article on this for v6 of Bing maps a long time ago but it looks like it has been taken offline, along with all the documentation for v6.3 of Bing maps.
With all this said, its worth pointing out that Bing Maps V6.3 is really old and was last updated about 5 or 6 years ago. It's not recommended to do any new development on it but to instead make use of version 7 which is a lot more powerful, has more features, and is significantly smaller in terms of download size. I have a migration guide here: http://social.technet.microsoft.com/wiki/contents/articles/20958.migrating-bing-maps-v6-3-to-v7.aspx

Related

Translate geo coordinates to pixels in static google maps image using GMap.NET

I am trying to create a static map from geo location ???,???
and fetch POI (places of interest) near the location, their geo location is required so I think I need to do a separate query for them and not just set markers for them in the static search.
//transform static map center to pixels
var center = projection.mercator.FromLatLngToPixel(
worldPosition.latitude,
worldPosition.longitude,
zoom
);
//iterate places
foreach (Place place in map.places)
{
//transform place location to pixels
var point = projection.mercator.FromLatLngToPixel(
place.location.latitude,
place.location.longitude,
zoom
);
//calculate pixel position relative to the image center
var pointRelativeToImage = center - point;
}
However, the results are slightly incorrect, so my question is how I do this right?
This is the method that calculates the pixel position: (GMaps.Net.Projections.MercatorProjection)
public override GPoint FromLatLngToPixel(double lat, double lng, int zoom)
{
GPoint ret = GPoint.Empty;
lat = Clip(lat, MinLatitude, MaxLatitude);
lng = Clip(lng, MinLongitude, MaxLongitude);
double x = (lng + 180) / 360;
double sinLatitude = Math.Sin(lat * Math.PI / 180);
double y = 0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI);
GSize s = GetTileMatrixSizePixel(zoom);
long mapSizeX = s.Width;
long mapSizeY = s.Height;
ret.X = (long)Clip(x * mapSizeX + 0.5, 0, mapSizeX - 1);
ret.Y = (long)Clip(y * mapSizeY + 0.5, 0, mapSizeY - 1);
return ret;
}
Solved the problem.
The slightly incorrect results were caused because i was calculating the pixels relative to the center pixel, when i should have calculated them relative to top left pixel. (0,0) pixel in google maps.
Solution:
//translate place to pixels
var needlePoint = m_mercator.FromLatLngToPixel(needle.latitude,
needle.longitude,
zoom);
//translate map center to pixel
var haystackPoint = m_mercator.FromLatLngToPixel(haystackCenter.latitude,
haystackCenter.longitude,
zoom);
//calculate the top left pixel
var haystackCorner = new Point(haystackPoint.x-(imageSize.x/2),
haystackPoint.y-(imageSize.y/2));
//translate the pixel to local space relative to the map
var point = new Point(needlePoint.x - haystackCorner.x,
needlePoint.y - haystackCorner.y);
This works assuming the 0,0 pixel on the image is on the top left corner, apparently the image API i was using had 0,0 pixel on the bottom left corner.

Calculation of Center Point from List of Latitude and Longitude are slightly diffrent from actual

I am calculating center Lat/Lng fron list of available Lat/Lng using C# and rendering on OpenLayer Map.
I observed the calculation of getting center lat/lng will give me slightly difference in lat/lng. I am referring this link for the calculation
Calculate the center point of multiple latitude/longitude coordinate pairs.
C# Code:
static void Main(string[] args)
{
List<GeoCoordinate> listCoordinate = new List<GeoCoordinate>();
listCoordinate.Add(new GeoCoordinate() { Latitude = 22.9833, Longitude = 72.5000 }); //Sarkhej
listCoordinate.Add(new GeoCoordinate() { Latitude = 18.9750, Longitude = 72.8258 }); //Mumbai
listCoordinate.Add(new GeoCoordinate() { Latitude = 22.3000, Longitude = 73.2003 }); //Vadodara
listCoordinate.Add(new GeoCoordinate() { Latitude = 26.9260, Longitude = 75.8235 }); //Jaipur
listCoordinate.Add(new GeoCoordinate() { Latitude = 28.6100, Longitude = 77.2300 }); //Delhi
listCoordinate.Add(new GeoCoordinate() { Latitude = 22.3000, Longitude = 70.7833 }); //Rajkot
GeoCoordinate centerCoordinate = GetCentralGeoCoordinate(listCoordinate); //Output (Latitude:23.696708071960074, Longitude:73.681549202080149)
Console.WriteLine("Lat:" + centerCoordinate.Latitude + ",Lon:" + centerCoordinate.Longitude);
Console.ReadKey();
}
public static GeoCoordinate GetCentralGeoCoordinate(List<GeoCoordinate> geoCoordinates)
{
if (geoCoordinates.Count == 1)
{
return geoCoordinates.Single();
}
double x = 0, y = 0, z = 0;
foreach (var geoCoordinate in geoCoordinates)
{
var latitude = geoCoordinate.Latitude * Math.PI / 180;
var longitude = geoCoordinate.Longitude * Math.PI / 180;
x += Math.Cos(latitude) * Math.Cos(longitude);
y += Math.Cos(latitude) * Math.Sin(longitude);
z += Math.Sin(latitude);
}
var total = geoCoordinates.Count;
x = x / total;
y = y / total;
z = z / total;
var centralLongitude = Math.Atan2(y, x);
var centralSquareRoot = Math.Sqrt(x * x + y * y);
var centralLatitude = Math.Atan2(z, centralSquareRoot);
return new GeoCoordinate(centralLatitude * 180 / Math.PI, centralLongitude * 180 / Math.PI);
}
Javascrip Code:
var arrLonLat = [
{'Lon' : 72.5000, 'Lat' : 22.9833},
{'Lon' : 72.8258, 'Lat' : 18.9750},
{'Lon' : 73.2003, 'Lat' : 22.3000},
{'Lon' : 75.8235, 'Lat' : 26.9260},
{'Lon' : 77.2300, 'Lat' : 28.6100},
{'Lon' : 70.7833, 'Lat' : 22.3000}];
var centerLonLat = getCenterLonLat(arrLonLat);
var lonLatSarkhej = new OpenLayers.LonLat(arrLonLat[0].Lon,arrLonLat[0].Lat).transform(epsg4326,projectTo);
var lonLatMumbai = new OpenLayers.LonLat(arrLonLat[1].Lon,arrLonLat[1].Lat).transform(epsg4326,projectTo);
var lonLatVadodara = new OpenLayers.LonLat(arrLonLat[2].Lon,arrLonLat[2].Lat).transform(epsg4326,projectTo);
var lonLatJaipur = new OpenLayers.LonLat(arrLonLat[3].Lon,arrLonLat[3].Lat).transform(epsg4326,projectTo);
var lonLatDelhi = new OpenLayers.LonLat(arrLonLat[4].Lon,arrLonLat[4].Lat).transform(epsg4326,projectTo);
var lonLatRajkot = new OpenLayers.LonLat(arrLonLat[5].Lon,arrLonLat[5].Lat).transform(epsg4326,projectTo);
//Center Point of Average Markers
var lonLatCenter = new OpenLayers.LonLat(73.681549202080149,23.696708071960074).transform(epsg4326,projectTo);
var markers = new OpenLayers.Layer.Markers("Markers");
map.addLayer(markers);
var size = new OpenLayers.Size(24,24);
var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
var icon = new OpenLayers.Icon('icon/Marker-Pink.png', size, offset);
var iconCenter = new OpenLayers.Icon('icon/Marker-Green.png', size, offset);
markers.addMarker(new OpenLayers.Marker(lonLatSarkhej,icon)); //Sarkhej
markers.addMarker(new OpenLayers.Marker(lonLatMumbai,icon.clone())); //Mumbai
markers.addMarker(new OpenLayers.Marker(lonLatVadodara,icon.clone())); //Vadodara
markers.addMarker(new OpenLayers.Marker(lonLatJaipur,icon.clone())); //Jaipur
markers.addMarker(new OpenLayers.Marker(lonLatDelhi,icon.clone())); //Delhi
markers.addMarker(new OpenLayers.Marker(lonLatRajkot,icon.clone())); //Rajkot
I am rendering 6 different location with Pink marker, and center with Green marker.
Please see below image for more clarification.
Now have drawn box to get idea about the center marker (Green) which is not actually center. I think it should be positioned on Green dot which is crossed by green Horizontal and Vertical line.
Can anybody let me know, does my center point is calculated correctly
or not?
Why it is not display at center of the box?
I have also added Ruler for calculation of center point.
Please help me to find the actual solution, please do let me know if you need more details for the same.
The correct center depends actually on the definition for center and there are several possibilites:
One might calculate the smallest rectangle containing all points and
use the center of this rectangle as you did with your green lines
and green point. (This is unusual)
One might ask which point is closest to all other points, i.e.
find a center point so that all distances between this center and
all other points are smallest (abs-norm, used for practical
problems)
One might ask which center point results in the least error, i.e.
the smallest (sum of) quadratic distances to all other points
(used ins math, statistics, etc)
You see, depending on the definition, you'll have to use a different algorithm and will arrive on a different center point.
The algorithm you show in your question seems to calculate (2.)

Drawings Map app coordinates WGS84

How to deal with big, WGS84 points coordinates?
What I think, I can do, is to translate world (WGS84) points, to points on screen in pixels. Is it a good way? It still doesn't work corectly because big zoom is needed, and I would have to change unit from meters to milimeters (but how? just multiplying the x, and y of points?).
Here's a very simplistic approach for this mapping problem. Geographers will probably cry about this, but it works quite well in practice as long as the coordinates are below about 70° latitude and the size of the window is not too big. Also, don't try to directly map large objects (such as very long lines) only using their start and end points.
public PointF GeoCoordToPixel(IGeographicPosition geoPos)
{
double tempLong = geoPos.Longitude;
if (tempLong > CenterPos.Longitude && (tempLong - CenterPos.Longitude) > 180)
{
// the position is to the left, over the antimeridian
tempLong = tempLong - 360;
}
if (tempLong < CenterPos.Longitude && (CenterPos.Longitude - tempLong) > 180)
{
// the position is to the right, over the antimeridian
tempLong = tempLong + 360;
}
PointF pt = new PointF(
(float)((tempLong - LongitudeOfOrigin) / LongitudeIncrement),
(float)((-geoPos.Latitude + LatitudeOfOrigin) / LatitudeIncrement));
return pt;
}
with CenterPos = Center of window; LatituteOfOrigin / LongitudeOfOrigin = Top left position of window; LongitudeIncrement / LatitudeIncrement = Scale of view. Their relation is:
LatitudeOfOrigin = CenterPos.Latitude + (m_drawingBuffer.Height / 2.0 * LatitudeIncrement);
LongitudeOfOrigin = CenterPos.Longitude - (m_drawingBuffer.Width / 2.0 * LongitudeIncrement);
and the inverse:
public CGeographicPosition PixelToGeoCoord(PointF pt)
{
double latitude = -(pt.Y * LatitudeIncrement) + LatitudeOfOrigin;
double longitude = (pt.X * LongitudeIncrement) + LongitudeOfOrigin;
if (longitude > 180)
{
longitude -= 360;
}
return (new CGeographicPosition(latitude, longitude, 0));
}
Not really difficult, is it?

What library for c# to use for the graphs of a Roundness-meter software?

I'm working on a metrology laboratory and i need to develop a software in c# for a roundness-meter equipment, I've already started and I've found a problem, I need the software to show real-time graphics from the measurement that is being made, for that I'll need to use a library like Mscharts or Zedgraph, that is really fast for refreshing the information and has support for round graphs like Polar or radar, especially polar charts.
The problem I've seen in most libraries is that they all lack support for round graphs and are relatively slow.
Does anyone has a sugestion of a lybrary I could use?
Thank you for your help.
PS:The Software should show graphics like these ones:
I would render them with GDI(+) (in winforms app).
Yes that is basic line drawing, but it will be powerful enough for the examples you gave. You need to refresh your highschool mathematics, but it will give you lots of control over the output and it will be fast.
Not sure if this will help or not. Xceed Charts in their advanced section talks about doing polar charts. Unfortunately they did not provide any images so you should talk to their sales people and see if you can get an eval copy to evaluate.
Consider using the roguewave IMSL Numerical .NET library
homepage of the IMSL numerical .NET library
Examples of graph, resembling what you have posted above
Especially the Polar plot seems to be what you need here.
I was surprised that ZedGraph doesn't support polar graphs out-of-the-box and there are very few examples online. Using this guildline, I created my own polar graph with ZedGraph in C#. I hope that WillKraemer has solved his problem already (4 years passed) and someone else finds my implementation useful.
First is the ZedGraphControl initialization:
myZg = new ZedGraphControl();
GraphPane myPane = myZg.GraphPane;
// Init lists
RadarPointList dseries1 = new RadarPointList();
RadarPointList dseries2 = new RadarPointList();
// Maximize available space in pane
myPane.Legend.Position = LegendPos.InsideTopLeft;
myPane.Title.IsVisible = false;
myPane.XAxis.IsVisible = false;
myPane.YAxis.IsVisible = false;
myPane.Border.IsVisible = false;
myPane.Chart.Border.IsVisible = false;
myPane.Margin.All = 0;
// Create concentric grid with 30 degrees spacing & add corresponding labels
for (double i = 0; i < 36; i+=3.0)
{
TextObj gridlbs = new TextObj((i * 10.0).ToString("0°"), (radius + 10.0) * Math.Cos((i * 10.0 * Math.PI) / 180.0), (radius + 10.0) * Math.Sin((i * 10.0 * Math.PI) / 180.0));
gridlbs.FontSpec.Border.IsVisible = false;
LineObj gridlns = new LineObj(0, 0, radius * Math.Cos((i * 10.0 * Math.PI) / 180.0), radius * Math.Sin((i * 10.0 * Math.PI) / 180.0));
myPane.GraphObjList.Add(gridlbs);
myPane.GraphObjList.Add(gridlns);
}
// Draw circular grid, 5 should look okay
for (double i = (radius / 5.0); i <= radius; i += (radius / 5.0))
{
EllipseObj gridcrl = new EllipseObj(-i, i, 2.0 * i, 2.0 * i);
gridcrl.ZOrder = ZOrder.E_BehindCurves;
myPane.GraphObjList.Add(gridcrl);
}
// Make sure the pane is big enough to fit the labels around the polar plot
myPane.XAxis.Scale.Min = -(radius + 20.0);
myPane.XAxis.Scale.Max = (radius + 20.0);
myPane.YAxis.Scale.Min = -(radius + 20.0);
myPane.YAxis.Scale.Max = (radius + 20.0);
_selectedRadius = radius;
// Keep X & Y axis in the correct ratio to avoid distorting polar circle
myZg_Resize((object)"Startup", EventArgs.Empty);
myZg.Resize += new EventHandler(myZg_Resize);
myZg.ZoomEvent += new ZedGraphControl.ZoomEventHandler(myZg_ZoomEvent2);
// Draw snailly curves (example)
for (int i = 0; i < 360; i++)
{
double r = (double)i/360.0 * radius;
PointPair pt = new PointPair(PointPair.Missing, r, null);
dseries1.Add(pt);
PointPair pt2 = new PointPair(PointPair.Missing, radius - r, null);
dseries2.Add(pt2);
}
// Curves are somple LineItem
FirstCurve = myPane.AddCurve("Snail", dseries1, Color.Blue, SymbolType.None);
SecondCurve = myPane.AddCurve("antiSnail", dseries2, Color.Red, SymbolType.None);
// Rotate the lists to aling with labels
dseries1.Rotation = 0;
dseries2.Rotation = 0;
I had to make sure that the graph is not distorted when the form/control resizes, so I added this in the Resize Event:
protected void myZg_Resize(object sender, EventArgs e)
{
GraphPane pane = myZg.GraphPane;
myZg.AxisChange();
bool IsXMin = ( pane.Rect.Width < pane.Rect.Height ) ? true : false;
if (IsXMin)
{
// Scale based on X (width)
pane.XAxis.Scale.Max = (radius + 20.0); pane.XAxis.Scale.Min = -(radius + 20.0);
double xPixPerUnit = (double)pane.Chart.Rect.Width / (pane.XAxis.Scale.Max - pane.XAxis.Scale.Min);
pane.YAxis.Scale.Max = (double)pane.Chart.Rect.Height / xPixPerUnit / 2.0;
pane.YAxis.Scale.Min = -pane.YAxis.Scale.Max;
myZg.AxisChange();
}
else
{
// Scale based on Y (height)
pane.YAxis.Scale.Max = (radius + 20.0); pane.YAxis.Scale.Min = -(radius + 20.0);
double yPixPerUnit = (double)pane.Chart.Rect.Height / (pane.YAxis.Scale.Max - pane.YAxis.Scale.Min);
pane.XAxis.Scale.Max = (double)pane.Chart.Rect.Width / yPixPerUnit / 2.0;
pane.XAxis.Scale.Min = -pane.XAxis.Scale.Max;
myZg.AxisChange();
}
}
Also, I decided to block the user from any zooming actions:
protected void myZg_ZoomEvent2(ZedGraphControl sender, ZoomState oldState, ZoomState newState)
{
myZg_Resize("zoomevent", EventArgs.Empty);
}
The output looks like the picture below:
Suggestions always welcome!

Create a SqlGeography polygon-circle from a center and radius

I would like to save a circle in a sql-server 2008 geography field, using c#.
In c# I have a latitude, a longitude and a radius but I just can't find a way to calculate the polygon that would represent the circle and create a SqlGeography from it.
I have tried to following function to create the polygon:
private List<Coordinate> getCirclePoints(Coordinate center, int radius, int speed) //speed 1: draws 360 sides, 2 draws 180 etc...
{
var centerLat = (center.Latitude * Math.PI) / 180.0; //rad
var centerLng = (center.Longitude * Math.PI) / 180.0; //rad
var dist = (float)radius / 6371.0; //d = angular distance covered on earth's surface
var circlePoints = new List<Coordinate>();
for (int x = 0; x <= 360; x += speed)
{
var brng = x * Math.PI / 180.0; //rad
var latitude = Math.Asin(Math.Sin(centerLat) * Math.Cos(dist) + Math.Cos(centerLat) * Math.Sin(dist) * Math.Cos(brng));
var longitude = ((centerLng + Math.Atan2(Math.Sin(brng) * Math.Sin(dist) * Math.Cos(centerLat), Math.Cos(dist) - Math.Sin(centerLat) * Math.Sin(latitude))) * 180.0) / Math.PI;
circlePoints.Add(new Coordinate((latitude * 180.0) / Math.PI, longitude));
}
return circlePoints;
}
And then try to convert this List<Coordinate> to a parsable string:
var s = "POLYGON((" + string.Join(",", points.ConvertAll(p => p.Longitude + " " + p.Latitude).ToArray()) + "))";
var poly = SqlGeography.STPolyFromText(new System.Data.SqlTypes.SqlChars((SqlString)s), 4326);
But it always complains the polygon has to be on a single hemisphere, where I'm sure it is the case.
Am I on the right track at all? Is there any other (simpler) way to do this?
OK, found the answer on my own. The trick is to create a point
var point = SqlGeography.Point(latitude, longitude, 4326);
Then create a buffer around the point
var poly = point.BufferWithTolerance(radiusInMeter, 0.01, true); //0.01 is to simplify the polygon to keep only a few sides
Then you could simply create a SqlCommand and add the polygon as parameter:
var param = new SqlParameter(#"Polygon", poly);
param.UdtTypeName = "Geography";
command.Parameters.Add(param);
Hope that will help someone else in the future!

Categories