I am trying to build ActivityTracker application for Android using Xamarin. I have already got access to Google Maps Api and can get current device location. Next step is connect locations I got into a polyline, but Polyline is not visible on map.
Here is code I tried
private Polyline polyline;
public void OnMapReady(GoogleMap map)
{
_map = map;
_map.MyLocationEnabled = true;
_map.TrafficEnabled = true;
_map.SetIndoorEnabled(true);
PolylineOptions poly = new PolylineOptions()
.InvokeColor(Color.Red)
.InvokeWidth(5)
.Visible(true)
.InvokeZIndex(30);
poly.Add(new LatLng(95, 50));
poly.Add(new LatLng(90, 55));
poly.Add(new LatLng(100, 50));
polyline = _map.AddPolyline(poly);
_map.MyLocationChange += MyLocationChanged;
}
Your code is good, but coordinates of polyline points is bad: Google Maps using WGS 84 Web Mercator projection with truncated latitudes of Latmax = ±85.05113°, so there is just no point with coordinates LatLng(95, 50) (95 - too big for latitude) and LatLng(90, 55) and LatLng(100, 50). Your coordinates should be from (-89.99,-179.99) to (89.99,179.99). Try, for example use 55, 60 and 70 instead of 95, 90 and 100:
private Polyline polyline;
public void OnMapReady(GoogleMap map)
{
_map = map;
_map.MyLocationEnabled = true;
_map.TrafficEnabled = true;
_map.SetIndoorEnabled(true);
PolylineOptions poly = new PolylineOptions()
.InvokeColor(Color.Red)
.InvokeWidth(5)
.Visible(true)
.InvokeZIndex(30);
poly.Add(new LatLng(55, 50));
poly.Add(new LatLng(60, 55));
poly.Add(new LatLng(70, 50));
polyline = _map.AddPolyline(poly);
_map.MyLocationChange += MyLocationChanged;
}
Related
I have 4 LatLng. For making a rectangle polygon, they should be in a list in either clockwise or anti-clockwise direction, so that when i add these LatLng in addPolygon(new LatLng) in google map it will create a rectangle shape.
But in my case, they might be in clockwise or anti-clockwise or in different form. One example is -> 1st LatLng followed by 3rd LatLng followed by 2nd LatLng followed by 4th LatLng. In this case, It will not create rectangle polygon on google map.
So I have to arrange it in clockwise or anti clockwise, so that the shape form must be rectangle, When i Put this LatLng list in addPolygon() in google map.
Suppose:
val rectOptions = PolygonOptions()
.add(
LatLng(37.35, -122.0),
LatLng(37.45, -122.0),
LatLng(37.45, -122.2),
LatLng(37.35, -122.2)
)
If I put rectOptions in AddPolygon() in mMap.addPolygon(rectOptions)
it will Create a rectangular shape on google map. In my case:
val rectOptions = PolygonOptions()
.add(
LatLng(37.45, -122.0),
LatLng(37.45, -122.2),
LatLng(37.35, -122.0),
LatLng(37.35, -122.2)
)
This will not create rectangular shape. So I have to arrange these LatLng so that they form rectangular shape.
In general your task is Convex hull construction and can be solved by one of Convex hull algorithms, e.g. like Gift wrapping (aka Jarvis) algorithm in this implementation.
Note that most of Convex hull algorithms implementations is for flat (x,y) point coordinates, not for LatLng location coordinates, so easiest way is to convert LatLng to flat (x,y) points with Projection.toScreenLocation() method and than, after Convex hull algorithm apply, convert it back to LatLng with Projection.fromScreenLocation() method.
Also remember, that the Projection object will only return valid values after the map has passed the layout process (i.e. it has valid width and height set) and you can get it in OnCameraIdleListener or use approach, described by andr in this answer.
So full demo source code can be like that:
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
private GoogleMap mGoogleMap;
private SupportMapFragment mMapSupportedFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMapSupportedFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map_fragment);
mMapSupportedFragment.getMapAsync(MainActivity.this);
}
#Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
#Override
public void onCameraIdle() {
ArrayList<LatLng> sourcePoints = new ArrayList<>();
sourcePoints.add(new LatLng(37.35, -122.0));
sourcePoints.add(new LatLng(37.45, -122.2));
sourcePoints.add(new LatLng(37.40, -122.1));
sourcePoints.add(new LatLng(37.35, -122.2));
sourcePoints.add(new LatLng(37.45, -122.0));
Projection projection = mGoogleMap.getProjection();
ArrayList<Point> screenPoints = new ArrayList<>(sourcePoints.size());
for (LatLng location : sourcePoints) {
Point p = projection.toScreenLocation(location);
screenPoints.add(p);
}
ArrayList<Point> convexHullPoints = convexHull(screenPoints);
ArrayList<LatLng> convexHullLocationPoints = new ArrayList(convexHullPoints.size());
for (Point screenPoint : convexHullPoints) {
LatLng location = projection.fromScreenLocation(screenPoint);
convexHullLocationPoints.add(location);
}
PolygonOptions polygonOptions = new PolygonOptions();
for (LatLng latLng : convexHullLocationPoints) {
polygonOptions.add(latLng);
}
mGoogleMap.clear();
Polygon polygon = mGoogleMap.addPolygon(polygonOptions.strokeColor(Color.argb(255, 49, 101, 187)).fillColor(Color.argb(100, 49, 101, 187)));
}
});
}
private boolean CCW(Point p, Point q, Point r) {
return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y) > 0;
}
public ArrayList<Point> convexHull(ArrayList<Point> points)
{
int n = points.size();
if (n <= 3) return points;
ArrayList<Integer> next = new ArrayList<>();
// find the leftmost point
int leftMost = 0;
for (int i = 1; i < n; i++)
if (points.get(i).x < points.get(leftMost).x)
leftMost = i;
int p = leftMost, q;
next.add(p);
// iterate till p becomes leftMost
do {
q = (p + 1) % n;
for (int i = 0; i < n; i++)
if (CCW(points.get(p), points.get(i), points.get(q)))
q = i;
next.add(q);
p = q;
} while (p != leftMost);
ArrayList<Point> convexHullPoints = new ArrayList();
for (int i = 0; i < next.size() - 1; i++) {
int ix = next.get(i);
convexHullPoints.add(points.get(ix));
}
return convexHullPoints;
}
}
Also you can find more simple algorithm if you need to "sort" points only for rectangles (e.g. you need to test which 3 points form a right angle and add it from first to third and then add fourth point and so on).
In a project I'm displaying a route using PolylineOptions for drawing rects from one POI to the next POI.
Also in each POI I'm drawing a circle.
The objective is to represent the direction of the route in some way, for example, drawing an arrow in the middle of each PolylineOptions rect. The problem is that I can't find how to do this.
This is my code:
PolylineOptions rectOptions = new PolylineOptions();
float[] prevHSV = new float[3];
Color.colorToHSV(getResources().getColor(R.color.colorPrimary), prevHSV);
rectOptions.color(Color.HSVToColor(255, prevHSV));
String[][] routeInformation = ((MyApplication)getApplication()).getLineInformation(line);
ArrayList<Double[]> routeStops = Util.getFullRouteFromLine(this, line);
final LatLngBounds.Builder builder = new LatLngBounds.Builder();
for (int i=0; i<routeInformation.length; i++){
LatLng latlng = new LatLng(Double.parseDouble(routeInformation[i][0]),Double.parseDouble(routeInformation[i][1]));
builder.include(latlng);
mMap.addCircle(new CircleOptions().center(latlng).radius(15).strokeColor(Color.HSVToColor(255, prevHSV)).fillColor(Color.HSVToColor(255, prevHSV)).zIndex(7777));
}
for (Double[] pos : routeStops){
rectOptions.add(new LatLng(pos[0],pos[1])).width(5);
}
mMap.addPolyline(rectOptions);
Is there a easy way to represent the direction of a route?
Something similar to this, but this is for the web version of google maps, not for the android version: http://jsfiddle.net/nX8U8/2/
IMHO easiest way is to use flat "north-oriented" direction marker (arrow) like that:
created via vector drawable ic_arrow_up.xml:
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="560"
android:viewportWidth="560"
android:height="24dp"
android:width="24dp"
>
<path android:fillColor="#0000FF"
android:pathData="M0,559.43C0,557.943 279.994,-0.561 280.458,0C282.014,1.884 560.512,559.569 559.999,559.776C559.665,559.911 496.562,532.823 419.77,499.581C419.77,499.581 280.15,439.14 280.15,439.14C280.15,439.14 140.756,499.57 140.756,499.57C64.089,532.807 1.056,560 0.681,560C0.307,560 0,559.743 0,559.43C0,559.43 0,559.43 0,559.43Z"
/>
</vector>
placed on each path polyline segment middle point with angle calculated on segment bearing. Both polyline segment middle and bearing you can determine via SphericalUtil class of Google Maps Android API Utility Library . Bearing can be found via SphericalUtil.computeHeading() with first and second points of segment as arguments and LatLng of segment middle point - via SphericalUtil.interpolate() also with first and second points of segment as arguments from and to and constant 0.5 (half) as fraction argument.
So, with source like this:
...
#Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
#Override
public void onMapLoaded() {
List<LatLng> sourcePoints = new ArrayList<>();
PolylineOptions polyLineOptions;
sourcePoints.add(new LatLng(-35.27801,149.12958));
sourcePoints.add(new LatLng(-35.28032,149.12907));
sourcePoints.add(new LatLng(-35.28099,149.12929));
sourcePoints.add(new LatLng(-35.28144,149.12984));
sourcePoints.add(new LatLng(-35.28194,149.13003));
sourcePoints.add(new LatLng(-35.28282,149.12956));
sourcePoints.add(new LatLng(-35.28302,149.12881));
sourcePoints.add(new LatLng(-35.28473,149.12836));
polyLineOptions = new PolylineOptions();
polyLineOptions.addAll(sourcePoints);
polyLineOptions.width(10);
polyLineOptions.color(Color.BLUE);
mGoogleMap.addPolyline(polyLineOptions);
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(sourcePoints.get(0), 15));
for (int i = 0; i < sourcePoints.size() - 1; i++) {
// get first and second points of polyline segment
LatLng segmentP1 = sourcePoints.get(i);
LatLng segmentP2 = sourcePoints.get(i+1);
// calculate middle point
LatLng segmentMiddlePoint = SphericalUtil.interpolate(segmentP1, segmentP2, 0.5);
// calculate bearing
float segmentBearing = (float) SphericalUtil.computeHeading(segmentP1, segmentP2);
// add flat marker at segment middle
addDirectionMarker(segmentMiddlePoint, segmentBearing);
}
});
}
...
public void addDirectionMarker(LatLng latLng, float angle) {
Drawable circleDrawable = ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_arrow_up);
BitmapDescriptor markerIcon = getMarkerIconFromDrawable(circleDrawable, 30, 30);
mGoogleMap.addMarker(new MarkerOptions()
.position(latLng)
.anchor(0.5f, 0.5f)
.rotation(angle)
.flat(true)
.icon(markerIcon)
);
}
you'll got something like that:
Also you can change size of arrows markers by set other values than 30, 30 in
BitmapDescriptor markerIcon = getMarkerIconFromDrawable(circleDrawable, 30, 30);
line.
I am trying to make it so that when I launch my app the phone will zoom in and center to the area in which I have set PolyLines to via an ArrayList of lat and lang points.
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
ArrayList<MyPoint> points = Data.getPoints();
PolylineOptions polylineOptions = new PolylineOptions();
for (int i = 0; i < points.size(); i++) {
MyPoint point = points.get(i);
polylineOptions.add(new LatLng(point.getLat(), point.getLng()));
}
Polyline polyline = mMap.addPolyline(polylineOptions);
}
I start up the app in genymotion and it zooms in and then centers at the lat/lang ArrayList points I've set.
Currently, I just get a map that shows nearly the whole globe and I have to zoom in manually to the polylines I've set up.
You need the camera update factory class to create a camera update and move the map's camera. Camera Update Factory Documentation
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
ArrayList<MyPoint> points = Data.getPoints();
int padding = 20; // or prefer padding
LatLngBounds.Builder builder = new LatLngBounds.Builder();
PolylineOptions polylineOptions = new PolylineOptions();
for (int i = 0; i < points.size(); i++) {
MyPoint point = points.get(i);
polylineOptions.add(new LatLng(point.getLat(), point.getLng()));
builder.include(new LatLng(point.getLat(), point.getLng()));
}
Polyline polyline = mMap.addPolyline(polylineOptions);
LatLngBounds bounds = builder.build();
map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, padding));
}
You need to use LatLngBounds and then zoom your camera to cover all the included markers:
LatLngBounds.Builder builder = new LatLngBounds.Builder();
Use the following line in a loop for all points/markers: (You can use the same loop you are using to add points to polylineOptions.)
builder.include(pointLatLng);
And finally, (outside the loop) -
LatLngBounds bounds = builder.build();
map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 20));
I'm using google directions and make a route between two points and setting a marker and I need to set a window with some information in both of these markers, but According to google I just can show a window one at the time! But in Uber App they did with both points.
This is what I did :
public void drawRoute(){
PolylineOptions po;
if(polyline == null){
po = new PolylineOptions();
for(int i = 0, tam = latLngs.size(); i < tam; i++){
po.add(latLngs.get(i));
}
po.color(Color.BLACK).width(10);
polyline = mMap.addPolyline(po);
LatLng myCurrentLocation = new LatLng(lat, lon);
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(myCurrentLocation, 11));
Marker mMarker;
mMarker = mMap.addMarker(new MarkerOptions().position(finalLocaltion).title(finalLocationName));
mMarker.showInfoWindow();
}
else{
polyline.setPoints(latLngs);
}
}
The window only appears when I click, and not by default!
I have a list full of LatLng points to create a polygon in google maps android v2, but I cannot figure out how to add this list to the .add area correctly. Here is my list full of points:
list.add(new LatLng(la,lo));
Here is the polygon via Google Dev
Polygon polygon = map.addPolygon(new PolygonOptions()
.add(*******HOW TO ITTERATE LIST************).strokeColor(Color.RED)
.fillColor(Color.BLUE));
How do you go about adding the points?
PolygonOptions opts=new PolygonOptions();
for (LatLng location : list) {
opts.add(location);
}
Polygon polygon = map.addPolygon(opts.strokeColor(Color.RED).fillColor(Color.BLUE));
This assumes that list is something like an ArrayList<LatLng>.
I had the same issue and I find this without need for iteration.
PolygonOptions polOpt =
new PolygonOptions().addAll(latlngs).strokeColor(Color.RED).fillColor(Color.BLUE);
Polygon polygon = mMap.addPolygon(polOpt);
It may help someone. I did the same its working.
ArrayList<LatLng> locations = new ArrayList();
Adding the latLng value in list.
locations .add(new LatLng(2, 2));
locations .add(new LatLng(4, 2));
locations .add(new LatLng(4, 4));
locations .add(new LatLng(2, 4));
locations .add(new LatLng(2, 2))
Creating the GeoFence on the map
PolylineOptions options = new PolylineOptions().width(5).color(Color.RED).geodesic(true);
for (int z = 0; z < locations.size(); z++) {
LatLng point = locations.get(z);
options.add(point);
}
mMap.addPolyline(options);
CameraPosition cameraPosition;
cameraPosition = new CameraPosition.Builder().target(new LatLng(50.8404969, -0.1504184)).zoom(15).build();
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));