In my android app I am trying to check, if a Geopoint is close to a Polyline, so I am using the built-in isCloseTo function.
I create the polyline:
Polyline startLine = new Polyline();
startLine.addPoint(new Geopoint(lat1, long1));
startLine.addPoint(new Geopoint(lat2, long2));
startLine.addPoint(new Geopoint(lat3, long3));
startLine.addPoint(new Geopoint(lat4, long4));
startLine.addPoint(new Geopoint(lat5, long5));
After that I add the Polyline to the mapview:
map.getOverlayManager().add(startLine);
After that I create a new Geopoint and a tolerance double to test the closeness:
GeoPoint testPoint = new GeoPoint(lat6, long6);
double testTolerance = 5.0;
Then I test the closeness:
startLine.isCloseTo(testPoint, testTolerance, map);
The above line always returns false no matter what I set the tolerance to (I have tried it with 5.0, 10.0, even 1000.0) and how close the point is (even setting testPoint to the exact same point as one of the startLine points, results in false).
Have I misunderstood how the isCloseTo function is supposed to work? Is there a mistake in the way I am using it?
Related
I have added current location via google map routing with
Routing routing = new Routing.Builder()
.travelMode(Routing.TravelMode.DRIVING)
.key(getResources().getString(R.string.google_maps_api))
.withListener(this)
.waypoints(new LatLng(mLastKnownLocation.getLatitude(), mLastKnownLocation.getLongitude()), site_location)
.alternativeRoutes(false)
.build();
routing.execute();
#Override
public void onRoutingSuccess(ArrayList<Route> route, int shortestRouteIndex) {
if (polylines.size() > 0) {
for (Polyline poly : polylines) {
poly.remove();
}
}
polylines = new ArrayList<>();
//add route(s) to the map.
for (int i = 0; i < route.size(); i++) {
//In case of more than 5 alternative routes
int colorIndex = i % COLORS.length;
PolylineOptions polyOptions = new PolylineOptions();
polyOptions.color(getResources().getColor(COLORS[colorIndex]));
polyOptions.width(10 + i * 13);
polyOptions.addAll(route.get(i).getPoints());
Polyline polyline = googleMap.addPolyline(polyOptions);
polylines.add(polyline);
int distance = route.get(i).getDistanceValue();
if (distance < 1000){
totalKm.setText( distance+" Metres");
}else {
totalKm.setText( (distance/1000) +" km");
}
}
LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(new LatLng(mLastKnownLocation.getLatitude(), mLastKnownLocation.getLongitude()));
builder.include(site_marker.getPosition());
LatLngBounds bounds = builder.build();
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 100);
googleMap.animateCamera(cu);
}
THis displays a driving directions like
But am trying to display the default google map driving icon with zoomed view like
How do i proceed to add such a map while still retaining the polylines to show driving view.
Jinesh Francis totally right in his answer: you should either run the default map Google Maps application through intent or modify the standard MapView (or MapFragment).
TLDR;
If you chose the second way - easiest approach is to use standard classes of Android Google Maps API to create view like in your example (other way is to create MapView-based custom view).
At first read carefully p 3.2.4 Restrictions Against Misusing the Services (d) of Google Maps Platform Terms of Service:
(d) No Re-Creating Google Products or Features. Customer will not use
the Services to create a product or service with features that are
substantially similar to or that re-create the features of another
Google product or service. Customer’s product or service must contain
substantial, independent value and features beyond the Google products
or services. For example, Customer will not: (i) re-distribute the
Google Maps Core Services or pass them off as if they were Customer’s
services; (ii) create a substitute of the Google Maps Core Services,
Google Maps, or Google Maps mobile apps, or their features; (iii) use
the Google Maps Core Services in a listings or directory service or to
create or augment an advertising product; (iv) combine data from the
Directions API, Geolocation API, and Maps SDK for Android to create
real-time navigation functionality substantially similar to the
functionality provided by the Google Maps for Android mobile app.
and if you not violate Terms of Service you can do what you want with that steps/tasks:
1) get user current location;
2) get a route path segment nearest to user current location (because user location rarely exactly on road);
3) get a azimuth (bearing) of this segment;
4) show map with route path and user current position marker with appropriate tilt and rotation according path segment bearing.
Task 1 can be solved like in this answer of Axxiss:
private final LocationListener mLocationListener = new LocationListener() {
#Override
public void onLocationChanged(final Location location) {
//your code here
}
};
Task 2 can be solved via PolyUtil.isLocationOnPath() like in that answer:
private LatLng getMarkerProjectionOnSegment(LatLng carPos, List<LatLng> segment, Projection projection) {
LatLng markerProjection = null;
Point carPosOnScreen = projection.toScreenLocation(carPos);
Point p1 = projection.toScreenLocation(segment.get(0));
Point p2 = projection.toScreenLocation(segment.get(1));
Point carPosOnSegment = new Point();
float denominator = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y);
// p1 and p2 are the same
if (Math.abs(denominator) <= 1E-10) {
markerProjection = segment.get(0);
} else {
float t = (carPosOnScreen.x * (p2.x - p1.x) - (p2.x - p1.x) * p1.x
+ carPosOnScreen.y * (p2.y - p1.y) - (p2.y - p1.y) * p1.y) / denominator;
carPosOnSegment.x = (int) (p1.x + (p2.x - p1.x) * t);
carPosOnSegment.y = (int) (p1.y + (p2.y - p1.y) * t);
markerProjection = projection.fromScreenLocation(carPosOnSegment);
}
return markerProjection;
}
Task 3 can be solved with code like that:
private float getBearing(LatLng begin, LatLng end) {
double dLon = (end.longitude - begin.longitude);
double x = Math.sin(Math.toRadians(dLon)) * Math.cos(Math.toRadians(end.latitude));
double y = Math.cos(Math.toRadians(begin.latitude))*Math.sin(Math.toRadians(end.latitude))
- Math.sin(Math.toRadians(begin.latitude))*Math.cos(Math.toRadians(end.latitude)) * Math.cos(Math.toRadians(dLon));
double bearing = Math.toDegrees((Math.atan2(x, y)));
return (float) bearing;
}
where begin and end is begin and end of current route path segment.
Task 4 can be solved with code like that:
as marker you can use vector drawable of north oriented arrow like that:
ic_up_arrow_circle.xml (also you can adjust transparency and colors):
<vector android:height="24dp" android:viewportHeight="93.934"
android:viewportWidth="93.934"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="#8fFF0000"
android:pathData="m0,46.9666c0,25.939 21.028,46.967 46.967,46.967c25.939,-0 46.967,-21.028 46.967,-46.967c0,-25.939 -21.027,-46.967 -46.967,-46.967c-25.939,-0 -46.967,21.028 -46.967,46.967zM78.262,67.4396l-31.295,-16.845l-31.295,16.845l31.295,-51.614l31.295,51.614z"
/>
<path
android:fillColor="#FFFFFF"
android:pathData="M78.262,67.4396l-31.295,-16.845l-31.295,16.845l31.295,-51.614l31.295,51.614z"
/>
</vector>
and you can place it on map with code like that:
public Marker addDirectionMarker(LatLng latLng, float angle) {
Drawable circleDrawable = ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_up_arrow_in_circle);
BitmapDescriptor markerIcon = getMarkerIconFromDrawable(circleDrawable, 150, 150);
return mGoogleMap.addMarker(new MarkerOptions()
.position(latLng)
.anchor(0.5f, 0.5f)
.rotation(angle)
.flat(true)
.icon(markerIcon)
);
}
where 150 is marker size in pixels. NB! You need flat marker for its rotation and tilt with map and 0.5f for move marker anchor exactly on its center point.
then you can show all of this on map:
...
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(userPosition)
.tilt(tilt)
.zoom(zoom)
.bearing(bearing)
.build();
mGoogleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
...
But if you do only that marker of user current position appeared in the center of screen (because GoogleMap.moveCamera() sets the center exactly at .target()). So, to avoid it you need to shift down the map slightly - in that case user location marker should be appeared at the bottom of screen. For map center shift you need get current map center screen coordinates, then change y coordinate and get new screen center. Something like that:
...
LatLng mapCenter = mGoogleMap.getCameraPosition().target;
Projection projection = mGoogleMap.getProjection();
Point centerPoint = projection.toScreenLocation(mapCenter);
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int displayHeight = displayMetrics.heightPixels;
centerPoint.y = centerPoint.y - (int) (displayHeight / 4.5); // move center down for approx 22%
LatLng newCenterPoint = projection.fromScreenLocation(centerPoint);
mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(newCenterPoint, zoom));
...
And with all of this stuff, for your route (with zoom = 15 and tilt = 50) you should get something like that:
As you can see, the route path is not exactly on road, so you need to get route path points more precisely than Directions API response. You can get that e.g. via Google Maps Roads API part Snap to Road which
returns the best-fit road geometry for a given set of GPS coordinates.
This service takes up to 100 GPS points collected along a route, and
returns a similar set of data with the points snapped to the most
likely roads the vehicle was traveling along.
like in that answer. If your route path has more than points you need to split in into 100-points portions and process them separately (also Snap to Road API has 2500 request per day per user (IP) and 10 requests per sec. restriction).
And as Jaswant Singh answered you:
need to set a custom marker (with icon same as that blue arrow) on
your current location and move it to the new location every time there
is onLocationChanged() callback is called (Also animate the camera to
that new location).
Also, you need to select zoom and tilt properties according, for example, current user speed: when user drives faster tilt -> 0. And so on. It's not a simple task.
In addition to Jinesh’s answer,
If you still want to add that marker for development, you need to set a custom marker (with icon same as that blue arrow) on your current location and move it to the new location every time there is onLocationChanged() callback is called (Also animate the camera to that new location).
And tilt the map a little to get the exact look of the google maps navigation view, though you won’t get to use all the functionalities but it’s worth to give it a try.
I have a MapView called mMapView. I want to add a point graphic to the map. Here is what I did:
Point p1 = new Point(66.9969, 6.65428); //I took this coordinate from my GPS device for demonstration
Point p2 = (Point) GeometryEngine.project(
p1,
SpatialReference.create(4326),
mMapView.getSpatialReference());
Graphic graphic = new Graphic(
p2,
new SimpleMarkerSymbol(Color.RED, 10, SimpleMarkerSymbol.STYLE.DIAMOND));
mGraphicsLayer.addGraphic(graphic);//mGraphicsLayer is my GraphicsLayer on my MapView
When I ran my program the marker was not there on my MapView. But when I replaced mMapView.getSpatialReference() with SpatialReference.create(32637) the graphic appeared in exactly the right position I wanted. Why this is happening? I checked my MapView spatial reference id and latestid and and it is 102100 and 3857 respectively.
My guess is that the map doesn't yet have its spatial reference when you call project. It's hard to say because you didn't say where in the app your code snippet is. But if you put it in your Activity's onCreate method, the map's spatial reference is probably null still. Try an OnStatusChangedListener, like this:
mMapView.setOnStatusChangedListener(new OnStatusChangedListener() {
public void onStatusChanged(Object source, STATUS status) {
if (STATUS.INITIALIZED.equals(status)) {
mGraphicsLayer = new GraphicsLayer();
mMapView.addLayer(mGraphicsLayer);
Point p1 = new Point(66.9969, 6.65428);
Point p2 = (Point) GeometryEngine.project(
p1,
SpatialReference.create(4326),
mMapView.getSpatialReference());
Graphic graphic = new Graphic(
p2,
new SimpleMarkerSymbol(Color.RED, 10, SimpleMarkerSymbol.STYLE.DIAMOND));
mGraphicsLayer.addGraphic(graphic);
}
}
});
Another possibility is that your coordinates are not in latitude and longitude. As written, with lon = 66.9969 and lat = 6.65428, it should be in the ocean near India. If you meant lon = 6.65428 and lat = 66.9969, it would be in the ocean near Norway. If you didn't mean either of those locations, then your coordinates are either incorrect or not in latitude and longitude.
I have 2 LatLng, usually a few kilometers apart, and I want to zoom map such that both points are visible. My code below sometimes works, sometimes not (map is zoomed to level 3 then). I think the reason is that the loop is too fast for the map to make the necessary adjustments in time.
Is there a way to wait for the map to update before making the test again? To note that process runs on UI thread
double x1=from.latitude;
double x2= to.latitude;
double x1half=(x1+x2)/2;
x1=from.longitude;
x2= to.longitude;
double x2half=(x1+x2)/2;
LatLng pp=new LatLng(x1half, x2half);
map.moveCamera(CameraUpdateFactory.newLatLng(pp));//3.0
//now zoom map so that both points are in
double zoom=21;//V3.3
while(zoom>2){//
map.animateCamera( CameraUpdateFactory.zoomTo( (float) zoom ) );
if(isCurrentLocationVisible(from,map)&&isCurrentLocationVisible(to,map)) return;
/now we know map is still not visible. give it a bit more room
zoom--;
}
I'd advice you to use the newLatLngBounds() instead..You can create your bounds from your two geo points with the LatLngBounds.builder().
To the second part of your question..You could do somethink like map.post(new Runnable..) which could solve the concurrency issue.
You can use LatLngBounds like the following:
LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(locationOne);
builder.include(locationTwo);
LatLngBounds bounds = builder.build();
int padding = 0; // offset from edges of the map in pixels
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds,screen width, screen height, padding);
map.moveCamera(cu);
I utilise the following code to put a route overlay onto an OSM droid map, using code gotten from the following tutorial (http://code.google.com/p/osmbonuspack/wiki/Tutorial_1) but slightly tweaked into a custom method, rather than being used in the OnCrerate method.
Now this does route and produces a green overlay on the map. However, there is a problem exhibited from the For Loop onwards. This is because road.mNodes is always size zero indicating that no instructions are coming down.
Incidently I also inspected RoadNodes and RoadItems and both were also size zero. This means the bubbles (ExtendedOVerlayItems) are never displayed on the route.
Any advice would be greatly appreciated.
//======================================================================================================
/**
* Add a route overlay between two geopoints with Bubble overlays on the route points.
*
* #param startPoint Route start.
* #param endPoint Route end.
*//
//======================================================================================================
public void addRouteOverlay(GeoPoint startPoint, GeoPoint endPoint)
{
//1 Routing via road manager
RoadManager roadManager = new OSRMRoadManager();
roadManager.addRequestOption("routeType=bicycle");
//Then, retreive the road between your start and end point:
ArrayList<GeoPoint> waypoints = new ArrayList<GeoPoint>();
waypoints.add(startPoint);
waypoints.add(endPoint); //end point
Road road = roadManager.getRoad(waypoints);
// then, build an overlay with the route shape:
PathOverlay roadOverlay = RoadManager.buildRoadOverlay(road, map.getContext());
roadOverlay.setColor(Color.GREEN);
//Add Route Overlays into map
map.getOverlays().add(roadOverlay);
map.invalidate();//refesh map
Drawable marker = ctx.getResources().getDrawable(R.drawable.map_marker_blue);
final ArrayList<ExtendedOverlayItem> roadItems =
new ArrayList<ExtendedOverlayItem>();
ItemizedOverlayWithBubble<ExtendedOverlayItem> roadNodes =
new ItemizedOverlayWithBubble<ExtendedOverlayItem>(ctx, roadItems, map);
for (int i=0; i<road.mNodes.size(); i++)
{
RoadNode node = road.mNodes.get(i);
ExtendedOverlayItem nodeMarker = new ExtendedOverlayItem("Step "+i, "", node.mLocation, ctx);
nodeMarker.setMarkerHotspot(OverlayItem.HotspotPlace.CENTER);
nodeMarker.setMarker(marker);
roadNodes.addItem(nodeMarker);
nodeMarker.setDescription(node.mInstructions);
nodeMarker.setSubDescription(road.getLengthDurationText(node.mLength, node.mDuration));
Drawable icon = ctx.getResources().getDrawable(R.drawable.ic_continue);
nodeMarker.setImage(icon);
}//end for
map.getOverlays().add(roadNodes);
}//===================================================================================================
I had this problem today and managed to solve it. The problem lies with an old version of the bonus pack. I updated to version osmbonuspack_v4.1.jar from osmbonuspack_v3.8.jar and it solved the problem. I also used the MapQuestRoadManager() option as opposed to the OSRMRoadManager().However, its worth bearing in mind that when doing this a few of the super type methods changed in the bonus pack - such as the onOpen() method on ExtendedOverlayItem required its parameter to be cast after calling.
final RoadManager manager= new MapQuestRoadManager();
manager.addRequestOption("routeType=fastest");
I have two geo points that vary intermittently, and want the MapView to resize and translate to make sure both points are always visible. I can easily re-centre the map on the point mid-way between the two, but how do I set the zoom level to ensure my two points are visible?
Check out his answer in a different post:
Google Map V2 how to set ZoomToSpan?
It solves it without much hassle. Note that you can only use that function AFTER the map has been loaded at least once. If not use function with specified map size on screen
LatLngBounds bounds = new LatLngBounds.Builder()
.include(new LatLng(CURRENTSTOP.latitude, CURRENTSTOP.longitude))
.include(new LatLng(MYPOSITION.latitude, MYPOSITION.longitude)).build();
Point displaySize = new Point();
getWindowManager().getDefaultDisplay().getSize(displaySize);
map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, displaySize.x, 250, 30));
Works like a charm and very dynamic!
Try this and see:
double latitudeSpan = Math.round(Math.abs(firstLat -
secLat));
double longitudeSpan = Math.round(Math.abs(firstLong -
secLong));
double currentLatitudeSpan = (double)mapView.getLatitudeSpan();
double currentLongitudeSpan = (double)mapView.getLongitudeSpan();
double ratio = currentLongitudeSpan/currentLatitudeSpan;
if(longitudeSpan < (double)(latitudeSpan+2E7) * ratio){
longitudeSpan = ((double)(latitudeSpan+2E7) * ratio);
}
mapController.zoomToSpan((int)(latitudeSpan*2), (int)(longitudeSpan*2));
mapView.invalidate();
in the new Maps API (v2) you can do it this way:
LatLng southwest = new LatLng(Math.min(laglng1.latitude, laglng2.latitude), Math.min(laglng1.longitude, laglng2.longitude));
LatLng northeast = new LatLng(Math.max(laglng1.latitude, laglng2.latitude), Math.max(laglng1.longitude, laglng2.longitude));
googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(new LatLngBounds(southwest, northeast),500,500, 0));