how to make multiple polygon on Google Map I am using
List<lat long> data = new Array List<Lat Long>();
but its create only one polygon when we draw another then last polygon deleted so anyone please help me thanks in advance
You can define a common method to draw polygon on google maps like this:
public PolygonOptions addPolygon(ArrayList < LatLng > arg) {
LatLng[] data = arg.toArray();
PolygonOptions polygonOptions;
for (int i = 0; i <= data.length; i++) {
polygonOptions = new PolygonOptions();
polygonOptions.add(data[i], data[i + 1], data[i + 2])).strokeColor(Color.RED).strokeWidth(2);
polygonOptions.fillColor(Color.parseColor("#51000000"));
return polygonOptions;
}
}
and then add it to your GoogleMap like this:
yourGoogleMap.addPolygon(addPolygon(data));
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).
I have a hicking app, that gets a array of markers from my database and plots them on my map, afterward it draws a polyline connecting all the markers. What i want to do is , to draw the polyline connecting the markers, but instead of showing 10 or 20 or 100 markers, to only SHOW 2, the marker that starts and the one that ends.
Would appreciate any help.
Below is the function that i use to draw the polylines.
private void createPolylinesFromLatLng(ArrayList<LatLng> polylist){
PolylineOptions options = new PolylineOptions().width(5).color(Color.BLUE).geodesic(true);
for (int z = 0; z < polylist.size(); z++) {
LatLng point = polylist.get(z);
options.add(point);
totalDistanceInMeters = SphericalUtil.computeLength(polylist);
test=String.format("%.2f",totalDistanceInMeters);
}
and this is how i retrieve my markers into a array
JSONArray array = new JSONArray(response);
for (int i = 0; i < array.length(); i++) {
JSONObject jo = array.getJSONObject(i);
Double lat = Double.parseDouble(jo.getString("lat"));
Double lng = Double.parseDouble(jo.getString("lon"));
polyline = new LatLng(Double.parseDouble(jo.getString("lat")),
Double.parseDouble(jo.getString("lon")));
polylist.add(polyline);
// location = new LatLng(lat,lng);
location=polyline;
MarkerOptions options = new MarkerOptions();
options.position(location);
mMap.addMarker(options);
thank you
One way of doing this would to just surround your code where you add the Marker with a if statement like this:
if(i == 0 || i == (array.length() - 1)){
MarkerOptions options = new MarkerOptions();
options.position(location);
mMap.addMarker(options);
}
That way only the first location and the last location are added.
I am trying to plot a complex polygon around a route, following its steps with a given radius. To do so I drew 50-sided uniform polygons (which are practically circles) around each step (coordinate) of the route. Now I obtain a set of coordinates of all the plotted circles around the route, and I can see them on the map but they are overlapped which is not very good looking, and it's not a good practice to add such a huge number of overlays on the map.
So what I need to do now is to merge all the polygons I have now into one polygon and plot it in the map.
I tried deleting the intersection points of every two polygons (testing if points of polygon1 lie inside polygon2 and vice versa) and merging all the rest of the coordinates in one array and then construct my new polygon but it didn't work. Here's a code snippet of how I'm doing this:
public ArrayList<PolygonOptions> polygons = new ArrayList<>();
// lineOptions is the set of route coordinates
for (int i = 0; i < lineOptions.getPoints().size() - 1; i++) {
// Draw a circle around each point of the route
PolygonOptions circle1 = drawCircle(lineOptions.getPoints().get(i), 0.1);
PolygonOptions circle2 = drawCircle(lineOptions.getPoints().get(i + 1), 0.1);
// Draw a convex hull between every two successive circles
PolygonOptions convexHull = convexHull(circle1, circle2);
convexHull.strokeWidth(0);
convexHull.fillColor(0x7F729E47);
activity.range.add(activity.mMap.addPolygon(convexHull));
polygons.add(convexHull);
}
if (polygons.size() == 1) {
pts.addAll(polygons.get(0).getPoints());
} else {
for (int i = 0; i < polygons.size() - 1; i++) {
ArrayList<LatLng> pts1 = new ArrayList<>();
ArrayList<LatLng> pts2 = new ArrayList<>();
pts1.addAll(polygons.get(i).getPoints());
pts2.addAll(polygons.get(i + 1).getPoints());
for (int j = 0; j < pts1.size(); j++) {
if (pointInPolygon(pts1.get(j), pts2)) {
pts1.remove(j);
}
}
for (int j = 0; j < pts2.size(); j++) {
if (pointInPolygon(pts2.get(j), pts1)) {
pts2.remove(j);
}
}
pts.addAll(pts1);
pts.addAll(pts2);
}
}
// This part didn't work
// PolygonOptions range = new PolygonOptions();
// range.addAll(pts);
// range.strokeWidth(0);
// range.fillColor(0x7F729E47);
// activity.range.add(activity.mMap.addPolygon(range));
Following #antonio's directions, and using JTS Topology Suit I was able to plot a polygon around the route (buffer the route) with a defined radius. When I used the buffer function in the JTS library I obtained a buffer of the route but the caps were oval, I read about it and this happens because the computed coordinates are projected on the earth's map which is not flat, the caps get more oval when the route is closer to the earth's poles, and more round when closer to the equator line (0ยบ latitude). Anyway, I used another functionality from thhe library to union all the polygons that I already had in my question, and this was the result:
public ArrayList<Polygon> polys = new ArrayList<>();
//lineOptions is the set of route coordinates
for (int i = 0; i < lineOptions.getPoints().size() - 1; i++) {
// Draw a circle around each point of the route
PolygonOptions circle1 = drawCircle(lineOptions.getPoints().get(i), 0.1);
PolygonOptions circle2 = drawCircle(lineOptions.getPoints().get(i + 1), 0.1);
// Draw a convex hull between every two successive circles
PolygonOptions convexHull = convexHull(circle1, circle2);
List<LatLng> latLngs = convexHull.getPoints();
List<Coordinate> coords = new ArrayList<>();
for (int j=0; j<latLngs.size(); j++) {
coords.add(new Coordinate(latLngs.get(j).latitude, latLngs.get(j).longitude));
if(j==latLngs.size()-1)
coords.add(new Coordinate(latLngs.get(0).latitude, latLngs.get(0).longitude));
}
Coordinate[] coordinates = coords.toArray(new Coordinate[coords.size()]);
GeometryFactory fact = new GeometryFactory();
LinearRing linear = new GeometryFactory().createLinearRing(coordinates);
Polygon poly = new Polygon(linear, null, fact);
polys.add(poly);
}
PolygonOptions combine = combineIntoOnePolygon(polys);
combine.strokeWidth(0);
combine.fillColor(0x7F729E47);
activity.range = activity.mMap.addPolygon(combine);
The function that does the combining:
static PolygonOptions combineIntoOnePolygon(Collection<Polygon> geometries ){
Geometry all = null;
PolygonOptions allPolygon = new PolygonOptions();
for(Iterator<Polygon> i = geometries.iterator(); i.hasNext(); ){
Polygon geometry = i.next();
if( geometry == null ) continue;
if( all == null ){
all = geometry;
}
else {
all = all.union( geometry );
}
}
List<Coordinate> bufferCoordinates = Arrays.asList(all.getCoordinates());
for (Coordinate c : bufferCoordinates) {
allPolygon.add(new LatLng(c.x, c.y));
}
return allPolygon;
}
You need to compute the buffer of the line. According to Wikipedia:
A buffer is an area defined by the bounding region determined by a set of points at a specified maximum distance from all nodes along segments of an object.
The buffer of a geometry is computed using the Minkowski sum. The Minkowsky sum takes two polygons (one is your polyline and the other is a circle if you want rounded caps) and moves the second one throught the path of the first one.
You can find some concrete implementations of the Minkowski sum that you can study. For example https://github.com/perelo/computational-geometry/blob/master/src/computational_geometry/model/algorithms/Minkowski.java
Hi Im having a little issue with displaying points on a map.
I use an Arraylist to store multiple lat/lng values and then do a for loop to add the point and auto zoom. Everything works fine when there are 2 or more markers. The problem is when only 1 marker is added it zooms in too close.
Anyone know how to resolve this?
public static void processMap()
{
for (int i = 0; i < lat.size(); i++)
{
MarkerOptions markerOptions = new MarkerOptions();
LatLng latLng = new LatLng(lat.get(i), lng.get(i));
markerOptions.position(latLng);
markerOptions.title("title");
markerOptions.snippet("description");
mMap.addMarker(markerOptions);
bounds.include(new LatLng(lat.get(i), lng.get(i)));
}
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), 150));
}
Updated code
public static void processMap()
{
int num = 0;
double lat2 = 0;
double lng2 = 0;
for (int l = 0; l < lat.size(); l++)
{
lat2 = lat.get(l);
lng2 = lng.get(l);
LatLng latLng = new LatLng(lat2, lng2);
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(latLng);
markerOptions.title(title.get(l));
markerOptions.snippet(description.get(l));
mMap.addMarker(markerOptions);
bounds.include(new LatLng(lat2, lng2));
num++;
}
if (num == 1)
{
// if only 1 marker
LatLng latLng2 = new LatLng(lat2, lng2);
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng2, 16));
}
else
{
// more than 1 marker
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), 150));
}
}
Hi sorry I didn't reply earlier I was attempting to fix the problem. I managed to fix it, but is this the correct way of going about it?
The edit you made should fix your problem. Anyway you don't need to set new LatLng if you only have 1 marker. Passing the bound should do. I would make this little edit:
if (num == 1)
{
// if only 1 marker
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(bounds.build(), 16));
}
else
{
// more than 1 marker
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), 150));
}
Hope it helps. Good luck!
Your code does not really tell why you are using .newLatLngBounds(), but this will center the map on your given bounding box and respecting the given 150px padding around the box.
The zoom is then adapted according to the elements inside the bounding box to show all of them. When there is only one marker inside the box, it will zoom to it (and probably too close). The docs say that the map will be centered "at the greatest possible zoom level".
So to prevent this, I would choose a good zoom level for the first marker, add it, move the camera with moveCamera(...) and after that continue with your code when there are other markers left.
The code below doesn't work with Google Maps API v2. The polygons (outer and inner polygons) are drawn with the right border, but the fill color of the outer one is not drawn.
PolygonOptions polygonOptions = new PolygonOptions();
polygonOptions.add(outerCoordinates);
polygonOptions.addHole(Arrays.asList(innerCoordinates));
polygonOptions.fillColor(Color.BLUE);
polygonOptions.strokeWidth(1.0f);
Does anybody face the same problem?
Check whether there is a requirement that polygon coordinates have to be clockwise (or counterclockwise) ordered. Try to change the order.
The vertices must be added in counterclockwise order. Reference
I wrote a function to determinate if a List<LatLng> is clockwise. The code is an implementation of this answer:
public boolean isClockwise(List<LatLng> region) {
final int size = region.size();
LatLng a = region.get(size - 1);
double aux = 0;
for (int i = 0; i < size; i++) {
LatLng b = region.get(i);
aux += (b.latitude - a.latitude) * (b.longitude + a.longitude);
a = b;
}
return aux <= 0;
}
Before adding the polygon points put these three lines:
if (isClockwise(polygon)) {
Collections.reverse(polygon);
}