Instead of having a polygon with a solid line surrounding it I want to create one with a dotted line, is this possible?
I know you could do this when you override the onDraw method of the overlay in v1 but the Overlay class does not exist anymore so how else can I achieve this?
It's currently not possible, but you may upvote this enhancement here: http://code.google.com/p/gmaps-api-issues/issues/detail?id=4633
UPDATE
Recently, Google implemented this feature for polylines and polygons in Google Maps Android API v2 and marked issue 4633 as Fixed.
See information about stroke patterns in the Shapes Guide. See an example in the Polylines and Polygons tutorial.
You can also read the corresponding blog post here:
https://maps-apis.googleblog.com/2017/02/styling-and-custom-data-for-polylines.html
First of all, take a look on the API
https://developers.google.com/maps/documentation/android/reference/com/google/android/gms/maps/model/Polyline
it is not yet possible with v2, but on v3 javascript API, it already is, look here:
https://developers.google.com/maps/documentation/javascript/overlays#PolylineSymbols
But it seems that it's possible to use this v3 javascript API in an android app, look here:
https://developers.google.com/maps/articles/android_v3
Maybe, this will help you
Find a LatLng at a distance of radius units from center LatLng on Map
now convert both these LatLngs to screenCoordinates
Use the formula used to construct a cirle x = Rsin(theta) , y = Rcos(theta)
you divide the circle into N segments and then draw polylines(drawn on map) on the circumference of the circle converting the screen coordinates to LatLngs
more the number of N more it looks like a circle , I have used N = 120 according the zoom level ,I am using 13.
private void addDottedCircle(double radius) {//radius is in kms
clearDottedCircle();
LatLng center,start,end;
Point screenPosCenter,screenPosStart,screenPosEnd;
Projection p = mMap.getProjection();
center = searchCenterMarker.getPosition();
start = new LatLng(center.latitude + radius/110.54,center.longitude);
// radius/110.54 gives the latitudinal delta we should increase so that we have a latitude at radius distance
// 1 degree latitude is approximately 110.54 kms , so the above equation gives you a rough estimate of latitude at a distance of radius distance
screenPosCenter = p.toScreenLocation(center);
screenPosStart = p.toScreenLocation(start);
double R = screenPosCenter.y - screenPosStart.y;
int N = 120;//N is the number of parts we are dividing the circle
double T = 2*Math.PI/N;
double theta = T;
screenPosEnd = new Point();
screenPosEnd.x = (int)(screenPosCenter.x-R*Math.sin(theta));
screenPosEnd.y = (int) (screenPosCenter.y-R*Math.cos(theta));
end = p.fromScreenLocation(screenPosEnd);
for(int i =0;i<N;i++){
theta+=T;
if(i%2 == 0){
//dottedCircle is a hashmap to keep reference to all the polylines added to map
dottedCircle.add(mMap.addPolyline(new PolylineOptions().add(start,end).width(5).color(Color.BLACK)));
screenPosStart.x = (int) (screenPosCenter.x-R*Math.sin(theta));
screenPosStart.y = (int) (screenPosCenter.y-R*Math.cos(theta));
start = p.fromScreenLocation(screenPosStart);
}
else{
screenPosEnd.x = (int)(screenPosCenter.x-R*Math.sin(theta));
screenPosEnd.y = (int) (screenPosCenter.y-R*Math.cos(theta));
end = p.fromScreenLocation(screenPosEnd);
}
}
}
If you are still looking for an answer have a look at this :
How to draw dashed polyline with android google map sdk v2?
Related
I am new to android. I've been trying to apply a field of view to my current location in google map. The image shows a FOV on Google map in iOS.
So basically I did a similar thing by adding 5 triangles with different alpha in order to make the whole fov a gradiant.
I have to update this fov according to azimuth of the device, so I remove those triangles ,recompute them and add them again.
for(int i=0; i<5;i++){
if(triangles[i]!=null){
triangles[i].remove();
}
//triangles=null;
la = mvalues[0]-hang/2;
lx = lon+i*Math.sin(Math.toRadians(la))/2775;
ly = lat+i*Math.cos(Math.toRadians(la))/2775;
ra = mvalues[0]+hang/2;
rx = lon+i*Math.sin(Math.toRadians(ra))/2775;
ry = lat+i*Math.cos(Math.toRadians(ra))/2775;
triangles[i] = map.addPolygon(new PolygonOptions().add(new LatLng(lat,lon), new LatLng(ly,lx), new LatLng(ry,rx)).strokeColor(Color.TRANSPARENT).fillColor(Color.argb(50, 0, 0, 50)));
}
The fov is always blinking, and gc_concurrent always happens. Is it possible to make it not blinking?
Thanks
https://developers.google.com/maps/documentation/android/shapes
According to the Google documentation above.
You shall update the shape by calling
Polygon.setPoints Link
rather than remove/add.
Hope this would help
I'm working on an Android Application using Gooogle Maps API v2. I have markers on my map, and I'd like to circle one of them. I managed to do that easily, by using the Circle and Circle Options classes. But I'd also like my circle to keep the same size on the screen when zooming or unzooming, just like the markers do. It means that the circle must have a constant radius in terms of pixels. Sadly, we cannot set a radius in pixels in the API v2.
I have tried several solutions, but I'm not satisfied.
In the first one, I just multiply or divide the radius :
#Override
public void onCameraChange(CameraPosition position)
{
if(previousZoom > position.zoom) {
mSelectionCircle.setRadius(Math.abs(position.zoom - previousZoom)*2*mSelectionCircle.getRadius());
}
else if(previousZoom < position.zoom) {
mSelectionCircle.setRadius(Math.abs(position.zoom - previousZoom)*mSelectionCircle.getRadius()/2);
}
previousZoom = position.zoom;
}
It seemed to work at first, but produces wrong results when zooming quickly or zooming with fingers. Moreover, the scaling is clearly visible on the screen.
My second solution uses pixel-meter conversions. The idea is to recalculate the radius in meters when zooming/unzooming, so the circle has a constant size on the screen. To do that, I get the current position of the Circle on the screen:
Point p1 = mMap.getProjection().toScreenLocation(mSelectionCircle.getCenter());
Then I create another point which is on the edge of the circle:
Point p2 = new Point(p1.x + radiusInPixels, p1.y);
Where
int radiusInPixels = 40;
After that, I use a function which returns the distance between these two points in meters.
private double convertPixelsToMeters(Point point1, Point point2) {
double angle = Math.acos(Math.sin(point1.x) * Math.sin(point2.x)
+ Math.cos(point1.x) * Math.cos(point2.x) * Math.cos(point1.y- point2.y));
return angle * Math.PI * 6378100.0; // distance in meters
}
6378100 is average Earth radius. Finally, I set the new radius of the Circle :
mSelectionCircle.setRadius(convertPixelsToMeters(p1, p2));
It should work in theory but I get ridiculous radius values (10^7 m!). The conversion function may be wrong?
So is there a simpler method to do that, or if not, may you help me to understand why my second soluton doesn't work?
Thanks!
You probably don't really care about an exact pixel size, just that it looks the same for all zoom levels and device rotations.
Here is a fairly simple way to do this. Draw (and redraw if the zoom is changed) a circle whose radius is some percentage of the diagonal of the visible screen.
The Google Maps API v2 has a getProjection() function that will return the lat/long coordinates of the 4 corners of the visible screen. Then using the super convenient Location class, you can calculate the distance of the diagonal of what is visible on the screen, and use a percentage of that diagonal as the radius of your circle. Your circle will be the same size no matter what the zoom scale is or which way the device is rotated.
Here is the code in Java:
public Circle drawMapCircle(GoogleMap googleMap,LatLng latLng,Circle currentCircle) {
// get 2 of the visible diagonal corners of the map (could also use farRight and nearLeft)
LatLng topLeft = googleMap.getProjection().getVisibleRegion().farLeft;
LatLng bottomRight = googleMap.getProjection().getVisibleRegion().nearRight;
// use the Location class to calculate the distance between the 2 diagonal map points
float results[] = new float[4]; // probably only need 3
Location.distanceBetween(topLeft.latitude,topLeft.longitude,bottomRight.latitude,bottomRight.longitude,results);
float diagonal = results[0];
// use 5% of the diagonal for the radius (gives a 10% circle diameter)
float radius = diagonal / 20;
Circle circle = null;
if (currentCircle != null) {
// change the radius if the circle already exists (result of a zoom change)
circle = currentCircle;
circle.setRadius(radius);
} else {
// draw a new circle
circle = googleMap.addCircle(new CircleOptions()
.center(latLng)
.radius(radius)
.strokeColor(Color.BLACK)
.strokeWidth(2)
.fillColor(Color.LTGRAY));
}
return circle;
}
Use a custom icon for Marker instead. You can create Bitmap and Canvas, draw on the latter and use it as a Marker icon:
new MarkerOptions().icon(BitmapDescriptorFactory.fromBitmap(bitmap))...
EDIT:
My previous answer is no longer valid.
As Jean-Philippe Jodoin brought up, you can simply do that with markers and setting their anchor to 0.5/0.5. It's a way cleaner solution.
Pasting the suggested code snippet here for reference:
marker = mMap.addMarker(new MarkerOptions().position(latlng).anchor(0.5f, 0.5f));
Old answer:
I came accross the same problem and could not find a solution, so I did it myself, I will post in the hope that it is helpful to some other people.
The "marker" approach did not work for me because I wanted circles to be centered on a specific lat/lng, and you cannot do that with a marker: if you set a circle icon for your marker, the circle edge will touch the lat/lng, but the circle will not be centered on the lat/lng.
I created a function to compute what should be the size of the circle in meters given the latitude and the camera zoom level, then added a camera listener on the map to update the size of the circle each time the camera changes zoom level. The result is a circle not changing in size (to the bare eye at least).
Here is my code:
public static double calculateCircleRadiusMeterForMapCircle(final int _targetRadiusDip, final double _circleCenterLatitude,
final float _currentMapZoom) {
//That base value seems to work for computing the meter length of a DIP
final double arbitraryValueForDip = 156000D;
final double oneDipDistance = Math.abs(Math.cos(Math.toRadians(_circleCenterLatitude))) * arbitraryValueForDip / Math.pow(2, _currentMapZoom);
return oneDipDistance * (double) _targetRadiusDip;
}
public void addCircleWithConstantSize(){
final GoogleMap googleMap = ...//Retrieve your GoogleMap object here
//Creating a circle for the example
final CircleOptions co = new CircleOptions();
co.center(new LatLng(0,0));
co.fillColor(Color.BLUE);
final Circle circle = googleMap.addCircle(co);
//Setting a listener on the map camera to monitor when the camera changes
googleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
#Override
public void onCameraMove() {
//Use the function to calculate the radius
final double radius = calculateCircleRadiusMeterForMapCircle(12, co.getCenter().latitude, googleMap.getCameraPosition().zoom);
//Apply the radius to the circle
circle.setRadius(radius);
}
});
}
As MaciejGórski suggested, it's correct and easy way to go; but if you have a lot of markers in google map, let's say 5k markers for example, it will slow down performance dramatically. Some suggestions to show this matter are:
1) Let search Marker clustering utility of Google android map API.
2) However, Marker clustering maybe not fit completely your purpose. So you can customize it by yourself. Here is the thread discussing about this matter: https://github.com/googlemaps/android-maps-utils/issues/29
I'm sorry, I did not try it, since I found using Polyline satisfies my purpose (display a path).
Hope this help,
Mttdat.
In our application we use google map APIs v1.
I wrote grid-based clusterization for markers (total amount up to few thousands). Everything works fine - good performance, etc...
The only problem is that I calculate grid depending on currently viewed area
private void createCluster2DArray() {
double cwidth = (cachedrightLongitude - cachedleftLongitude) / clustersXnum;
double cheight = (cachedtopLatitude - cachedbottomLatitude) / clustersYnum;
for (int i = 0; i < clustersXnum; i++) {
for (int j = 0; j < clustersYnum; j++) {
Cluster cluster;
if (clusters[i][j] == null) {
cluster = new Cluster();
clusters[i][j] = cluster;
} else {
cluster = clusters[i][j];
cluster.list.clear();
}
//calculate dimensions
cluster.left = cachedleftLongitude + i * cwidth;
cluster.right = cluster.left + cwidth;
cluster.bottom = cachedbottomLatitude + j * cheight;
cluster.top = cluster.bottom + cheight;
cluster.calculateCenter(mMapView);
}
}
}
cachedrightLongitude, cachedrightLongitude, cachedrightLongitude, cachedrightLongitude are borders of device screen area in degrees.
The problem, you can see, is that cluster borders changing every time when user changes visible area (change zoom level, or just slide the screen). This leads to clusters recalculation and markers redistribution over them.
The only solution I see is to create some kind of static screen-independent clusters greed for each zoom level(for example at zoom level 5 size of cluster will be 10milli degrees and at level 6 it will be 2milli degrees, so only border-clusters will dynamicaly change their size and outer borders). Am i right?
Is there any other suggestions?
For android maps API v1 there is a clustering library here: https://github.com/damianflannery/Polaris. This is a fork of Cyril Mottier's Polaris library, but the discussion on pull request suggest it won't be merged back into original. See here. I haven't looked at the source, so I can't tell you if they use grid clustering.
As for your question, I think using static screen-independent cluster grid is the way to go. I'd only suggest changing the values of millidegrees. For zoom level that is different by 1, millidegs should be divided (or multiplied) by 2.
Also note that with latitude you can't use degrees value directly, but you have to push it through a Mercator projection. This is to make grid consist of squares instead of having them look like rectangles with height few times greater than width closer to the north and south poles.
This is basically what I do in Android Maps Extensions for maps API v2.
I assumed 180 degrees grid size on zoom level 0, so 90 degrees on zoom level 1, 45 on 2, etc. and about 85 microdegrees on zoom 21. The value can be changed in the API.
To you the most useful parts of the code from Extensions lib would be: SphericalMercator to convert latitude and some portions from GridClusteringStrategy.
Google recently introduced the circle as a map object in V2 and can easily be drawn:
https://developers.google.com/maps/documentation/android/shapes#circles
Does anyone know how to make this clickable, so that I can open an info window etc... ?
Thanks a lot for the help.
As of June 2016, Google has introduced clickable property of CircleOptions.
https://developers.google.com/android/reference/com/google/android/gms/maps/model/CircleOptions#public-constructor-summary
You can use Circle.setClickable(boolean) method as well and listen to OnCircleClickListener event.
https://developers.google.com/android/reference/com/google/android/gms/maps/model/Circle.html#public-methods
Update:
You should probably use GoogleMap::setOnCircleClickListener.
Old answer:
For Circles it should be easy: use OnMapClickListener and this code:
void onMapClick(LatLng position) {
LatLng center = circle.getCenter();
double radius = circle.getRadius();
float[] distance = new float[1];
Location.distanceBetween(position.latitude, position.longitude, center.latitude, center.longitude, distance);
boolean clicked = distance[0] < radius;
}
You can of course iterate over circles if you have more of them.
For others you have to code checking yourself. The algorithm for Polygon is not hard to find online.
It's very straight forward - mMap.setOnCircleClickListener(this);
I'm working on a mapping app that plots pins on a MapView based on a user's query. I'm trying to scale the map to fit all the results pins, but I've run into a seemingly strange situation.
I have two variables set up:
latSpan is the difference between the maximum latitude and minimum latitude of any of the results points
lonSpan is the difference between the maximum longitude and minimum longitude of any of the results points
This method
while ((mapView.getLatitudeSpan()) < latSpan) || (mapView.getLongitudeSpan() < lonSpan)){
mapController.zoomOut();
}//end of while loop
is supposed to zoom out to make sure all the pins fit on the viewable map screen.
But I'm experiencing something rather strange. The results of mapView.getLatitudeSpan() and mapView.getLongitudeSpan() are routinely greater than my latSpan and lonSpan values, so the MapController doesn't zoom out enough.
My map is zoomed in pretty far--level 15 or higher.
As an example, one set of search results gave the following values:
latSpan = 17928
lonSpan = 11636
mapView.getLatitudeSpan() = 21933
mapView.getLongitudeSpan() = 20598
Based on these numbers, you wouldn't think that the MapController would need to zoom out. Yet there are pins plotted both above the top and below the bottom of the screen. I changed my WHILE loop to read
while ((mapView.getLatitudeSpan() - 6000) < latSpan...
and that helps, but the right query will still cause issues.
But the real question is, why is this happening?
I'm not sure why you're code isn't working from the snippet provided. Its possible that you are not converting your latSpan and lonSpan to microDegrees (as shown below) and this would cause some issues.
Also if you're trying to make sure your mapView is showing all of the results, there's not much point trying to determine if it needs to zoom before zooming, just zoom it every time. If it turns out that it doesn't need to zoom then nothing will appear to happen and if it does then it does.
You can set a map up to encompass all of your points and move to the centroid of the points as follows:
GeoPoint max = new GeoPoint(maxLatitude, maxLongitude);
GeoPoint min = new GeoPoint(minLatitude, minLongitude);
int maxLatMicro = max.getLatitudeE6();
int maxLonMicro = max.getLongitudeE6();
int minLatMicro = min.getLatitudeE6();
int minLonMicro = min.getLongitudeE6();
GeoPoint center = new GeoPoint((maxLatMicro+minLatMicro)/2,(maxLonMicro + minLonMicro)/2);
controller.zoomToSpan(maxLatMicro - minLatMicro, maxLonMicro - minLonMicro);
controller.animateTo(center);