Draw a circle on the MapControl - c#

I am aware of technique to draw a circle by generating points by basic math and then use the points to create a MapPolygon.
Is there another way?
For example, I see there is a class for circles:
public sealed class Geocircle : IGeocircle, IGeoshape
But I don't know how to use it, there doesn't seem to be any MapLayer for it.

Geocircle is used to create a geographic circle object for the given position and radius. It often use to make map Geofence, but not to display the cycle on the map.
There many way that draw a circle on the map
generating points by basic math
for (var i = 0; i < 360; i++)
{
//draw a cycle
BasicGeoposition point = new BasicGeoposition()
{ Latitude = centerLatitude + ri * Math.Cos(3.6 * i * 3.14 / 180), Longitude = centerLongitude + ri * Math.Sin(3.6 * i * 3.14 / 180) };
list.Add(point);
}
Add Ellipse to map
private void MyMap_Loaded(object sender, RoutedEventArgs e)
{
// Specify a known location.
BasicGeoposition snPosition = new BasicGeoposition { Latitude = 47.620, Longitude = -122.349 };
Geopoint snPoint = new Geopoint(snPosition);
// Create a XAML border.
var ellipse1 = new Ellipse();
ellipse1.Fill = new SolidColorBrush(Windows.UI.Colors.Coral);
ellipse1.Width = 200;
ellipse1.Height = 200;
// Center the map over the POI.
MyMap.Center = snPoint;
MyMap.ZoomLevel = 14;
// Add XAML to the map.
MyMap.Children.Add(ellipse1);
MapControl.SetLocation(ellipse1, snPoint);
MapControl.SetNormalizedAnchorPoint(ellipse1, new Point(0.5, 0.5));
}

You can create XAML shapes and add them as a child to the MapControl. Then you set their location using SetLocation:
var circle = new Ellipse() {
Height = 20,
Width = 20,
Fill = new SolidColorBrush(Colors.Blue)
};
map.Children.Add(circle);
var location = new Geopoint(new BasicGeoposition()
{
Latitude = 51.1789,
Longitude = -1.8261
});
map.SetLocation(circle, location);
The Geocircle class is used for geofencing, so it is not applicable here.

Related

How to make a circle type buffer around NetTopologySuite Point with radius in N meters?

I have to draw a circle with N meters diameter around some geolocation.
I could able to generate coordinates for vertically oriented ellipse only.
C#
using NetTopologySuite;
//
const int SRID = 4326;
var fact = new GeometryFactory(new PrecisionModel(), SRID);
var point = fact.CreatePoint(new Coordinate(lat, long));
var bufferParameters = new BufferParameters();
var poly = point.Buffer(0.0005, bufferParameters) as NetTopologySuite.Geometries.Polygon;
var coords = new List<BasicGeoposition>();
foreach (var cItem in poly.Coordinates)
{
coords.Add(new BasicGeoposition() { Latitude = cItem.X, Longitude = cItem.Y });
}
Please help me to understand how to set the radius and make circle instead of an ellipse.

Usage of a Picture instead of Rectangle Shape in C#

Background: I am currently busy with showing position of a Vehicle on a Zoomable Canvas based on the Position (X,Y) and Orientation (for Rotation). I use Rectangle for visualizing the vehicle. Everything works well but I got a bit greedy and now I want to replace the Rectangle with Top View Picture of the Vehicle, so it looks that the vehicle itself is moving instead a Rectangle.
Code Below:
private void PaintLocationVehicle(VehicleClass vc)
{
IEnumerable<Rectangle> collection = vc.ZoomableCanvas.Children.OfType<Rectangle>().Where(x => x.Name == _vehicleobjectname);
List<Rectangle> listE = collection.ToList<Rectangle>();
for (int e = 0; e < listE.Count; e++)
vc.ZoomableCanvas.Children.Remove(listE[e]);
// Assign X and Y Position from Vehicle
double drawingX = vc.gCurrentX * GlobalVar.DrawingQ;
double drawingY = vc.gCurrentY * GlobalVar.DrawingQ;
// Scale Length and Width of Vehicle
double tractorWidthScaled = vc.tractorWidth * GlobalVar.DrawingQ;
double tractorLengthScaled = vc.tractorLength * GlobalVar.DrawingQ;
// Get Drawing Location
double _locationX = drawingX - (tractorLengthScaled / 2);
double _locationY = drawingY - ((tractorWidthScaled / 2));
RotateTransform rotation = new RotateTransform();
// Angle in 10th of a Degree
rotation.Angle = vc.gCurrentTheeta/10 ;
double i = 0;
//paint the node
Rectangle _rectangle = new Rectangle();
_rectangle.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString(vc.VehicleColor == "" ? "Black" : vc.VehicleColor));
_rectangle.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString(vc.VehicleColor == "" ? "Black" : vc.VehicleColor));
i += 0;
_rectangle.Width = tractorLengthScaled ;
_rectangle.Height = tractorWidthScaled;
rotation.CenterX = _rectangle.Width / 2;
rotation.CenterY = _rectangle.Height / 2;
_rectangle.RenderTransform = rotation;
Canvas.SetTop(_rectangle, _locationY + i);
Canvas.SetLeft(_rectangle, _locationX + i);
_rectangle.SetValue(ZoomableCanvas.ZIndexProperty, 2);
string _tooltipmsg = "Canvas: " + vc.ZoomableCanvas.Name;
// Assign ToolTip Values for User
_tooltipmsg += "\nX: " + vc.gCurrentX;
_tooltipmsg += "\nY: " + vc.gCurrentY;
_rectangle.ToolTip = _tooltipmsg;
_rectangle.Name = _vehicleobjectname;
//add to the canvas
vc.ZoomableCanvas.Children.Add(_rectangle);
}
Note: VehicleClass holds all the Values for a certain Vehicle. DrawingQ holds the transformation scale from Reality to Zoomable Canvas.
So the issues I forsee:
How to append the Size of a Jpeg file to get the size same as
Rectangle?
What kind of Shape object shall I use? Please
suggest.
If i undrestand you correctly. you wanted to show an image of the vechicle inside the rectangle. in order to do that you can use
ImageBrush and assign to the Rectangle Fill property
something like this
Rectangle rect = new Rectangle();
rect.Width = 100;
rect.Height = 100;
ImageBrush img = new ImageBrush();
BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
bmp.UriSource = new Uri("vehicle image path");
bmp.EndInit();
img.ImageSource = bmp;
rect.Fill = img;
I hope that helps

Drawing point geometry with StreamGeometryContext

Trying to draw a polar type chart using GeometryContext in C#. I have input of direction as an xRange (start & stop) which is in degrees. I'm converting this to Radians. All good. The yRange is cut in & cut out wind speed currently in m/s as a double. I'm trying to acheive a simplified version of the image below without the axis labels etc.
For each object to be charted I am returning an XY range:
public IEnumerable<Styled2DRange> Query()
{
SolidColorBrush brush = new SolidColorBrush(Colors.Maroon);
brush.Freeze();
Pen linePen = new Pen(brush, 3);
linePen.Freeze();
SolidColorBrush fillBrush = new SolidColorBrush(Colors.Maroon);
fillBrush.Freeze();
foreach (var range in this.Charts)
{
Range xRange = new Range(ConvertToRadians(range.StartDirection), ConvertToRadians(range.EndDirection));
Range yRange = new Range(range.CutInWindSpeed, range.CutOutWindSpeed);
yield return new 2DRange()
{
Range = new XYRange()
{
XRange = xRange,
YRange = yRange
},
Line = linePen,
Fill = fillBrush
};
}
yield break;
}
This method is called from my override of Onrender. Obviously my points to be drawn by the StreamGeometryContext don't make sense as the Range.Y values are just wind speeds in m/s:
protected override void OnRender(DrawingContext dc)
{
Point origin = new Point(0, 0);
double maxR = 0;
SweepDirection outerSweep = SweepDirection.Clockwise;
SweepDirection innerSweep = SweepDirection.Counterclockwise;
outerSweep = SweepDirection.Counterclockwise;
innerSweep = SweepDirection.Clockwise;
foreach (Styled2DRange range in Query())
{
maxR = Math.Max(maxR, range.Range.YRange.End);
Point outerScreenPointBefore = new Point(range.Range.XRange.Start, range.Range.YRange.End);
Point outerScreenPointAfter = new Point(range.Range.XRange.End, range.Range.YRange.End);
Point innerScreenPointBefore = new Point(range.Range.XRange.Start, range.Range.YRange.Start);
Point innerScreenPointAfter = new Point(range.Range.XRange.End, range.Range.YRange.Start);
StreamGeometry sectorGeometry = new StreamGeometry();
sectorGeometry.FillRule = FillRule.Nonzero;
using (StreamGeometryContext geometryContext = sectorGeometry.Open())
{
geometryContext.BeginFigure(innerScreenPointBefore, true, true);
geometryContext.LineTo(outerScreenPointBefore, true, false);
double outerCircleRadius = Math.Sqrt(Math.Pow(outerScreenPointBefore.X - origin.X, 2) + Math.Pow(outerScreenPointBefore.Y - origin.Y, 2));
geometryContext.ArcTo(outerScreenPointAfter, new Size(outerCircleRadius, outerCircleRadius), 0, false, outerSweep, true, false);
geometryContext.LineTo(innerScreenPointAfter, true, false);
double innerCircleRadius = Math.Sqrt(Math.Pow(innerScreenPointBefore.X - origin.X, 2) + Math.Pow(innerScreenPointBefore.Y - origin.Y, 2));
geometryContext.ArcTo(innerScreenPointBefore, new Size(innerCircleRadius, innerCircleRadius), 0, false, innerSweep, true, false);
}
sectorGeometry.Freeze();
dc.DrawGeometry(range.Fill, range.Line, sectorGeometry);
}
}
So how do I reference the wind speed to create an actual point within the bounds of the drawing?
The four points of your chart sector are lying on two concentric circles, where the radius of the inner circle is given by the start wind speed, and that of the outer circle by the end wind speed. The position of the points on each circle is directly given by the wind directions in radians.
Provided that you have the variables startDirection and endDirection for the wind directions and startSpeed and endSpeed for the wind speeds, a sector would be constructed like this:
var pStart = new Point(Math.Sin(startDirection), -Math.Cos(startDirection));
var pEnd = new Point(Math.Sin(endDirection), -Math.Cos(endDirection));
var isLargeArc = Math.Abs(endDirection - startDirection) > Math.PI;
var geometry = new StreamGeometry();
using (var sgc = geometry.Open())
{
sgc.BeginFigure( // start point on inner circle
new Point(startSpeed * pStart.X, startSpeed * pStart.Y),
true, true);
sgc.ArcTo( // end point on inner circle
new Point(startSpeed * pEnd.X, startSpeed * pEnd.Y),
new Size(startSpeed, startSpeed), // radius of inner circle
0d, isLargeArc, SweepDirection.Clockwise, true, true);
sgc.LineTo( // end point on outer circle
new Point(endSpeed * pEnd.X, endSpeed * pEnd.Y),
true, true);
sgc.ArcTo( // start point on outer circle
new Point(endSpeed * pStart.X, endSpeed * pStart.Y),
new Size(endSpeed, endSpeed), // radius of outer circle
0d, isLargeArc, SweepDirection.Counterclockwise, true, true);
}

WPF Modify Path Data at Runtime

Hi I have a path from GetFlattenedPathGeometry where i can iterate through the figures and segments to get the points to add to a PointCollection.
I then multiply each point.x/y by a scale factor to get a full scaled version of the original path data. (not using scaletransform as it doesn't suit my requirements).
If i use something like:
public static PathGeometry GetPathGeometry(PointCollection polygonCorners)
{
List<PathSegment> pathSegments = new List<PathSegment> { new PolyLineSegment(polygonCorners, true) };
PathGeometry pathGeometry = new PathGeometry();
pathGeometry.Figures.Add(new PathFigure(polygonCorners[0], pathSegments, true));
return pathGeometry;
}
It returns a new path geometry but doesn't handle ellipses with excluded path geometry in that the path is just one continuous line.
Is there a way to convert the PointCollection to Path.Data (eg: with the "M" "L" and such) for me to re-use Geometry.Parse(the new string)?
Here is the code i'm using to get the flattenedgeometry pointcollection:
PathGeometry g = path.Data.GetFlattenedPathGeometry();
foreach (var f in g.Figures)
{
foreach (var s in f.Segments)
{
if (s is PolyLineSegment)
{
foreach (var pt in ((PolyLineSegment) s).Points)
{
strGeom += pt.ToString();
Point ptn = new Point(pt.X * ScaleX, pt.Y * ScaleY);
pcol.Add(ptn);
}
}
}
}
< Edit Images >
Here is the original path with rectangles and ellipses subtracted from the geometry.
And here is what is looks like re-creating from the code.
If i use the original GetFlattenedPathGeometry, it looks like the original but i need to scale the points to a new resolution.
Hope this makes it clearer.
You could simply call ToString on the PathGeometry to get the whole path data string at once:
var sourceGeometry = path.Data.GetFlattenedPathGeometry();
var geometryString = sourceGeometry.ToString(CultureInfo.InvariantCulture);
var targetGeometry = Geometry.Parse(geometryString);
And why can't you just apply a ScaleTransform to the whole geometry before calling GetFlattenedPathGeometry? The following works perfectly for me (with two EllipseGeometries in an excluding CombinedGeometry):
var pathGeometry = path.Data.Clone();
pathGeometry.Transform = new ScaleTransform(0.5, 0.5);
var scaledGeometry = pathGeometry.GetFlattenedPathGeometry();
EDIT: From what you write in your question and comments, I'm guessing that all you actually want to do is to add or combine geometries with different scaling factors. If that is true, your flattened geometry approach is by far to complicated, as you could easily do that with the following two methods:
private PathGeometry AddGeometries(
Geometry geometry1, Geometry geometry2, double scale)
{
geometry2 = geometry2.Clone();
geometry2.Transform = new ScaleTransform(scale, scale);
var pathGeometry = PathGeometry.CreateFromGeometry(geometry1);
pathGeometry.AddGeometry(geometry2);
return pathGeometry;
}
private PathGeometry CombineGeometries(
Geometry geometry1, Geometry geometry2, GeometryCombineMode mode, double scale)
{
geometry2 = geometry2.Clone();
geometry2.Transform = new ScaleTransform(scale, scale);
return Geometry.Combine(geometry1, geometry2, mode, null);
}
Given a Path with some geometry in its Data property, you may now add (or combine) an arbitray other geometry with a scaling factor with a call like this:
Geometry newGeometry1 = ...
double scale1 = ...
path.Data = AddGeometries(path.Data, newGeometry1, scale1);
Geometry newGeometry2 = ...
double scale2 = ...
path.Data = CombineGeometries(path.Data, newGeometry2,
GeometryCombineMode.Exclude, scale2);
Found the answer by perseverance.
The code to get each point of flattenedpathgeometry and add a scale to each point and recreate the same flattenedpathgeometry with the new points. hope it helps someone. And thanks Clemens. Appreciate your efforts.
path.Data = Geometry.Parse(CurrentObject.Geometry1);
PathGeometry g = path.Data.GetFlattenedPathGeometry();
PathGeometry g = path.Data.GetFlattenedPathGeometry();
foreach (var f in g.Figures)
{
Point pt1 = f.StartPoint;
pt1.X = pt1.X * ScaleX;
pt1.Y = pt1.Y * ScaleY;
strGeom += "M" + pt1.ToString();
foreach (var s in f.Segments)
if (s is PolyLineSegment)
{
count = 0;
foreach (var pt in ((PolyLineSegment)s).Points)
{
int scount = ((PolyLineSegment)s).Points.Count;
if (count == 0)
{
Point pts = new Point(pt.X * ScaleX, pt.Y * ScaleY);
strGeom += "L" + pts.ToString();
}
else if (count < scount)
{
Point pts = new Point(pt.X * ScaleX, pt.Y * ScaleY);
strGeom += " " + pts.ToString();
}
else if (count == scount)
{
Point pts = new Point(pt.X * ScaleX, pt.Y * ScaleY);
strGeom += " " + pts.ToString() + "Z";
}
count++;
}
}
}
path.Data = Geometry.Parse(strGeom);
Here's an image of the paths sent from a remote session: 1366x768 scales to 1920x1080

Mark the regions over the zed graph

Currently I am using ZedGraph to display my curve. I want to mark the particular regions of the curve over the ZedGraph control and label it like follows.
Note: I need different type of markings in the X and Y axis based on the text size.
Thanks for your help in advance.
You have two options,
1) Use BoxObject to draw at a specific region as follows
and you can use the source code as follows:
private void Form1_Load(object sender, EventArgs e)
{
// Create an instance of Graph Pane
GraphPane myPane = zedGraphControl1.GraphPane;
// Build a PointPairList with points based on Sine wave
PointPairList list = new PointPairList();
for (double i = 0; i < 36; i++)
{
double x = i * 10.0 + 50.0;
double y = Math.Sin(i * Math.PI / 15.0) * 16.0;
list.Add(x, y);
}
// Hide the legend
myPane.Legend.IsVisible = false;
// Add a curve
LineItem curve = myPane.AddCurve("label", list, Color.Red, SymbolType.Circle);
curve.Line.Width = 1.5F;
curve.Symbol.Fill = new Fill(Color.White);
curve.Symbol.Size = 5;
// Make the XAxis start with the first label at 50
myPane.XAxis.Scale.BaseTic = 50;
// Fill the axis background with a gradient
myPane.Chart.Fill = new Fill(Color.White, Color.SteelBlue, 45.0F);
// Draw Region 1
drawRegion(list[0].X, list[10].X,"Positive Cycle");
// Calculate the Axis Scale Ranges
zedGraphControl1.AxisChange();
// Refresh to paint the graph components
Refresh();
}
private void drawRegion(double xMin, double xMax, string regName)
{
GraphPane pane = zedGraphControl1.GraphPane;
BoxObj box = new BoxObj(xMin,20, xMax, 40.0, Color.Empty, Color.LightSteelBlue);// Color.FromArgb(225, 245, 225));
box.Location.CoordinateFrame = CoordType.AxisXYScale;
box.Location.AlignH = AlignH.Left;
box.Location.AlignV = AlignV.Top;
// place the box behind the axis items, so the grid is drawn on top of it
box.ZOrder = ZOrder.E_BehindCurves;//.D_BehindAxis;//.E_BehindAxis;
pane.GraphObjList.Add(box);
// Add Region text inside the box
TextObj myText = new TextObj(regName, 160, -15);
myText.Location.CoordinateFrame = CoordType.AxisXYScale;
myText.Location.AlignH = AlignH.Right;
myText.Location.AlignV = AlignV.Center;
myText.FontSpec.IsItalic = true;
myText.FontSpec.IsBold = false;
myText.FontSpec.FontColor = Color.Red;
myText.FontSpec.Fill.IsVisible = false;
myText.FontSpec.Border.IsVisible = false;
pane.GraphObjList.Add(myText);
zedGraphControl1.Refresh();
}
2) This is a bit difficult but do-able, Draw individual vertical lines discussed here: 1, 2 and add the required text etc.
I suggest you to use the option 1, which is lot easier than 2 !

Categories

Resources