I have been trying to draw a rectangle on the whole map(in a map activity) in Android Studio, i need a delimitation of the equator area, from one part of the map to the other. ( in a big rectangle) But every time i put the coordinates for the rectangle it goes the other way around, so it goes backwards and makes a small square from Pacific ocean to China , Australia and back.
Also, any idea how i can make a button take the shape of a country on the map?
package com.example.android.coffeeknowledge;
import android.content.res.Resources;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MapStyleOptions;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.PolygonOptions;
import com.google.android.gms.maps.model.PolylineOptions;
public class coffeeMap extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coffee_map);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
/**
* Manipulates the map once available.
* This callback is triggered when the map is ready to be used.
* This is where we can add markers or lines, add listeners or move the camera. In this case,
* we just add a marker near Sydney, Australia.
* If Google Play services is not installed on the device, the user will be prompted to install
* it inside the SupportMapFragment. This method will only be triggered once the user has
* installed Google Play services and returned to the app.
*/
private static final LatLng cameraZoom = new LatLng(37.35, -122.0);
#Override
public void onMapReady(GoogleMap googleMap) {
try{
boolean success = googleMap.setMapStyle(
MapStyleOptions.loadRawResourceStyle(this, R.raw.mapstyle));
if(!success){
Log.e("coffeeMap","Style parsing failed.");
}
}catch(Resources.NotFoundException e){
Log.e("coffeeMap", "Can`t find style.Error: " , e);
}
mMap = googleMap;
// Instantiates a new Polygon object and adds points to define a rectangle
PolygonOptions rectOptions = new PolygonOptions()
.fillColor(R.color.white)
.add(new LatLng(24.376368, 101.181309),
new LatLng(-28.912738, 103.818027),
new LatLng(-26.841671, -117.944509),
new LatLng(27.616242, -122.020003),
new LatLng(24.376368, 101.181309));
// Get back the mutable Polygon
Polygon polygon = mMap.addPolygon(rectOptions);
// Add a marker in Sydney and move the camera
//mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(cameraZoom, 13));
LatLng sydney = new LatLng(35.175321, -107.619365);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker"));
}
}
Thank you.
The map api will always choose the shortest route between 2 points taking into account geodesic path or rectangular projection. The segments of interest in your example are:
new LatLng(-28.912738, 103.818027), // A
new LatLng(-26.841671, -117.944509), // B
and
new LatLng(27.616242, -122.020003), // C
new LatLng(24.376368, 101.181309)) // D
These two segments will cross the anti-meridian (and not go the other way around) because that is the shortest path between those two points. (This is always what would be desired.)
So to overcome this in your example, simply add an intermediate mid-point (assuming non-geodesic, or rectangular projection) in the segments (A-B and C-D) to force it to go "the other way". Using A-B as an example:
new LatLng(-28.912738, 103.818027),
new LatLng(-27.877204, -7.0), // approximate midpoint in desired direction (M)
new LatLng(-26.841671, -117.944509),
So the original distance from A-B (assuming geodesic) is 12830 km. And with the forced intermediate point is: A-M 10320km and M-B 10460km. These distance calculations are merely to demonstrate the point (pun intended).
The same approach applies to C-D.
So in pictures, your OP view using:
PolygonOptions rectOptions = new PolygonOptions()
.fillColor(R.color.colorPrimary)
.add(new LatLng(24.376368, 101.181309),
new LatLng(-28.912738, 103.818027),
new LatLng(-26.841671, -117.944509),
new LatLng(27.616242, -122.020003),
new LatLng(24.376368, 101.181309));
is displayed as:
and with the 2 intermediate points:
PolygonOptions rectOptions = new PolygonOptions()
.fillColor(R.color.colorPrimary)
.add(new LatLng(24.376368, 101.181309),
new LatLng(-28.912738, 103.818027),
new LatLng(-27.877204, -7.0),
new LatLng(-26.841671, -117.944509),
new LatLng(27.616242, -122.020003),
new LatLng( 25.9, -7.0),
new LatLng(24.376368, 101.181309));
is displayed as:
Just for fun, and to emphasize the midpoint determination depends on the projection, here is the same polygon using geodesic:
Finding the spherical midpoint for 2 points spanning an arc greater than pi radians is a problem for another day...
A handy online tool for more consideration: https://www.movable-type.co.uk/scripts/latlong.html.
Related
How can I retrieve latitude and longitude of Google Maps data from my database and display it to the user on the map in android ?
I have a database mysql containing some fields as longitude and latitude.
What is the best way to do such?
Is it possible through a Volley library?
If there is an example of this, it is good to me
Yes you can draw stuff in the onMapReady callback. You can create Markers, draw circles, draw polygons.
To add a marker just call:
LatLng point = new LatLng(exampleLat, exampleLng);
mGoogleMap.addMarker(new MarkerOptions().position(point));
Here is an example of how to draw a circle:
CircleOptions circleOptions = new CircleOptions()
.center(new LatLng(lat, lng))
.radius(radius)
.fillColor(Color.RED)
.strokeColor(Color.TRANSPARENT);
Circle circle = mGoogleMap.addCircle(circleOptions);
Here is a Polygon:
PolygonOptions polygonalOptions = new PolygonOptions()
.fillColor(Color.RED)
.strokeColor(Color.TRANSPARENT)
.clickable(true);
for (List<Double> latLng : myPolygon.path) {
LatLng point = new LatLng(latLng.get(0), latLng.get(1));
polygonalOptions.add(point);
}
mGoogleMap.addPolygon(polygonalOptions);
PS: Writing from my phone sorry for any mistakes or formatting issues.
I want to show polyline between start point and end point of particular route in mapview with lite mode in recycler view.for that I used latlngbounds to include all the route point but when route is so long say for 20 km one of two point is not showing in mapview ..any idea how to achieve this..any help is appreciated
I had managed this scenario by setting the padding on four sides by following code snippet.
mGoogleMap.setPadding(100, 100, 100, 100); //GoogleMap mGoogleMap
Then add two latlng to LatLngBounds.Builder.
Add builder to bounds, then to Map.
public void setViewPortToBounds(LatLng point1, LatLng point2) {
mGoogleMap.setPadding(100, 100, 100, 100);
mapBuilder = new LatLngBounds.Builder();
mapBuilder.include(point1);
mapBuilder.include(point2);
mapBounds = mapBuilder.build();
mGoogleMap.setOnCameraChangeListener(onCameraChangeListener);
}
GoogleMap.OnCameraChangeListener onCameraChangeListener = new GoogleMap
.OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition cameraPosition) {
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(mapBounds, 0));
mGoogleMap.setOnCameraChangeListener(null);
}
};
i solved it by including all route points on polyline and then move camera to that bounds,and then zoom out the camera..
I have continuous LatLngs of various areas in a city. Is there any way I can create clickable polygons with it. Once way to go about would be to
Generate polygons with the available LatLngs.( I want to visually show the polygons on the map with color encoding)
Set up setOnMapClickListener.
Do a point inside polygon test.
I understand that this is very naive. What are the alternative approaches?
Here's how I did it.
Polygon polygon = getMap().addPolygon(new PolygonOptions()
.add(new LatLng(12.780712, 77.770956), new LatLng(12.912006, 77.229738), new LatLng(12.412006, 77.629738), new LatLng(12.912006, 77.229738))
.strokeColor(0xFF00AA00)
.fillColor(0x2200FFFF)
.strokeWidth(2)
);
polygon.setClickable(true);
getMap().setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() {
public void onPolygonClick(Polygon polygon) {
mClusterManager = new ClusterManager<MyItem>(getApplicationContext(), getMap());
getMap().setOnCameraChangeListener(mClusterManager);
getMap().moveCamera(CameraUpdateFactory.newLatLngZoom(getMap().getCameraPosition().target, getMap().getCameraPosition().zoom));
try {
readItems();
} catch (JSONException e) {
Toast.makeText(getApplicationContext(), "Problem reading list of markers.", Toast.LENGTH_LONG).show();
}
}
});
Hope that helps.
you don't need to go crazy for having clickable polygon. I did it time ago but now, there is an api for that:
GoogleMap.setOnPolygonClickListener(OnPolygonClickListener)
You can use it easily:
GoogleMap mymap =....//init your map
mymap.setOnPolygonClickListener(new OnPolygonClickListener(){
void onPolygonClick(Polygon polygon){
//do whatever with polygon!
}
});
In adding Polygon to the map. First create a PolygonOptions object and add some points to it. These points will form the outline of the polygon. You then add the polygon to the map by calling GoogleMap.addPolygon(PolygonOptions) which will return a Polygon object.
This following code snippet show how to add polygon to a map.
// Instantiates a new Polygon object and adds points to define a rectangle PolygonOptions rectOptions = new PolygonOptions()
.add(new LatLng(37.35, -122.0),
new LatLng(37.45, -122.0),
new LatLng(37.45, -122.2),
new LatLng(37.35, -122.2),
new LatLng(37.35, -122.0));
// Get back the mutable Polygon Polygon polygon = myMap.addPolygon(rectOptions);
By default, polygons are not clickable. You can enable and disable the clickability by calling Polygon.setClickable(boolean).
Like N Dorigatti said. In using OnPolygonClickListener to listen click events, call GoogleMap.setOnPolygonClickListener(OnPolygonClickListener).
When a user clicks on a polygon, you will receive an onPolygonClick(Polygon) callback.
Check this document for more information.
I'm going to develop a location-based game for android platforms. I want to create a marker in random coordinates (around the user location) but on the road! Moreover i'm going to animate this marker from this point (A) to second random point (B). Any idea?
thank you!
ps.I use the sample code of the example of Nutiteq 3D map app.
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import com.nutiteq.MapView;
import com.nutiteq.components.Color;
import com.nutiteq.components.Components;
import com.nutiteq.components.MapPos;
import com.nutiteq.components.Options;
import com.nutiteq.geometry.Marker;
import com.nutiteq.log.Log;
import com.nutiteq.projections.EPSG3857;
import com.nutiteq.projections.Projection;
import com.nutiteq.rasterlayers.TMSMapLayer;
import com.nutiteq.style.MarkerStyle;
import com.nutiteq.ui.DefaultLabel;
import com.nutiteq.ui.Label;
import com.nutiteq.utils.UnscaledBitmapLoader;
import com.nutiteq.vectorlayers.MarkerLayer;
public class MainActivity extends Activity {
private MapView mapView;
#SuppressLint("NewApi")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// enable logging for troubleshooting - optional
Log.enableAll();
Log.setTag("hellomap");
// 1. Get the MapView from the Layout xml - mandatory
mapView = (MapView) findViewById(R.id.mapView);
// Optional, but very useful: restore map state during device rotation,
// it is saved in onRetainNonConfigurationInstance() below
Components retainObject = (Components) getLastNonConfigurationInstance();
if (retainObject != null) {
// just restore configuration and update listener, skip other initializations
mapView.setComponents(retainObject);
MyLocationMapEventListener mapListener = (MyLocationMapEventListener) mapView.getOptions().getMapListener();
mapListener.reset(this, mapView);
mapView.startMapping();
return;
} else {
// 2. create and set MapView components - mandatory
mapView.setComponents(new Components());
}
// 3. Define map layer for basemap - mandatory.
// Here we use MapQuest open tiles
// Almost all online tiled maps use EPSG3857 projection.
TMSMapLayer mapLayer = new TMSMapLayer(new EPSG3857(), 0, 18, 0,
"http://otile1.mqcdn.com/tiles/1.0.0/osm/", "/", ".png");
mapView.getLayers().setBaseLayer(mapLayer);
// set initial map view camera - optional. "World view" is default
// Location: San Francisco
// NB! it must be in base layer projection (EPSG3857), so we convert it from lat and long
mapView.setFocusPoint(mapView.getLayers().getBaseLayer().getProjection().fromWgs84(-122.41666666667f, 37.76666666666f));
// rotation - 0 = north-up
mapView.setRotation(0f);
// zoom - 0 = world, like on most web maps
mapView.setZoom(10.0f);
// tilt means perspective view. Default is 90 degrees for "normal" 2D map view, minimum allowed is 30 degrees.
mapView.setTilt(75.0f);
// Activate some mapview options to make it smoother - optional
mapView.getOptions().setPreloading(true);
mapView.getOptions().setSeamlessHorizontalPan(true);
mapView.getOptions().setTileFading(true);
mapView.getOptions().setKineticPanning(true);
mapView.getOptions().setDoubleClickZoomIn(true);
mapView.getOptions().setDualClickZoomOut(true);
// set sky bitmap - optional, default - white
mapView.getOptions().setSkyDrawMode(Options.DRAW_BITMAP);
mapView.getOptions().setSkyOffset(4.86f);
mapView.getOptions().setSkyBitmap(
UnscaledBitmapLoader.decodeResource(getResources(),
R.drawable.sky_small));
// Map background, visible if no map tiles loaded - optional, default - white
mapView.getOptions().setBackgroundPlaneDrawMode(Options.DRAW_BITMAP);
mapView.getOptions().setBackgroundPlaneBitmap(
UnscaledBitmapLoader.decodeResource(getResources(),
R.drawable.background_plane));
mapView.getOptions().setClearColor(Color.WHITE);
// configure texture caching - optional, suggested
mapView.getOptions().setTextureMemoryCacheSize(40 * 1024 * 1024);
mapView.getOptions().setCompressedMemoryCacheSize(8 * 1024 * 1024);
// define online map persistent caching - optional, suggested. Default - no caching
mapView.getOptions().setPersistentCachePath(this.getDatabasePath("mapcache").getPath());
// set persistent raster cache limit to 100MB
mapView.getOptions().setPersistentCacheSize(100 * 1024 * 1024);
// 4. Start the map - mandatory
mapView.startMapping();
// 5. Add simple marker to map.
// define marker style (image, size, color)
Bitmap pointMarker = UnscaledBitmapLoader.decodeResource(getResources(), R.drawable.olmarker);
MarkerStyle markerStyle = MarkerStyle.builder().setBitmap(pointMarker).setSize(0.5f).setColor(Color.WHITE).build();
// define label what is shown when you click on marker
Label markerLabel = new DefaultLabel("San Francisco", "Here is a marker");
// define location of the marker, it must be converted to base map coordinate system
MapPos markerLocation = mapLayer.getProjection().fromWgs84(-122.416667f, 37.766667f);
// create layer and add object to the layer, finally add layer to the map.
// All overlay layers must be same projection as base layer, so we reuse it
MarkerLayer markerLayer = new MarkerLayer(mapLayer.getProjection());
markerLayer.add(new Marker(markerLocation, markerLabel, markerStyle, null));
mapView.getLayers().addLayer(markerLayer);
// add event listener
MyLocationMapEventListener mapListener = new MyLocationMapEventListener(this, mapView);
mapView.getOptions().setMapListener(mapListener);
// add GPS My Location functionality
MyLocationCircle locationCircle = new MyLocationCircle();
mapListener.setLocationCircle(locationCircle);
initGps(locationCircle);
}
#Override
public Object onRetainNonConfigurationInstance() {
Log.debug("onRetainNonConfigurationInstance");
return this.mapView.getComponents();
}
protected void initGps(final MyLocationCircle locationCircle) {
final Projection proj = mapView.getLayers().getBaseLayer().getProjection();
LocationListener locationListener = new LocationListener()
{
public void onLocationChanged(Location location) {
if (locationCircle != null) {
locationCircle.setLocation(proj, location);
locationCircle.setVisible(true);
mapView.setFocusPoint(mapView.getLayers().getBaseLayer().getProjection().fromWgs84(location.getLongitude(), location.getLatitude()));
}
}
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.debug("GPS onStatusChanged "+provider+" to "+status);
}
public void onProviderEnabled(String provider) {
Log.debug("GPS onProviderEnabled");
}
public void onProviderDisabled(String provider) {
Log.debug("GPS onProviderDisabled");
}
};
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000, 100, locationListener);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, locationListener);
}
ps.This is excactly i wand to do but in android sdk http://openplans.github.io/Leaflet.AnimatedMarker/
You can just use setMapPos() method of Markers in Nutiteq SDK.
In Google Maps v2 for Android, how can I get the visible markers? I know I can use Projection and eliminate points < 0 and points > screen size. But I do not wish to check one by one, it can be too slow if I have a lot of markers. Is there any easy way? Or some off-the-shelf solution? If yes, which one?
Ok, the following is the code the I have used before to determine what the user can see and then only draw the markers that are visible.
I think you might be able to adapt it to your purpose.
get the current rectangle "viewport" of the map (note:must be run on the main thread)
this.mLatLngBounds = this.mMap.getProjection().getVisibleRegion().latLngBounds;
sort the 2 points (top-left and bottom-right) so that we can use min/max logic
double lowLat;
double lowLng;
double highLat;
double highLng;
if (this.mLatLngBounds.northeast.latitude < this.mLatLngBounds.southwest.latitude)
{
lowLat = this.mLatLngBounds.northeast.latitude;
highLat = this.mLatLngBounds.southwest.latitude;
}
else
{
highLat = this.mLatLngBounds.northeast.latitude;
lowLat = this.mLatLngBounds.southwest.latitude;
}
if (this.mLatLngBounds.northeast.longitude < this.mLatLngBounds.southwest.longitude)
{
lowLng = this.mLatLngBounds.northeast.longitude;
highLng = this.mLatLngBounds.southwest.longitude;
}
else
{
highLng = this.mLatLngBounds.northeast.longitude;
lowLng = this.mLatLngBounds.southwest.longitude;
}
then in my case I had this data in a db, so i could use >= and <= to extract only the pins i wanted
You could use the android-map-extension library. Amongst others it offers a List GoogleMap.getDisplayedMarkers() method.
You could add this Extension Function to GoogleMap class if you are using Kotlin
fun GoogleMap.isMarkerVisible(markerPosition: LatLng) =
projection.visibleRegion.latLngBounds.contains(markerPosition)
You just pass the marker position as a parameter of this method and do whatever you want with the result.
If you are using Java you can just declare the function wherever fits better for you.
Hope it helps!
You are looking for either markers:
https://developers.google.com/maps/documentation/android/marker
private GoogleMap mMap;
mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
mMap.addMarker(new MarkerOptions()
.position(new LatLng(0, 0))
.title("Hello world"));
Or direct drawing:
https://developers.google.com/maps/documentation/android/shapes
// Instantiates a new Polyline object and adds points to define a rectangle
PolylineOptions rectOptions = new PolylineOptions()
.add(new LatLng(37.35, -122.0))
.add(new LatLng(37.45, -122.0)) // North of the previous point, but at the same longitude
.add(new LatLng(37.45, -122.2)) // Same latitude, and 30km to the west
.add(new LatLng(37.35, -122.2)) // Same longitude, and 16km to the south
.add(new LatLng(37.35, -122.0)); // Closes the polyline.
// Get back the mutable Polyline
Polyline polyline = myMap.addPolyline(rectOptions);