I have two overlays in my openstreetmap app, Itemized overlay and Path overlay together. I want to provide click inside the path inside the path overlay and custom marker
Not available with PathOverlay.
Use OSMBonusPack Polyline.
If you just want to open a bubble, use setInfoWindow.
If you want to do something else, inherit from Polyline class, override onSingleTapConfirmed, and use isCloseTo.
Finally I got answer for the Above question...
Add all path overlays into a single layer.
In on single tap check isPointOnLine(lox,loy,ltx,lty, x, y)
public boolean isPointOnLine(double lox, double loy, double ltx,
double lty, double x, double y) {
// determine if point is on line
Double dx = x - lox;
Double dy = y - loy;
Double tx = ltx - lox;
Double ty = lty - loy;
// normalise the line vector
Double t1 = new Double(1 / Math.sqrt(tx * tx + ty * ty));
tx *= t1;
ty *= t1;
// calculate inverse length of secondary vector
Double dl = new Double(1 / Math.sqrt(dx * dx + dy * dy));
// take dot product of normalised line vector, and rotated normalised
// secondary vector
Double dot = (dy * tx - dx * ty) * dl;
// Increase these values for less or more picky
if (dot < -0.2 || dot > 0.2)
return false;
// calculate distance along line segment by taking dot product of
// normalised
// line vector and un-normalised secondary vector
Double dis = tx * dx + ty * dy;
if (dis < 0 || dis > 1 / t1)
return false;
return true;
}
Related
I have a Page which contains an AbsoluteLayout with a PinchZoomContainer (like the one from microsoft docs). In this Container is an image that fills the entire screen. When the user tapes on the image, another image (a pin) is positioned at the tappostion. So far this works. My problem is, that i couldn't figure out, how to calculate the position of the added image (user tapped) when the user zooms in and out.
I want the added image to stay at the tapped position, no matter if the user zooms in or out.
When i put the image in a grid the calculation gets done automatically.
Is there a better way to achieve that? I've chosen AbsoluteLayout because i need to put the image at the tapped position.
The following code is used for zooming. Here I can't figure out how to do the calculation for the added image. The image gets added to the AbsoluteLayout on runtime.
Normal scale
Zoomed in
void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started)
{
// Store the current scale factor applied to the wrapped user interface element,
// and zero the components for the center point of the translate transform.
startScale = Content.Scale;
Content.AnchorX = 0;
Content.AnchorY = 0;
startTransX = pin.TranslationX;
startTranxY = pin.TranslationY;
}
if (e.Status == GestureStatus.Running)
{
// Calculate the scale factor to be applied.
currentScale = currentScale + (e.Scale - 1) * startScale;
currentScale = Math.Max(1, currentScale);
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the X pixel coordinate.
double renderedX = Content.X + xOffset;
double deltaX = renderedX / Width;
double deltaWidth = Width / (Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the Y pixel coordinate.
double renderedY = Content.Y + yOffset;
double deltaY = renderedY / Height;
double deltaHeight = Height / (Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
// Calculate the transformed element pixel coordinates.
double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);
// Apply translation based on the change in origin.
// needs to use Xamarin.Forms.Internals or Extension
Content.TranslationX = targetX.Clamp(-Content.Width * (currentScale - 1), 0);
Content.TranslationY = targetY.Clamp(-Content.Width * (currentScale - 1), 0);
x = Content.TranslationX;
y = Content.TranslationY;
// Apply scale factor
Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed)
{
// Store the translation delta's of the wrapped user interface element.
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
x = Content.TranslationX;
y = Content.TranslationY;
}
}
I am working with drawing polylines on google maps. Given an origin and destination (latitude,longitude) coordinates how can I get 'x' number of points on that line?
I have tried applying mathematical concepts such as the equation of a line y = mx + c or even (x-x1)/(x1-x2) = (y-y1)/(y1-y2) but these methodologies do not work. The world is not flat. What is the formula for finding all points in a linear line for latitude/longitude values? Does anyone have any ideas for this? I believe I have to apply this equation: https://en.wikipedia.org/wiki/Mercator_projection
EDIT:
Someone suggested trying to convert the lat/lng to points and then doing the math and then converting back to lat/lng. There seems to be a large margin or error when doing this. Latitude is accurate, but Longitude is completely off. The TILE_SIZE = 256, the size of tiles that Google returns for Google Maps
public GoogleMapsProjection2() {
this._pixelOrigin = new PointF(TILE_SIZE / 2.0, TILE_SIZE / 2.0);
this._pixelsPerLonDegree = TILE_SIZE / 360.0;
this._pixelsPerLonRadian = TILE_SIZE / (2 * Math.PI);
}
public PointF fromLatLngToPoint(double lat, double lng, int zoom) {
PointF point = new PointF(0, 0);
point.x = _pixelOrigin.x + lng * _pixelsPerLonDegree;
// Truncating to 0.9999 effectively limits latitude to 89.189. This is
// about a third of a tile past the edge of the world tile.
double siny = bound(Math.sin(degreesToRadians(lat)), -0.9999, 0.9999);
point.y = _pixelOrigin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) * -_pixelsPerLonRadian;
int numTiles = 1 << zoom;
point.x = point.x * numTiles;
point.y = point.y * numTiles;
return point;
}
public PointF fromPointToLatLng(PointF point, int zoom) {
int numTiles = 1 << zoom;
point.x = point.x / numTiles;
point.y = point.y / numTiles;
double lng = (point.x - _pixelOrigin.x) / _pixelsPerLonDegree;
double latRadians = (point.y - _pixelOrigin.y) / -_pixelsPerLonRadian;
double lat = radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2);
return new PointF(lat, lng);
}
public final class PointF {
public double x;
public double y;
public PointF(double x, double y) {
this.x = x;
this.y = y;
}
}
A line segment in a Google Maps polyline is a geodesic line. In a spherical earth approximation, this is a great circle. The polyline with a single segment (your origin and destination in lat/long coordinates) is rendered onto a 2D map using a projection, but it's still a geodesic line (great circle).
GIS stack exchange has this question of interpolating two (latitude, longitude) coordinates to compute waypoints. The answer suggests using GeographicLib, which has a java library, and provides a JavaScript example. The resulting waypoints in latitude/longitude coordinates are your inputs to the polyline.
It's best to perform the interpolation in the latitude/longitude coordinate system to avoid magnifying errors with projection or discretization.
I have three gps locations as double lat and lng. I have three radius that correspond to each of the lat and lng values. The radius form circles around the locations. I want to determine the one point where all three circles overlap.
My starting point:
(x−lat_1)^2+(y−lng_1)^2=r_1^2
(x−lat_2)^2+(y−lng_2)^2=r_2^2
(x−lat_3)^2+(y−lng_3)^2=r_3^2
But here I am stuck - not only is that system of equations over-determined, it is also unclear, how to mix up degrees, minutes and seconds with a radius in meters.
What could a function(pseudocode is enough) look like that receives three locations and three radius and outputs one coordinate showing the overlapping.
Speaking of which, there needs to be some tolerance, as neither the radius nor the locations are too precise.
Take a look at this question:
Find intersecting point of three circles programmatically
I'm posting here the code that does what you need:
private static final double EPSILON = 0.000001;
private boolean calculateThreeCircleIntersection(double x0, double y0, double r0,
double x1, double y1, double r1,
double x2, double y2, double r2)
{
double a, dx, dy, d, h, rx, ry;
double point2_x, point2_y;
/* dx and dy are the vertical and horizontal distances between
* the circle centers.
*/
dx = x1 - x0;
dy = y1 - y0;
/* Determine the straight-line distance between the centers. */
d = Math.sqrt((dy*dy) + (dx*dx));
/* Check for solvability. */
if (d > (r0 + r1))
{
/* no solution. circles do not intersect. */
return false;
}
if (d < Math.abs(r0 - r1))
{
/* no solution. one circle is contained in the other */
return false;
}
/* 'point 2' is the point where the line through the circle
* intersection points crosses the line between the circle
* centers.
*/
/* Determine the distance from point 0 to point 2. */
a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ;
/* Determine the coordinates of point 2. */
point2_x = x0 + (dx * a/d);
point2_y = y0 + (dy * a/d);
/* Determine the distance from point 2 to either of the
* intersection points.
*/
h = Math.sqrt((r0*r0) - (a*a));
/* Now determine the offsets of the intersection points from
* point 2.
*/
rx = -dy * (h/d);
ry = dx * (h/d);
/* Determine the absolute intersection points. */
double intersectionPoint1_x = point2_x + rx;
double intersectionPoint2_x = point2_x - rx;
double intersectionPoint1_y = point2_y + ry;
double intersectionPoint2_y = point2_y - ry;
Log.d("INTERSECTION Circle1 AND Circle2:", "(" + intersectionPoint1_x + "," + intersectionPoint1_y + ")" + " AND (" + intersectionPoint2_x + "," + intersectionPoint2_y + ")");
/* Lets determine if circle 3 intersects at either of the above intersection points. */
dx = intersectionPoint1_x - x2;
dy = intersectionPoint1_y - y2;
double d1 = Math.sqrt((dy*dy) + (dx*dx));
dx = intersectionPoint2_x - x2;
dy = intersectionPoint2_y - y2;
double d2 = Math.sqrt((dy*dy) + (dx*dx));
if(Math.abs(d1 - r2) < EPSILON) {
Log.d("INTERSECTION Circle1 AND Circle2 AND Circle3:", "(" + intersectionPoint1_x + "," + intersectionPoint1_y + ")");
}
else if(Math.abs(d2 - r2) < EPSILON) {
Log.d("INTERSECTION Circle1 AND Circle2 AND Circle3:", "(" + intersectionPoint2_x + "," + intersectionPoint2_y + ")"); //here was an error
}
else {
Log.d("INTERSECTION Circle1 AND Circle2 AND Circle3:", "NONE");
}
return true;
}
Usage:
calculateThreeCircleIntersection(-2.0, 0.0, 2.0, // circle 1 (center_x, center_y, radius)
1.0, 0.0, 1.0, // circle 2 (center_x, center_y, radius)
0.0, 4.0, 4.0);// circle 3 (center_x, center_y, radius)
As you said, you probably need to do some unit conversion here. There is some complicated formula that calculates distance between two geolocations, so you need to reverse it to get meters from radian based distance.
Here you may find implementations of this calculation and try to reverse it:
Calculate distance between two latitude-longitude points? (Haversine formula)
I'm trying to figure out how best to do this, I have a map with one Polygon drawn on it. Since it doesn't seem as though the Google Maps API V2 has a touch detection on a Polygon. I was wonder if it is possible to detect whether the touch point is inside the Polygon? If so then how, my main goal is to outline a state on a map and when the user taps that state it will show more details inside a custom view. As of now I am able to capture the MapOnClick of the map but when the user taps inside the Polygon I want the polygon.getID() set on the Toast. I am a newbie so I apologize if I am not clear enough.
googleMap.setOnMapClickListener(new OnMapClickListener()
{
public void onMapClick(LatLng point)
{
boolean checkPoly = true;
Toast.makeText(MainActivity.this,"The Location is outside of the Area", Toast.LENGTH_LONG).show();
}
});
}
}
catch (Exception e) {
Log.e("APP","Failed", e);
}
Ok this is what I have semi-working so far
private boolean rayCastIntersect(LatLng tap, LatLng vertA, LatLng vertB) {
double aY = vertA.latitude;
double bY = vertB.latitude;
double aX = vertA.longitude;
double bX = vertB.longitude;
double pY = tap.latitude;
double pX = tap.longitude;
if (aY > bY) {
aX = vertB.longitude;
aY = vertB.latitude;
bX = vertA.longitude;
bX = vertA.latitude;
}
System.out.println("aY: "+aY+" aX : "+aX);
System.out.println("bY: "+bY+" bX : "+bX);
if (pX < 0) pX += 360;
if (aX < 0) aX += 360;
if (bX < 0) bX += 360;
if (pY == aY || pY == bY) pY += 0.00000001;
if ((pY > bY || pY < aY) || (pX > Math.max(aX, bX))) return false;
if (pX < Math.min(aX, bX))
return true;
// }
double m = (aX != bX) ? ((bY - aY) / (bX - aX)) : aX;
double bee = (aX != pX) ? ((pY - aY) / (pX - aX)) : aX;
double x = (pY - bee) / m;
return x > pX;
}
}
The issue that I am having is the touch is true to the left of each polygon until it reaches another one. What's wrong with my algorithm that would cause this issue? Any help would be appreciated.
The problem you're trying to solve is the Point in Polygon test.
To help visualize the concept of Ray Casting:
Draw a Polygon on a piece of paper. Then, starting at any random point, draw a straight line to the right of the page. If your line intersected with your polygon an odd number of times, this means your starting point was inside the Polygon.
So, how do you do that in code?
Your polygon is comprised of a list of vertices: ArrayList<Geopoint> vertices. You need to look at each Line Segment individually, and see if your Ray intersects it
private boolean isPointInPolygon(Geopoint tap, ArrayList<Geopoint> vertices) {
int intersectCount = 0;
for(int j=0; j<vertices.size()-1; j++) {
if( rayCastIntersect(tap, vertices.get(j), vertices.get(j+1)) ) {
intersectCount++;
}
}
return (intersectCount%2) == 1); // odd = inside, even = outside;
}
private boolean rayCastIntersect(Geopoint tap, Geopoint vertA, Geopoint vertB) {
double aY = vertA.getLatitude();
double bY = vertB.getLatitude();
double aX = vertA.getLongitude();
double bX = vertB.getLongitude();
double pY = tap.getLatitude();
double pX = tap.getLongitude();
if ( (aY>pY && bY>pY) || (aY<pY && bY<pY) || (aX<pX && bX<pX) ) {
return false; // a and b can't both be above or below pt.y, and a or b must be east of pt.x
}
double m = (aY-bY) / (aX-bX); // Rise over run
double bee = (-aX) * m + aY; // y = mx + b
double x = (pY - bee) / m; // algebra is neat!
return x > pX;
}
The Google Maps Support library now has a static method that does this check for you:
PolyUtil.containsLocation(LatLng point, List<LatLng>polygon, boolean geodesic);
Although the docs don't mention it explicitly in the guide the method is there
Maps Support Library docs
With the release of Google Play Services 8.4.0, the Maps API has included support for adding an OnPolygonClickListener to Polygons. Both polygons, polylines and overlays support similar events.
You just need to call GoogleMap.setOnPolygonClickListener(OnPolygonClickListener listener) to set it up, and correspondingly for the other listeners (setOnPolylineClickListener, &c):
map.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() {
#Override
public void onPolygonClick(Polygon polygon) {
// Handle click ...
}
});
Although a bit late, it solves this use case quite nicely.
Though user1504495 has answered in short as I have used it. But instead of using whole Map Utility Library Use this methods.
From your activity class pass params accordingly:
if (area.containsLocation(Touchablelatlong, listLatlong, true))
isMarkerINSide = true;
else
isMarkerINSide = false;
and put following in a Separate class :
/**
* Computes whether the given point lies inside the specified polygon.
* The polygon is always cosidered closed, regardless of whether the last point equals
* the first or not.
* Inside is defined as not containing the South Pole -- the South Pole is always outside.
* The polygon is formed of great circle segments if geodesic is true, and of rhumb
* (loxodromic) segments otherwise.
*/
public static boolean containsLocation(LatLng point, List<LatLng> polygon, boolean geodesic) {
final int size = polygon.size();
if (size == 0) {
return false;
}
double lat3 = toRadians(point.latitude);
double lng3 = toRadians(point.longitude);
LatLng prev = polygon.get(size - 1);
double lat1 = toRadians(prev.latitude);
double lng1 = toRadians(prev.longitude);
int nIntersect = 0;
for (LatLng point2 : polygon) {
double dLng3 = wrap(lng3 - lng1, -PI, PI);
// Special case: point equal to vertex is inside.
if (lat3 == lat1 && dLng3 == 0) {
return true;
}
double lat2 = toRadians(point2.latitude);
double lng2 = toRadians(point2.longitude);
// Offset longitudes by -lng1.
if (intersects(lat1, lat2, wrap(lng2 - lng1, -PI, PI), lat3, dLng3, geodesic)) {
++nIntersect;
}
lat1 = lat2;
lng1 = lng2;
}
return (nIntersect & 1) != 0;
}
/**
* Wraps the given value into the inclusive-exclusive interval between min and max.
* #param n The value to wrap.
* #param min The minimum.
* #param max The maximum.
*/
static double wrap(double n, double min, double max) {
return (n >= min && n < max) ? n : (mod(n - min, max - min) + min);
}
/**
* Returns the non-negative remainder of x / m.
* #param x The operand.
* #param m The modulus.
*/
static double mod(double x, double m) {
return ((x % m) + m) % m;
}
/**
* Computes whether the vertical segment (lat3, lng3) to South Pole intersects the segment
* (lat1, lng1) to (lat2, lng2).
* Longitudes are offset by -lng1; the implicit lng1 becomes 0.
*/
private static boolean intersects(double lat1, double lat2, double lng2,
double lat3, double lng3, boolean geodesic) {
// Both ends on the same side of lng3.
if ((lng3 >= 0 && lng3 >= lng2) || (lng3 < 0 && lng3 < lng2)) {
return false;
}
// Point is South Pole.
if (lat3 <= -PI/2) {
return false;
}
// Any segment end is a pole.
if (lat1 <= -PI/2 || lat2 <= -PI/2 || lat1 >= PI/2 || lat2 >= PI/2) {
return false;
}
if (lng2 <= -PI) {
return false;
}
double linearLat = (lat1 * (lng2 - lng3) + lat2 * lng3) / lng2;
// Northern hemisphere and point under lat-lng line.
if (lat1 >= 0 && lat2 >= 0 && lat3 < linearLat) {
return false;
}
// Southern hemisphere and point above lat-lng line.
if (lat1 <= 0 && lat2 <= 0 && lat3 >= linearLat) {
return true;
}
// North Pole.
if (lat3 >= PI/2) {
return true;
}
// Compare lat3 with latitude on the GC/Rhumb segment corresponding to lng3.
// Compare through a strictly-increasing function (tan() or mercator()) as convenient.
return geodesic ?
tan(lat3) >= tanLatGC(lat1, lat2, lng2, lng3) :
mercator(lat3) >= mercatorLatRhumb(lat1, lat2, lng2, lng3);
}
/**
* Returns tan(latitude-at-lng3) on the great circle (lat1, lng1) to (lat2, lng2). lng1==0.
* See http://williams.best.vwh.net/avform.htm .
*/
private static double tanLatGC(double lat1, double lat2, double lng2, double lng3) {
return (tan(lat1) * sin(lng2 - lng3) + tan(lat2) * sin(lng3)) / sin(lng2);
}
/**
* Returns mercator Y corresponding to latitude.
* See http://en.wikipedia.org/wiki/Mercator_projection .
*/
static double mercator(double lat) {
return log(tan(lat * 0.5 + PI/4));
}
/**
* Returns mercator(latitude-at-lng3) on the Rhumb line (lat1, lng1) to (lat2, lng2). lng1==0.
*/
private static double mercatorLatRhumb(double lat1, double lat2, double lng2, double lng3) {
return (mercator(lat1) * (lng2 - lng3) + mercator(lat2) * lng3) / lng2;
}
Here's a full working example to know if a touch happened on a polygon. Some of the answers are more complicated than they need to be. This solution uses the "android-maps-utils"
// compile 'com.google.maps.android:android-maps-utils:0.3.4'
private ArrayList<Polygon> polygonList = new ArrayList<>();
private void addMyPolygons() {
PolygonOptions options = new PolygonOptions();
// TODO: make your polygon's however you want
Polygon polygon = googleMap.addPolygon(options);
polygonList.add(polygon);
}
#Override
public void onMapClick(LatLng point) {
boolean contains = false;
for (Polygon p : polygonList) {
contains = PolyUtil.containsLocation(point, p.getPoints(), false);
if (contains) break;
}
Toast.makeText(getActivity(), "Click in polygon? "
+ contains, Toast.LENGTH_SHORT).show();
}
#Override
protected void onMapReady(View view, Bundle savedInstanceState) {
googleMap.setOnMapClickListener(this);
addMyPolygons();
}
I know I am posting this very late but I had some issue with the answer posted here, so I studied both the top answers and an article (which I think is the origin of this method) and modified Matt Answer to compile something that works best for me.
Problem with Matt Answer: It doesn't calculate the last line of polygon (i.e. one created by the last vertex and the first vertex)
Problem with Dwill Answer: It seems complex and daunting especially when you are already frustrated on how to make things work
Other checks I have added:
Checked if a polygon is actually created
Checked if any side of polygon is parallel to y-axis
I have tried to comment and explain as much I as I could hope this would be helpful for someone
One more thing, this is written in Dart and mainly focused on finding if current position is inside a geofence.
Future<bool> checkIfLocationIsInsideBoundary({
required LatLng positionToCheck,
required List<LatLng> boundaryVertices,
}) async {
// If there are less than 3 points then there will be no polygon
if (boundaryVertices.length < 3) return false;
int intersectCount = 0;
// Check Ray-cast for lines created by all the vertices in our List
for (int j = 0; j < boundaryVertices.length - 1; j++) {
if (_rayCastIntersect(
positionToCheck,
boundaryVertices[j],
boundaryVertices[j + 1],
)) {
intersectCount++;
}
}
// Check for line created by the last vertex and the first vertex of the List
if (_rayCastIntersect(
positionToCheck,
boundaryVertices.last,
boundaryVertices.first,
)) {
intersectCount++;
}
// If our point is inside the polygon they will always intersect odd number of
// times, else they will intersect even number of times
return (intersectCount % 2) == 1; // odd = inside, even = outside
}
bool _rayCastIntersect(LatLng point, LatLng vertA, LatLng vertB) {
final double aY = vertA.latitude;
final double bY = vertB.latitude;
final double aX = vertA.longitude;
final double bX = vertB.longitude;
final double pY = point.latitude;
final double pX = point.longitude;
// If vertices A and B are both above our point P then obviously the line made
// by A and B cannot intersect with ray-cast of P. Note: Only y-coordinates of
// each points can be used to check this.
if (aY > pY && bY > pY) return false;
// If vertices A and B are both below our point P then obviously the line made
// by A and B cannot intersect with ray-cast of P. Note: Only y-coordinates of
// each points can be used to check this.
if (aY < pY && bY < pY) return false;
// Since we will be casting ray on east side from our point P, at least one of
// the vertex (either A or B) must be east of P else line made by A nd B
// cannot intersect with ray-cast of P. Note: Only x-coordinates of each
// points can be used to check this.
if (aY < pY && bY < pY) return false;
// If line made by vertices is parallel to Y-axis then we will get
// 'Divided by zero` exception when calculating slope. In such case we can
// only check if the line is on the east or the west relative to our point. If
// it is on the east we count is as intersection. Note: we can be sure our
// ray-cast will intersect the line because it is a vertical line, our
// ray-cast is horizontal and finally we already made sure that both the
// vertices are neither above nor below our point. Finally, since `aX == bX`
// we can check if either aX or bX is on the right/east of pX
if (aX == bX) return aX > pX;
// Calculate slope of the line `m` made by vertices A and B using the formula
// `m = (y2-y1) / (x2-x1)`
final double m = (aY - bY) / (aX - bX); // Rise over run
// Calculate the value of y-intersect `b` using the equation of line
final double b = aY - (aX * m); // y = mx + b => b = y - mx
// Now we translate our point P along X-axis such that it intersects our line.
// This means we can pluck y-coordinate of our point P into the equation of
// our line and calculate a new x-coordinate
final double x = (pY - b) / m; // y = mx + b => x = (y - b) / m
// Till now we have only calculated this new translated point but we don't
// know if this point was translated towards west(left) of towards
// east(right). This can be determined in the same way as we have done above,
// if the x-coordinate of this new point is greater than x-coordinate of our
// original point then it has shifted east, which means it has intersected our
// line
return x > pX;
}
Just for consistency - onMapClick is not called when user taps on a polygon (or other overlay), and it's mentioned in javadoc.
I made a workaround to intercept taps events before MapFragment handles them, and project point to map coordinates and check if the point is inside any polygon, as suggested in other answer.
See more details here
I am trying to draw an arrow to point to objects in am image. I have been able to write code to draw the line but cant seem to be able to find a way to draw the arrowhead.The code I wrote to draw a dragabble line is as follows.I need to draw an arrowhead on ACTION_UP event to the direction in which the line is pointing
if(event.getAction() ==MotionEvent.ACTION_DOWN) {
if (count==1){
x1 = event.getX();
y1 = event.getY();
System.out.println(count+"count of value a;skd");
Toast.makeText(getApplicationContext(), ""+(radius+count), Toast.LENGTH_LONG).show();
Log.i(TAG, "coordinate x1 : "+String.valueOf(x1)+" y1 : "+String.valueOf(y1));
}
}
else if(event.getAction() ==MotionEvent.ACTION_MOVE){
imageView.setImageBitmap(bmp2);
x2 = event.getX();
y2 = event.getY();
posX=(float)(x1+x2)/2;
posY=(float)(y1+y2)/2;
radius=(float) Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))/2;
onDraw();
Toast.makeText(getApplicationContext(), ""+radius, Toast.LENGTH_LONG).show();
}
Hi, for anyone still needing help .This is how I did it in the end
float h=(float) 30.0;
float phi = (float) Math.atan2(y2 - y1, x2 - x1);
float angle1 = (float) (phi - Math.PI / 6);
float angle2 = (float) (phi + Math.PI / 6);
float x3 = (float) (x2 - h * Math.cos(angle1));
float x4 = (float) (x2 - h * Math.cos(angle2));
float y3 = (float) (y2 - h * Math.sin(angle1));
float y4 = (float) (y2 - h * Math.sin(angle2));
c.drawLine(x1, y1,x2,y2 ,pnt);
c.drawLine(x2, y2,x3,y3 ,pnt);
c.drawLine(x2, y2,x4,y4 ,pnt);
I got help from the accepted answer and ios section in stackoverflow
How I would do this is to find the slope of the line, which is drawn between two points(start and end). The slope would be (dy/dx), and that would be a good start point for your arrow. Assuming you want the base of the arrowhead to be perpendicular to the line of the arrow, to find the slope of the base you would find the opposite reciprocal of the slope of the line. for example, lets say that your line has a slope of 2. The slope for the base of your triangle would be (-1/2), because you do (1/(oldslope)) and multiply by -1. I don't know android very well, but if I remember correctly, in Java, you would use a drawPolygon method, and you would have to specify 4 points(3 unique and 1 the same as the first to close it). Given the slope of the base of the tip, we can get our first two points and our final point. You should know before you start the dimensions of the arrowhead you wish to draw, so in this case b will be the length of your baseline. If you take ϴ=arctan(dy/dx), that will give you an angle between the x axis and your baseline. With that ϴ value, you can do ydif = b*sin(ϴ) to get the difference in y value between the two base corners of your arrow. Doing the same thing but with xdif = b*cos(ϴ) gives you the difference in the x value between the two base points. If the location of the final point of the line that the user drew is, say, (x1, y1), then the locations of the basepoints of the triangle would be (x1-(xdif/2), y1-(ydif/2)) and (x1+(xdif/2), y1+(ydif/2)). These two points, p1 and p2, are the first, second, and fourth points in the draw polygon method. To find the third point, we need to find the angle of the original line, by doing ϴ=arctan(dy/dx), this time using your original dy/dx. with that angle. Before we finish the actual calculation of the point, you first have to know how far from the end of your line the tip of the arrow should actually be, in my case, I will use the var h and h = 10. To get the cordinate, (x,y), assuming the cordinate for the line tip is (x1, y1)you would do (x1+hcosϴ, y1+hsinϴ). Use that for the third value in drawPolygon(), and you should be done. sorry if I kind of rushed at the end, I got kind of tired of typing, comment if you need help.
If you managed to draw a line from the input event, you might additionally draw a triangle on its end indicating the direction.
On another project I drew a square everytime a magnetic point on a grid was touched (as you can see here) Sorry I can not provide you any sample code right now. But if that's a suitable approach for you, I might post it later.
Here is a good code, its not mine, It was a Java Graphics2D code that I converted to Canvas. All credit go to the original guy/lady who wrote it
private void drawArrowHead(Canvas canvas, Point tip, Point tail)
{
double dy = tip.y - tail.y;
double dx = tip.x - tail.x;
double theta = Math.atan2(dy, dx);
int tempX = tip.x ,tempY = tip.y;
//make arrow touch the circle
if(tip.x>tail.x && tip.y==tail.y)
{
tempX = (tip.x-10);
}
else if(tip.x<tail.x && tip.y==tail.y)
{
tempX = (tip.x+10);
}
else if(tip.y>tail.y && tip.x==tail.x)
{
tempY = (tip.y-10);
}
else if(tip.y<tail.y && tip.x==tail.x)
{
tempY = (tip.y+10);
}
else if(tip.x>tail.x || tip.x<tail.x)
{
int rCosTheta = (int) ((10)*Math.cos(theta)) ;
int xx = tip.x - rCosTheta;
int yy = (int) ((xx-tip.x)*(dy/dx) + tip.y);
tempX = xx;
tempY = yy;
}
double x, y, rho = theta + phi;
for(int j = 0; j < 2; j++)
{
x = tempX - arrowLength * Math.cos(rho);
y = tempY - arrowLength * Math.sin(rho);
canvas.drawLine(tempX,tempY,(int)x,(int)y,this.paint);
rho = theta - phi;
}
}
Just call this for both sides of your line and it will draw an arrow at each side!