I am currently using OpenCV to create a system which detects whether vehicles are in certain zones. So far, I've got to the point where a Rect is drawn around each vehicle. My next step is to find the central points of these rectangles and see whether that falls within a particular zone.
I realise that the best way of doing this is probably to find the coordinates of the Rect's four corners and then take an average x and average y-coordinate to find the coordinates for the central point. However, I'm not sure how to do this. Is there some function which lets me access OpenCV's Rect coordinates directly?
Edit to original: As was pointed out, we cannot use '+' operator directly on tl() and br().
Use this instead to get the center of a rectangle:
Point p1 = new Point(100, 100);
Point p2 = new Point(600, 800);
Rect myrect = new Rect(p1, p2);
System.out.println(String.format("Rectangle: %s", myrect));
Point centroid = new Point(myrect.x + 0.5*(myrect.width), myrect.y + 0.5*(myrect.height));
System.out.println(String.format("centroid: %s", centroid));
This prints:
Rectangle: {100, 100, 500x700}
centroid: {350.0, 450.0}
Old answer:
[Use rect's methods:
tl ()
br ()
to get the top left and bottom right points, respectively.]
If you did want to use tl, br methods you can do:
Point anotherCentroid = new Point(0.5 * (myrect.br().x + myrect.tl().x), 0.5 * (myrect.br().y + myrect.tl().y));
Related
I want to do some geometric calculations in Java and found that Spatial4j should suit my needs.
I want to be able to compute stuff like whether two polygons overlap or what their bounding box is.
My thinking is that I need to create a polygon from a series of points.
To that end I have tested with this code:
Point point1 = shapeFactory.pointXY(0, 0);
Point point2 = shapeFactory.pointXY(5, 1);
Point point3 = shapeFactory.pointXY(3, 3);
Point point4 = shapeFactory.pointXY(0, 1);
List<Point> points = new ArrayList<>();
points.addAll(Arrays.asList(point1, point2, point3, point4));
So, I have my points now. How do I go about making a polygon (or for that matter any shape) from these points ?
I would think that shapeFactory.polygon() would create me a polygon but that throws me an UnsupportedOperationException. Any help ?
Alright, it seems that Spatial4j does not connect the points, so it is not a filled shape. Instead I relied on the Spatial4j implementation of JTS and that did the trick. (Spatial4j's polygon is not implemented).
JtsSpatialContextFactory jtsSpatialContextFactory = new JtsSpatialContextFactory();
JtsSpatialContext jtsSpatialContext = jtsSpatialContextFactory.newSpatialContext();
JtsShapeFactory jtsShapeFactory = jtsSpatialContext.getShapeFactory();
ShapeFactory.PolygonBuilder polygonBuilder = jtsShapeFactory.polygon();
// note due to it being a builder one needs to chain the points.
Shape shape1 = polygonBuilder.pointXY(4, 0).pointXY(3, 3).pointXY(1, 4).pointXY(0, 0).pointXY(4, 0).build();
Now doing for example shape.getArea() returns the surface area.
One can also create a Geometry from a Shape by doing jtsShapeFactory.getGeometryFrom(shape), which then returns a Geometry.
Note: Watch out with doing polygonBuilder.pointXY() even after calling build(). It will still append these points to whatever was chained to the builder before the build.
Earthquake threat circle on the map
I am using UnfoldingMaps to display earthquake information on the map.
I plan to show the threat circle on the map.
A circle is drawn given its radius and center position in pixels. How to get the radius is the problem I met.
Suppose I have the threat circle radius R in kilometers and the center marker A.
I want to create a marker B on the circle so that I can use the screen distance as the screen radius.
I decided to create B with the same longitude but a different latitude from A. I change R to delta latitude.
But after drawing the circle I found it is not the right one since the red triangular should be in the circle according to their distance.
The main difficulty is exactly how to calculate screen radius according to kilometers.
public void calcThreatCircleOnScreen(UnfoldingMap map) {
float radius = 0;
float deltaLat=(float) (threatCircle()/6371/2/3.1415927*360);
Location centerLocation = this.getLocation();
Location upperLocation = new Location(centerLocation);
upperLocation.setLat(centerLocation.getLat() + deltaLat);
SimplePointMarker upperMarker = new SimplePointMarker(upperLocation);
ScreenPosition center = this.getScreenPosition(map);
ScreenPosition upper = upperMarker.getScreenPosition(map);
radius = Math.abs(upper.y - center.y);
setThreatCircleOnScreen(radius);
}
This is going to depend on two things: the zoom level of the map, and the projection you're using.
You need to unproject kilometers to pixels, and you can probably figure out how to do that using google and the Unfolding API.
For example, I found a MercatorProjection class that contains a constructor that takes a zoom level, and methods for projecting and unprojecting points between world coordinates and pixel coordinates.
That's just a starting point, since I'm not sure what units those methods are taking, but hopefully this is a direction for you to take your googling and experimenting.
I'd recommend trying to get something working and posting an MCVE if you get stuck. Good luck.
Now I have the answer for this question. Hope it will be helpful for others.
Earthquake threat circle on the map
My early solution to calculate radius in pixels from km is correct. I think it a simple and powerful idea (independent of projecting API)
The only problem is I should use diameter rather than radius in drawing the circle. I should draw with d=2r like this
float d = 2 * threatCircleRadius();
pg.noFill();
pg.ellipse(x,y,d,d);
I found another cleaner solution like below by consulting the author of UnfoldingMaps. (https://github.com/tillnagel/unfolding/issues/124)
My early solution first changes distance to delta latitude, then create new location by changing latitude.
The new solution use the API GeoUtils.getDestinationLocation(sourceLocation, compassBearingDegree, distanceKm) to directly get the new location!
In addition, I needn't create a new marker to find its screen position.
public void calcThreatCircleOnScreen(UnfoldingMap map) {
float radius = 0;
Location centerLocation = this.getLocation();
Location upperLocation = GeoUtils.getDestinationLocation(centerLocation, 0, threatCircle());
//SimplePointMarker upperMarker = new SimplePointMarker(upperLocation);
ScreenPosition center = map.getScreenPosition(centerLocation);
ScreenPosition upper = map.getScreenPosition(upperLocation);
radius = PApplet.dist(center.x, center.y, upper.x, upper.y);
setThreatCircleOnScreen(radius);
}
I'm currently looking into the drawPolygon(int[] xPoints, int[] yPoints, int nPoints) method in Java.
If I am not mistaken, the first two parameters are arrays, indicating the x-coordinates and y-coordinates of the polygon.
My question is, how are the polygon's coordinates interpreted from the two arrays?
For instance, I want to draw a line between the points (100, 300) and (200, 400). That is, a line increasing from left to right.
However, if I put these values into their respective arrays:
xPoints = {100, 200}; //x-coordinates
yPoints = {300, 400}; //y-coordinates
I get a line decreasing from left to right. As if the points are interpreted (100, 400) and (200, 300).
Thus, my question is: how are the array elements evaluated to make up the points of the polygon?
Thanks!
The default coordinate system has the origin in the upper left hand side corner of the canvas, and the y values increase from the top of the screen downwards. You can use an affine transform if you aren't happy with this orientation.
This is an example (!) from some code I have lying around - you may have to adapt it according to your situation:
// Polygon -> PathIterator -> Path2D, and then:
Path2D path = ...;
at.scale( 1, -1 );
path.transform( at );
bbox = path.getBounds2D();
at = new AffineTransform();
at.translate( -bbox.getMinX(), -bbox.getMinY() );
path.transform( at );
The coordinate system has origo in the top-left corner, and the y-axis increasing downwards.
This is why you get a downward slope when you increase the y-coordinate.
This question already has an answer here:
How to set button by a contour polygon? Android
(1 answer)
Closed 9 years ago.
Have a look at below image.
Total image is around 300X300. Inside that 1 Diamond shape is there. I know its Points as below
pointA = new Point(0, 183);
pointB = new Point(183, 0);
pointC = new Point(366, 183);
pointD = new Point(183, 366);
If I touch on this whole image, how can I detect whether touched point is inside Diamond area or outside?
I also had a look at this link but could not understand some points.
create a Shape object from your Points and
check that point exists inside that Shpae
m not sure about this but it should be similar to this one...
Rectangle rect = new Rectangle();//use your points co-ordinates
if (rect.contains(x,y))
{
//isinside
}
What you are referring to is the L1 Norm, or Manhattan Distance. To test if your clicked point is inside your diamond (or less than an L1 norm of 183, all you need to do is do the following (in pseudo-code):
isInside(ClickedPoint)
{
X=abs(ClickedPoint.x-183);
Y=abs(Clickedpoint.y-183);
if (X+Y<=183) return inside
else return outside
}
Sorry for not including true Java code, but that shouldn't be too hard to code up.
What you have to do is rotate the touched point and the diamond points by 45 degrees:
public Point rotatePoint(Point pt, Point center)
{
double cosAngle = Math.cos(45);
double sinAngle = Math.sin(45);
double dx = (pt.x-center.x);
double dy = (pt.y-center.y);
pt.x = center.x + (int) (dx*cosAngle-dy*sinAngle);
pt.y = center.y + (int) (dx*sinAngle+dy*cosAngle);
return pt;
}
create a Rect from the points:
Point centerPoint = new Point(183,183);
Rect r = new Rect(rotatePoint(pointA, centerPoint).x, rotatePoint(pointA, centerPoint).y, rotatePoint(pointC, centerPoint).x, rotatePoint(pointC, centerPoint).y);
then use test if it contains the point:
r.contains(rotatePoint(ClickedPoint, centerPoint))
This will return true if the point is in the diamond.
I am trying to compute a polygon that surrounds a line connecting multiple points (e.g. a GPX track).
The image below shows an example with the track as red line and the desired polygon in blue.
As simplification the red points are denoted by x and y - not by latitude/longitude.
How do I compute such an environment (light blue polygon) if I only have the list of the three points specifying the path?
Partial solutions (e.g. for only two points) or hints about mathematical libraries (in Java) that provide algorithms for such a computation would also bring me one step forward.
Further assumptions:
The track is intersection free.
Update:
Using the approach as presented by Rogach and xan I ran into some problems if the angle between the lines is smaller than 90 degree or larger than 270 degree:
As you can see the polygon gets intersects itself which leads to a serious problem.
From my point of view using an java.awt.geom.Area is the better approach:
My solution (based on the code by Rogach):
For each line connecting two points of the track I compute a surrounding polygon. Afterwards I add (area union) the computed polygon to an Area which does all the necessary computation for me. As the Area strictly uses the "or" algorithm on adding new polygons I do not have to care about "self intersections" of the polygon as presented in the update above.
Area area = new Area();
for (int i = 1; i < points.size(); i++) {
Point2D point1 = points.get(i - 1);
Point2D point2 = points.get(i);
Line2D.Double ln = new Line2D.Double(point1.getX(), point1.getY(), point2.getX(), point2.getY());
double indent = 15.0; // distance from central line
double length = ln.getP1().distance(ln.getP2());
double dx_li = (ln.getX2() - ln.getX1()) / length * indent;
double dy_li = (ln.getY2() - ln.getY1()) / length * indent;
// moved p1 point
double p1X = ln.getX1() - dx_li;
double p1Y = ln.getY1() - dy_li;
// line moved to the left
double lX1 = ln.getX1() - dy_li;
double lY1 = ln.getY1() + dx_li;
double lX2 = ln.getX2() - dy_li;
double lY2 = ln.getY2() + dx_li;
// moved p2 point
double p2X = ln.getX2() + dx_li;
double p2Y = ln.getY2() + dy_li;
// line moved to the right
double rX1_ = ln.getX1() + dy_li;
double rY1 = ln.getY1() - dx_li;
double rX2 = ln.getX2() + dy_li;
double rY2 = ln.getY2() - dx_li;
Path2D p = new Path2D.Double();
p.moveTo(lX1, lY1);
p.lineTo(lX2, lY2);
p.lineTo(p2X, p2Y);
p.lineTo(rX2, rY2);
p.lineTo(rX1_, rY1);
p.lineTo(p1X, p1Y);
p.lineTo(lX1, lY1);
area.add(new Area(p));
}
As I see, this problem is similar to polygon buffering problem.
I think following approach can help you:
For each segment of your track, find two lines - one to the left and one to the right.
Then, iterate for over your ofsetted lines, and resolve intersections. For example:
http://img25.imageshack.us/img25/7660/temprhk.png
Add caps to ends, and you're done! :)
And some code:
Moving a line to the left:
Line2D l;
double indent; // distance from central line
double dx = ln.getX2() - ln.getX1();
double dy = ln.getY2() - ln.getY1();
double length = ln.getP1().distance(ln.getP2());
double newX1 = l.getX1() - indent*(dy/length);
double newY1 = l.getY1() + indent*(dx/length);
double newX2 = l.getX2() - indent*(dy/length);
double newY2 = l.getY2() + indent*(dx/length);
Line2D leftLine = new Line2D.Double(newX1, newY1, newX2, newY2);
For moving it to the right, change "+" to "-" and vice versa in the last 4 lines of code.
About working with intersections - if two line segment intersect, you just output the intersection point. If they do not, then situation is a bit more complicated - you can, of course, still output the intersection, but in case of rapidly turning track, there will be strange outbursts. I inserted an arc segment in similar situation, but the code is to big and scattered, so I can't paste it here.
Or, you can do as you show on your picture - just connect end points.
And, by the way, if speed is not a big issue, you can use even better way - for each line of track, find left and right lines, add caps, pack it all into Path2D, then create Area from Path2D.
In such case, you can make this "line with caps" as intersection of three areas: rectangle, whose points are just end points of right and left line, and two circles with centers on original track segment ends.
When you compute Areas for all lines, just intersect them using Area add() method.
This approach deals with just any situations, even self-intersections and breaks in the track.
See my answer to a similar question, "How to draw an outline around any line."
Same idea as Rogach provides here, but perhaps different drawings and explanations will help clarify it.
If you don't want to write the code for the buffering as described by Rogach, JTS could do the magic for you.
See the developer guide for a quick introduction.
Half-baked suggestion: Calculate the normal to each segment. Then, for each vertex V_i, interpolate the normals from its adjacent segments to get n_i (normalise it again) and add two vertices at V_i +/- a*n_i where a is some scaling factor.
If you join these points, you won't get exactly your blue polygon, but it might be good enough.
You may have to keep track of which "side" the new vertices are on. If you can close the curve without self-intersections this just becomes a point in polygon test for each vertex.