Calculating area:
var coordinates = new List<Coordinate> {
new Coordinate(55, 35),
new Coordinate(55, 35.1),
new Coordinate(55.1, 35.1),
new Coordinate(55.1, 35),
};
Console.WriteLine(new Polygon(coordinates).Area); // ~0.01
Calculation is right, because it's happen in orthogonal coordinate system.
But how to mark that coordinates are in WGS?
It seems that task is more complicated that I've expected. I found this useful discussion on google groups
Firstly we need to found projection system, that is most suitable for our region where we need to compute area. For example you can take one of UTM zones
using DotSpatial.Projections;
using DotSpatial.Topology;
public static double CalculateArea(IEnumerable<double> latLonPoints)
{
// source projection is WGS1984
var projFrom = KnownCoordinateSystems.Geographic.World.WGS1984;
// most complicated problem - you have to find most suitable projection
var projTo = KnownCoordinateSystems.Projected.UtmWgs1984.WGS1984UTMZone37N;
// prepare for ReprojectPoints (it mutates array)
var z = new double[latLonPoints.Count() / 2];
var pointsArray = latLonPoints.ToArray();
Reproject.ReprojectPoints(pointsArray, z, projFrom, projTo, 0, pointsArray.Length / 2);
// assemblying new points array to create polygon
var points = new List<Coordinate>(pointsArray.Length / 2);
for (int i = 0; i < pointsArray.Length / 2; i++)
points.Add(new Coordinate(pointsArray[i * 2], pointsArray[i * 2 + 1]));
var poly = new Polygon(points);
return poly.Area;
}
You can get the area directly from IGeometry or from Feature.Geometry. Also You need to repeat the first coordinate to close your polygon.
FeatureSet fs = new FeatureSet(FeatureType.Polygon);
Coordinate[] coord = new Coordinate[]
{
new Coordinate(55, 35),
new Coordinate(55, 35.1),
new Coordinate(55.1, 35.1),
new Coordinate(55.1, 35),
new Coordinate(55, 35)
};
fs.AddFeature(new Polygon(new LinearRing(coord)));
var area = fs.Features.First().Geometry.Area;
Related
As result, only just the last line was boxed as seen above.
The code starts from this post below.
Extracting text OpenCV
First, I start it from Diomedes DomÃnguez's C# code.
I modified only one line because original code throws here.
var mask = new Mat(Mat.Zeros(bw.Size(), MatType.CV_8UC1));
System.ArgumentException: 'empty ranges
parameter: ranges'
So I added range.
var mask = new Mat(Mat.Zeros(bw.Size(), MatType.CV_8UC1), new Rect(0, 0, bw.Size().Width, bw.Size().Height));
So here is code what I've tried.
Mat large = new Mat(#"D:\cap_price.PNG");
Mat rgb = new Mat(), small = new Mat(), grad = new Mat(), bw = new Mat(), connected = new Mat();
// downsample and use it for processing
Cv2.PyrDown(large, rgb);
Cv2.CvtColor(rgb, small, ColorConversionCodes.BGR2GRAY);
// morphological gradient
var morphKernel = Cv2.GetStructuringElement(MorphShapes.Ellipse, new OpenCvSharp.Size(3, 3));
Cv2.MorphologyEx(small, grad, MorphTypes.Gradient, morphKernel);
// binarize
Cv2.Threshold(grad, bw, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
// connect horizontally oriented regions
morphKernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(9, 1));
Cv2.MorphologyEx(bw, connected, MorphTypes.Close, morphKernel);
// find contours
var mask = new Mat(Mat.Zeros(bw.Size(), MatType.CV_8UC1));
Cv2.FindContours(connected, out OpenCvSharp.Point[][] contours, out HierarchyIndex[] hierarchy, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple, new OpenCvSharp.Point(0, 0));
// filter contours
var idx = 0;
foreach (var hierarchyItem in hierarchy)
{
OpenCvSharp.Rect rect = Cv2.BoundingRect(contours[idx]);
var maskROI = new Mat(mask, rect);
maskROI.SetTo(new Scalar(0, 0, 0));
// fill the contour
Cv2.DrawContours(mask, contours, idx, Scalar.White, -1);
// ratio of non-zero pixels in the filled region
double r = (double)Cv2.CountNonZero(maskROI) / (rect.Width * rect.Height);
if (r > .45 /* assume at least 45% of the area is filled if it contains text */
&&
(rect.Height > 8 && rect.Width > 8) /* constraints on region size */
/* these two conditions alone are not very robust. better to use something
like the number of significant peaks in a horizontal projection as a third condition */
)
{
Cv2.Rectangle(rgb, rect, new Scalar(0, 255, 0), 1);
}
}
//rgb.SaveImage(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "rgb.jpg"));
rgb.SaveImage(#"D:\rgb.jpg");
I want each text line to be boxed.
I'm working on pedestrians detection algorithm and got stuck on using ConnectedComponentsWithStats() method. I can't get to the values of stats. Here is what I wrote so far:
var labels = new Mat();
var stats = new Mat();
var centroids = new Mat();
var nLabels = CvInvoke.ConnectedComponentsWithStats(greyImage, labels, stats, centroids);
var centroidPoints = new MCvPoint2D64f[nLabels];
centroids.CopyTo(centroidPoints);
foreach (MCvPoint2D64f point in centroidPoints)
{
var x = point.X;
var y = point.Y;
}
The stats are stored as int32's in opencv c++ and have 5 fields x,y,width,height and area.
You can see the opencv implementation of ConnectedComponentsWithStats here.
Also you can see the struct CCStatsOp opencv returns the stats as here.
Sadly EMGUCV has not created the data structure CCStatsOp so you will need to just store the raw integer data like such.
int[] statsData = new int[stats.Rows * stats.Cols];
stats.CopyTo(statsData);
for (int i = 0; i < stats.Rows; i++)
{
var x = statsData[i * stats.Cols + 0];
var y = statsData[i * stats.Cols + 1];
var width = statsData[i * stats.Cols + 2];
var height = statsData[i * stats.Cols + 3];
var area = statsData[i * stats.Cols + 4];
}
You can also roll your own CCStats op with this code here.
Struct
public struct CCStatsOp
{
public Rectangle Rectangle;
public int Area;
}
Code
var labels = new Mat();
var stats = new Mat();
var centroids = new Mat();
var nLabels = CvInvoke.ConnectedComponentsWithStats(greyImage, labels, stats, centroids);
var centroidPoints = new MCvPoint2D64f[nLabels];
centroids.CopyTo(centroidPoints);
CCStatsOp[] statsOp = new CCStatsOp[stats.Rows];
stats.CopyTo(statsOp);
foreach (var statop in statsOp)
{
Console.WriteLine($"Rectangle: {statop.Rectangle} Area: {statop.Area}");
}
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);
}
I have a self-defined struct of a point (as opposed to System.Drawing.Point):
struct PointD
{
public double X,Y;
}
I want to get a Seq of points, and from there extract the minimum area rectangle:
using (MemStorage stor = new MemStorage())
{
Seq<PointD> seq = new Seq<PointD>(CvInvoke.CV_MAKETYPE(6, 2), stor);
seq.Push(new PointD(0.5, 0));
seq.Push(new PointD(1.0, 0));
seq.Push(new PointD(0, 1.0));
seq.Push(new PointD(1.0, 1.0));
var output = seq.GetMinAreaRect();
}
However, this code throws an exception at GetMinAreaRect(), saying that the input sequence must be 2d points. My question is there a way to get my data format through correctly? I was thinking that I just lack a bit of something, as this code can work for a System.Drawing.Point.
I assume following would work:
int[] pts = {
new PointF(0.5, 0),
new PointF(1.0, 0),
new PointF(0, 1.0),
new PointF(1.0, 1.0)
};
MCvBox2D box = PointCollection.MinAreaRect(pts);
Additional example : http://www.emgu.com/wiki/index.php/Minimum_Area_Rectangle_in_CSharp
I've got a method that transforms a number of cylinders. If I run the method a second time it transforms the cylinders from their original position rather than their new position.
Is there anyway of 'applying' the transformation so that it changes the underlying values of the cylinders so that I can re-transform from the new values?
Can anyone assist?
Cheers,
Andy
void TransformCylinders(double angle)
{
var rotateTransform3D = new RotateTransform3D { CenterX = 0, CenterY = 0, CenterZ = 0 };
var axisAngleRotation3D = new AxisAngleRotation3D { Axis = new Vector3D(1, 1, 1), Angle = angle };
rotateTransform3D.Rotation = axisAngleRotation3D;
var myTransform3DGroup = new Transform3DGroup();
myTransform3DGroup.Children.Add(rotateTransform3D);
_cylinders.ForEach(x => x.Transform = myTransform3DGroup);
}
You are remaking the Transform3DGroup every time the method is called:
var myTransform3DGroup = new Transform3DGroup();
Transforms are essentially a stack of matrices that get multiplied together. You are clearing that stack every time you make a new group. You need to add consecutive transforms to the existing group rather than remake it.