i'm getting a Point object throw my MapView Projectiion object.
I have a list of Overlays and i want to check if each Overlay is inside my current screen (which is of curse depends on the zoom level).
I couldn't find a way to check if a given point is inside the screen.
What i did found out is that when i'm Logging the Points from the Projection, the points that're not in the screen have negative value..
is it true to say that if a Point has a negative value it is outside my screen ?
GeoPoint yourPoint = null;// w/e point u wanna see is on the screen
Point newPoint = null; // x/y point
mapView.getProjection().toPixels(yourPoint, newPoint); //convert to xy Point
int height = context.getResources().getDisplayMetrics().heightPixels;
int width = context.getResources().getDisplayMetrics().widthPixels;
Rect screen = new Rect(0,0,width,height); //rect that represents your Screen
if(screen.contains(newPoint.x, newPoint.y){
// your point is on the screen
}
Related
I have an imageview with a layout on top of it which has buttons added at specific x and y coordinates. How do i make sure that everything lines up the same on different screen sizes so the buttons are always in the same position relative to the image regardless of screen, also how to maintain all the positioning if the orientation is horizontal.
I actually built a class specifically or this. You can find it here:
https://gist.github.com/nathan-fiscaletti/190a660620f6130e6a15962f59b21f22
The way you would use it is as follows:
Replace the ImageView in your layout with SizeAwareImageView.
<com.my.app.SizeAwareImageView
android:id="#+id/sizeAwareImageView"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/viewHeader"
app:srcCompat="#drawable/somePicture" />
You can then determine the placement of those objects and use use the following to figure out where to place them.
PointF location = sizeAwareImageView.getScaledCoordinateWithPadding(new PointF(10f, 10f));
// place the object at the location
This will keep your objects at the same location based on the drawable applied to the image view. No matter what screen it is on, the location variable will always point to the same placement on the image.
I use this to store the location of map markers in a database, they can then be used to move a pin around a static map drawable regardless of what device they are displayed on.
/**
* Update the map markers location and display it.
*
* #param mapPinLocation The new location.
*/
private void updateMarker(final PointF mapPinLocation) {
markerContainer.removeView(mapMarker);
PointF location = mapImageView
.getScaledCoordinateWithPadding(mapPinLocation, 100f);
if (getContext() != null) {
int markerSize = (int) DisplayUtil.dpToPx(25, getContext());
RelativeLayout.LayoutParams lp
= new RelativeLayout.LayoutParams(markerSize, markerSize);
// Android uses top left of the view for it's coords
// so we need to center it at it's X/Y coords.
if (location != null) {
lp.topMargin = (int) location.y - (markerSize / 2);
lp.leftMargin = (int) location.x - (markerSize / 2);
mapMarker.setLayoutParams(lp);
markerContainer.addView(mapMarker);
}
}
}
Hopefully this is actually what you were looking for.
Background
Suppose I have a Google maps view, and another view on top of it, that covers a part of it, hiding some content of the map.
The problem
I need to make the "camera" of the map, to focus and have a marker on a coordinate , yet let it all be in the middle of the visible part of the map.
Something like this:
The original code was focusing on (about) the center of the entire screen, making the marker almost invisible (as the bottom view covers it).
Thing is, I can't find the proper way to set the correct value to the Y coordinate of the map itself (meaning latitude).
What I've tried
I tried, given the height of the bottom view, and the coordinate that I've put the marker on, to calculate the delta (yet of course not change the marker itself) :
final float neededZoom = 6.5f;
int bottomViewHeight = bottomView.getHeight();
LatLng posToFocusOn = ...;
final Point point = mMap.getProjection().toScreenLocation(posToFocusOn);
final float curZoom = mMap.getCameraPosition().zoom;
point.y += bottomViewHeight * curZoom / neededZoom;
posToFocusOn = mMap.getProjection().fromScreenLocation(point);
final CameraUpdate cameraPosition = CameraUpdateFactory.newCameraPosition(new Builder().target(posToFocusOn).zoom(neededZoom).build());
Sadly, this focuses way above the marker.
The question
What's wrong with what I wrote? What can I do to fix it?
ok, I've found a workaround, which I think works on all devices (tested on 3, each with a different screen resolution and size) :
I've measured how many pixels (and then converted to DP) a change of one degree has on the marker itself.
From this, I measured the height of each view, and calculated the delta needed to move the camera.
In my case, it's this way (supposing the zoom is 6.5f) :
//measured as 223 pixels on Nexus 5, which has xxhdpi, so divide by 3
final float oneDegreeInPixels = convertDpToPixels( 223.0f / 3.0f);
final float mapViewCenter = mapViewHeight / 2.0f;
final float bottomViewHeight = ...;
final float posToFocusInPixelsFromTop = (mapViewHeight - bottomViewHeight) / 2.0f ;// can optionally add the height of the view on the top area
final float deltaLatDegreesToMove = (mapViewCenter - posToFocusInPixelsFromTop) / oneDegreeInPixels;
LatLng posToFocusOn = new LatLng(latitude - deltaLatDegreesToMove, longitude);
final CameraUpdate cameraPosition = CameraUpdateFactory.newCameraPosition(new Builder().target(posToFocusOn).zoom(neededZoom).build());
And it worked.
I wonder if it can be adjusted to support any value of zoom.
Your code is almost right, but it goes above the marker because you are taking into account bottomViewHeight when computing point.y instead of bottomViewHeight/2 (When your view's size is 200px, you only need to displace the map 100px to recenter it):
point.y += (bottomViewHeight / 2) * curZoom / neededZoom;
Update:
This is a more general approach taht takes into account the map bounds and calculates a new map bounds according to the height of your bottomView. This is zoom independent.
public void recenter() {
LatLngBounds mapBounds = mMap.getProjection().getVisibleRegion().latLngBounds;
Point nothEastPoint = mMap.getProjection().toScreenLocation(mapBounds.northeast);
Point souhWestPoint = mMap.getProjection().toScreenLocation(mapBounds.southwest);
Point newNorthEast = new Point(nothEastPoint.x, nothEastPoint.y + bottomView.getHeight() / 2);
Point newSouhWestPoint = new Point(souhWestPoint.x, souhWestPoint.y + bottomView.getHeight() / 2);
LatLngBounds newBounds = LatLngBounds.builder()
.include(mMap.getProjection().fromScreenLocation(newNorthEast))
.include(mMap.getProjection().fromScreenLocation(newSouhWestPoint))
.build();
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(newBounds, 0));
}
Note that each time you call recenter() the map will move.
I created an android app to draw free shapes over google map v2. The idea of the app is that I combines two apps, one is to draw free shapes and the other is normal google map v2 app.
This link is my last question about this app and it contains the code
The app works well with me but now I have a new problem. My problem is that when I draw a line over a specific location on the map and convert control to map and drag it, I found that the line keep in its place in the view and the map moves under the line and this leads the line to be in another location not the location that I want.
Is there are any way to make the line to be steady in the location I draw in it and when I drag the map the line dragged with its location?
Hope anyone got my mean.
For example if you are drawing line on your mapview using canvas then you need to get x,y points of start and end point.
Then by following code you can change that x,y points into latitude and longitude.
public boolean onTouchEvent(MotionEvent event)
{
int X = (int)event.getX();
int Y = (int)event.getY();
GeoPoint geoPoint = mapView.getProjection().fromPixels(X, Y);
}
Then resgister listener on your mapvierw like this.
map.setOnCameraChangeListener(new OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition arg0) {
// Move camera.
Here remove your view from screen and then get lat long of visible region by passing x,y points of 4 regions in `mapView.getProjection().fromPixels(x,y)` and then check if latitude and longitude of your line within range if yes then drawline by following code.
float pisteX;
float pisteY;
Projection projection = this.mapView.getProjection();
Point pt = new Point();
GeoPoint gie = new GeoPoint(latitude,longitude);
Rect rec = mapView.getScreenRect(new Rect());
projection.toPixels(gie, pt);
pisteX = pt.x-rec.left; // car X screen coord
pisteY = pt.y-rec.top; // car Y screen coord
Now draw line between this two (x,y) points.
}
});
Hope I can make you clear and you can understand what I want to say.
I am trying to solve a problem with drawing a path from huge (100k+) set of GeoPoints to a MapView on Android.
Firstly I would like to say, I searched through StackOverflow a lot and haven't found an answer.The bottleneck of my code is not actually drawing into canvas, but Projection.toPixels(GeoPoint, Point) or Rect.contains(point.x, point.y) method..I am skipping points not visible on screen and also displaying only every nth point according to current zoom-level. When the map is zoomed-in I want to display as accurate path as possible so I skipping zero (or nearly to zero) points, so that when finding visible points I need to call the projection method for every single point in the collection. And that is what really takes a lot of time (not seconds, but map panning is not fluid and I am not testing it on HTC Wildfire:)). I tried caching calculated points, but since points be recalculated after every map pan/zoom it haven't helped
at all.
I thought about usage of some kind of prune and search algorithm instead of iterate the array, but I figured out the input data is not sorted (I can't throw away any branch stacked between two invisible points). That could I possible solve with simple sort at the beginning, but I am still not sure even the logarithmic count of getProjection() and Rect.contains(point.x, point.y) calls instead of linear would solve the performance problem.
Bellow is my current code. Please help me if you know how to make this better. Thanks a lot!
public void drawPath(MapView mv, Canvas canvas) {
displayed = false;
tmpPath.reset();
int zoomLevel = mapView.getZoomLevel();
int skippedPoints = (int) Math.pow(2, (Math.max((19 - zoomLevel), 0)));
int mPointsSize = mPoints.size();
int mPointsLastIndex = mPointsSize - 1;
int stop = mPointsLastIndex - skippedPoints;
mapView.getDrawingRect(currentMapBoundsRect);
Projection projection = mv.getProjection();
for (int i = 0; i < mPointsSize; i += skippedPoints) {
if (i > stop) {
break;
}
//HERE IS THE PROBLEM I THINK - THIS METHOD AND THE IF CONDITION BELOW
projection.toPixels(mPoints.get(i), point);
if (currentMapBoundsRect.contains(point.x, point.y)) {
if (!displayed) {
Point tmpPoint = new Point();
projection.toPixels(mPoints.get(Math.max(i - 1, 0)),
tmpPoint);
tmpPath.moveTo(tmpPoint.x, tmpPoint.y);
tmpPath.lineTo(point.x, point.y);
displayed = true;
} else {
tmpPath.lineTo(point.x, point.y);
}
} else if (displayed) {
tmpPath.lineTo(point.x, point.y);
displayed = false;
}
}
canvas.drawPath(tmpPath, this.pathPaint);
}
So I figured out how to make it all much faster!
I will post it here, somebody could possibly found it useful in the future.
It has emerged that usage of projection.toPixels() can really harm application performance. So I figured out that way better than take every single GeoPoint, convert it to Point and then check if it is contained in map viewport is, when I count actuall viewport radius of the map as following:
mapView.getGlobalVisibleRect(currentMapBoundsRect);
GeoPoint point1 = projection.fromPixels(currentMapBoundsRect.centerX(), currentMapBoundsRect.centerY());
GeoPoint point2 = projection.fromPixels(currentMapBoundsRect.left, currentMapBoundsRect.top);
float[] results2 = new float[3];
Location.distanceBetween(point1.getLatitudeE6()/1E6, point1.getLongitudeE6()/1E6, point2.getLatitudeE6()/1E6, point2.getLongitudeE6()/1E6, results2);
The radius is in results2[0]..
Then I can take every single GeoPoint and count the distance between it and the center of the map mapView.getMapCenter(). Then I can compare the radius with computed distance and decide whether ot not diplay the point.
So that's it, hope It will be helpful.
I'm trying to use something like a compass, passing it longitude/latitude values to let it point to a specific location, my code can draw an arrow while moving the phone (using GPS) to determine the location.
I want to use an image instead of the drawing thing
public void draw(Canvas canvas) {
double angle = calculateAngle(currentLongitude, currentLatitude, targetLongitude, targetLatitude);
//Correction;
angle-=90;
//Correction for azimuth
angle-=azimuth;
if((getContext() instanceof Activity) && ((Activity)getContext()).getWindowManager().getDefaultDisplay().getOrientation()==Configuration.ORIENTATION_PORTRAIT)angle-=90;
while(angle<0)angle=angle+360;
Rect rect = canvas.getClipBounds();
int height = rect.bottom-rect.top;
int width = rect.right-rect.left;
int left = rect.left;
int top = rect.top;
}
You need to do two things:
Rotate the pointer image
Draw the resulting bitmap to screen.
A few tricks, it might be faster to pre-rotate the image when your program starts up rather than rotating it every time you draw; however it would also take more memory.