I'd like to both zoom and scroll a GoogleMap object at the same time. Unfortunately, it seems like if I just make two moveCamera calls (one after another), only the second call appears to take effect.
An alternative approach would be to pass a CameraPosition, but unfortunately it looks like the CameraPosition constructor does not take an argument that deals with an amount to scroll (which is invariant to the zoom level), but only an argument as to what lat/lon to go to.
Is there some clever way to combine/concatenate CameraUpdate objects so I can just issue one moveCamera command that does both a pan and a zoom?
I assume something like this is possible because you can do it when you're touching the map. You put down two fingers, and you can zoom in/out by spreading your fingers and pan by moving them both simultaneously.
It looks like your best option will be to modify the CameraPosition object returned when you call GoogleMap.getCameraPosition(). However, you won't be able to just increment that latitude and longitude of the CameraPosition object by the number of pixels you scrolled. You'll have to get the CameraPosition's LatLng coordinates, convert that to a Point object using the Projection class, modify the Point, and then convert it back into a LatLng object.
For example:
GoogleMap map; // This is your GoogleMap object
int dx; // X distance scrolled
int dy; // Y distance scrolled
float dz; // Change in zoom level
float originalZoom;
CameraPosition position = map.getCameraPosition();
Project projection = map.getProjection();
LatLng mapTarget = position.target;
originalZoom = position.zoom;
Point mapPoint = projection.toScreenLocation(mapTarget);
mapPoint.x += dx;
mapPoint.y += dy;
mapTarget = projection.fromScreenLocation(mapPoint);
CameraPosition newPosition = new CameraPosition(position)
.target(mapTarget)
.zoom(originalZoom+dz)
.build();
map.moveCamera(CameraUpdateFactory.newCameraPosition(newPosition));
There is this method newLatLngZoom (LatLng latLng, float zoom), which will zoom to the point located at latLng. For your case of zooming to a point P that is off the center, it is able to do it with some math involves.
To reverse thinking about the problem, we know the screen coordinate of the map center C after the zoom so that we know the LatLng of C. This LatLng needs to be passed in to the newLatLngZoom method so that it will center to the point and P is the same as before (which is as we defined the problem, zoom to P). So we need to find the screen coordinate of C before the zoom.
Let's assume the zoom value before zoom is z1, and after zoom is z1 + dz. Google map API states that at zoom level N, the width of the world is approximately 256*2^N dp. So the width of the world before is 256*2^z1, and after is 256*2^(z1 + dz).
We also know that the ratio of the distance of P to C to the whole world doesn't change before and after the zoom. The coordinate of C after the zoom is just center of your map view, which is known. Let's assume coordinate of C before the zoom as (Cx, Cy), then we can have our equation as (Px - Cx) / 256*2^z1 = (Px - C'x) / 256*2^(z1 + dz), and (Py - Cy) / 256*2^z1 = (Py - C'y) / 256*2^(z1 + dz). I ignore the aspect ratio to transfer width to height, because it will be cancelled out as you solve the equation.
Solve the equation for Cx and Cy, and use that value as your parameters in newLatLngZoom. If you want to add panning and you know the dx dy value that needs to be panned, then just calculate the dx' dy' in the map after the zoom and add that to C.
Related
I am working on a 2D java game engine using AWT canvas as a basis. Part of this game engine is that it needs to have hitboxes with collision. Not just the built in rectangles (tried that system already) but I need my own Hitbox class because I need more functionality. So I made one, supports circular and 4-sided polygon shaped hitboxes. The way the hitbox class is setup is that it uses four coordinate points to serve as the 4 corner vertices that connect to form a rectangle. Lines are draw connecting the points and these are the lines that are used to detect intersections with other hitboxes. But I now have a problem: rotation.
There are two possibilities for a box hitbox, it can just be four coordinate points, or it can be 4 coordinate points attached to a gameobject. The difference is that the former is just 4 coordinates based on 0,0 as the ordin while the attached to gameobject stores offsets in the coordinates rather than raw location data, so (-100,-100) for example represents the location of the host gameobject but 100 pixels to the left, and 100 pixels up.
Online I found a formula for rotating points about the origin. Since Gameobject based hitboxes were centered around a particular point, I figured that would be the best option to try it on. This code runs each tick to update a player character's hitbox
//creates a rectangle hitbox around this gameobject
int width = getWidth();
int height = getHeight();
Coordinate[] verts = new Coordinate[4]; //corners of hitbox. topLeft, topRight, bottomLeft, bottomRight
verts[0] = new Coordinate(-width / 2, -height / 2);
verts[1] = new Coordinate(width / 2, -height / 2);
verts[2] = new Coordinate(-width / 2, height / 2);
verts[3] = new Coordinate(width / 2, height / 2);
//now go through each coordinate and adjust it for rotation
for(Coordinate c : verts){
if(!name.startsWith("Player"))return; //this is here so only the player character is tested
double theta = Math.toRadians(rotation);
c.x = (int)(c.x*Math.cos(theta)-c.y*Math.sin(theta));
c.y = (int)(c.x*Math.sin(theta)+c.y*Math.cos(theta));
}
getHitbox().vertices = verts;
I appologize for poor video quality but this is what the results of the above are: https://www.youtube.com/watch?v=dF5k-Yb4hvE
All related classes are found here: https://github.com/joey101937/2DTemplate/tree/master/src/Framework
edit: The desired effect is for the box outline to follow the character in a circle while maintaining aspect ratio as seen here: https://www.youtube.com/watch?v=HlvXQrfazhA . The current system uses the code above, the effect of which can be seen above in the previous video link. How should I modify the four 2D coordinates to maintain relative aspect ratio throughout a rotation about a point?
current rotation system is the following:
x = x*Cos(theta) - y *Sin(theta)
y = x*Sin(theta) + y *Cos(theta)
where theta is degree of rotation in raidians
You made classic mistake:
c.x = (int)(c.x*Math.cos(theta)-c.y*Math.sin(theta));
c.y = (int)(c.x*Math.sin(theta)+c.y*Math.cos(theta));
In the second line you use modified value of c.x. Just remember tempx = c.x
before calculations and use it.
tempx = c.x;
c.x = (int)(tempx*Math.cos(theta)-c.y*Math.sin(theta));
c.y = (int)(tempx*Math.sin(theta)+c.y*Math.cos(theta));
Another issue: rounding coordinates after each rotation causes distortions and shrinking after some rotations. It would be wise to store coordinates in floats and round them only for output, or remember starting values and apply rotation by accumulated angle to them.
I have set values of minimum longitude and latitude of a specific static map image. That map image is a cut of some country.
/**
* Maximum longitude value of the map
*/
private float mapLongitudeMax;
/**
* Minimum longitude value of the map
*/
private float mapLongitudeMin;
/**
* Maximum latitude value of the map
*/
private float mapLatitudeMax;
/**
* Minimum latitude value of the map
*/
private float mapLatitudeMin;
And I have a BufferedImage called mapImage.
I have a method that I wrote with a friend that receives longitude and latitude and gives you an X and a Y position approximately on the map so you can draw something on the map.
Now if I want to move my mouse around the map, I want it to show longitude/latitude of my mouse position, that means I need to create a method which converts X and Y of the mouse position to longitude and latitude, which should do the opposite of my other method.
This is my method to convert globe coordinates to image X and Y:
protected Location getCoordinatesByGlobe(float latitude, float longitude) {
/**
* Work out minimum and maximums, clamp inside map bounds
*/
latitude = Math.max(mapLatitudeMin, Math.min(mapLatitudeMax, latitude));
longitude = Math.max(mapLongitudeMin, Math.min(mapLongitudeMax, longitude));
/**
* We need the distance from 0 or minimum long/lat
*/
float adjLon = longitude - mapLongitudeMin;
float adjLat = latitude - mapLatitudeMin;
float mapLongWidth = mapLongitudeMax - mapLongitudeMin;
float mapLatHeight = mapLatitudeMax - mapLatitudeMin;
float mapWidth = mapImage.getWidth();
float mapHeight = mapImage.getHeight();
float longPixelRatio = mapWidth / mapLongWidth;
float latPixelRatio = mapHeight / mapLatHeight;
int x = Math.round(adjLon * longPixelRatio) - 3;// these are offsets for the target icon that shows.. eedit laterrr #oz
int y = Math.round(adjLat * latPixelRatio) + 3; //
// turn it up
y = (int) (mapHeight - y);
return new Location(x, y);
}
Now I tried thinking, the first thought that came into my head is just doing the same in reverse... so I started doing it and I ran into problems like, I can't get the value of adjLon or adjLat without having the longitude or latitude, so this can't be simply done by reversing it. I am all new to coordinates systems so it's all a bit confusing for me but I am starting to catch it up.
Any tips for me?
EDIT (Its not possible?)
According to this answer, you can't really get real results because the earth is not flat, it can't really be converted to a flat map with longitude and latitude without implementing a real mathematical algorithm to make it work with the changes.
There are few reasons in my code why the answer can not be exact:
Because of the reason above
Because my X,Y values are integers and not floats.
So my question now, if it is really impossible with my method?
Sadly, there's not an easy answer to this. While you can write the projection routines yourself, the easiest thing to do is probably to get a GIS library, but since I ended up doing this in C# and not Java, I don't know what's available.
The biggest piece of information you need is exactly which projection your map image uses. The Mercator Projection is quite popular, but it's not the only one. You also need to make sure that your chosen projection works for the range of latitudes and longitudes you want. The Mercator projection kind of breaks if you start going above +-70 N, so if you're doing a lot of positions at the poles that might not be the projection for you.
From what i read in your code, your image is in longitude/latitude coordinates, and you draw it on a canvas to be display on screen. Then you add your listener on this image is that correct ?
if this is correct the response is trival, as you can retrieve the X/Y position in your image via MouseListener method on Canvas and transform it base on the position of the mouse inside the canvas (methode getX/getY from mouseEvent) and the current dimension of the canvas and then translate this position within the bound of longitude/latitude.
longitude = minLongitude + (MouseEvent.getX/Canvas.Width)*maxLongitude
lalitude = minLaltitude + (MouseEvent.getY/Canvas.Height)*maxLatitude
If not then you will have to know as #ginkner say the projection technique use to pass from long/lat to X/Y and take the inverse transformation knowing that you will lost some information.
There is a difference between geography coordinate and geometry coordinate it is quite like the 3D earch surface and a canvas to draw on. The web Mercator projection or other popular projection coordinate systems are used for an abstraction of visualization of the earth surface. So that pixel shift in different location would result in different distances.
If you are looking for a some basic GIS java library to handle this type of problem, Geotools in Java could be one option. GeoTools
I have two layouts. One (A) has a ScaleGestureDetector active on it's contents to handle a PinchZoom functionality while the other (B) has dynamically added ImageViews known as Markers. B sits perfectly on top of A with mirrored alignments and sizes.
When a user zooms in on A, I want the Markers on B to translate in a manner that they remain fixed on the points they were on relative to A. For example, if a user places a Marker on a point of interest (POI) on A, and zooms into a different point, the Marker should remain pinned on the POI.
Currently I'm using the below code for every ScaleGestureDetector instance/action/run:
float[] values = new float[9];
trans.getValues(values);
float transx = values[Matrix.MTRANS_X];
float transy = values[Matrix.MTRANS_Y];
float scalex = values[Matrix.MSCALE_X];
float scaley = values[Matrix.MSCALE_Y];
float scale = (float) Math.sqrt(scalex * scalex + scaley * scaley);
focusX = transx - focusX;
focusY = transy - focusY;
transx = focusX;
transy = focusY;
float markerX = marker.getX();
float markerY = marker.getY();
if(markerX > startPoint.x) {
marker.setTranslationX((-transx/scale) + (-transx%scale));
}else if(markerX < startPoint.x) {
marker.setTranslationX((transx/scale) + (transx%scale));
}
if(markerY > startPoint.y) {
marker.setTranslationY((-transy/scale) + (-transy%scale));
}else if(markerY < startPoint.y) {
marker.setTranslationY((transy/scale) + (transy%scale));
}
Where trans is the Matrix used to perform postScales on A's contents and focusX and focusY are merely used to prevent cumulative build-up of the translation values. startPoint is the point of first contact, defined in the MotionEvent.ACTION_DOWN case.
My problem is that the translation becomes progressively "unhinged" when the users zooms in further away from the markers. Larger zoom gestures also cause the Markers to float away from their designated points. For clarity, they get closer to the zoom point and move away from the POI.
I suspect the translation amount at the bottom of the code segment is missing something, likely relative to the size of the layouts.
If I were you, I would take the following approach:
Find the markers position at the start of the movement.
Find the math to correctly identify where the marker should be at any point along the movement.
Use the setX and setY to set it directly to that point.
That will avoid any floating point uncertainties that might arise.
I've got a camera set up, and I can move with WASD and rotate the view with the mouse. But now comes the problem: I want to add physics to the camera/player, so that it "interacts" with my other jBullet objects. How do I do that? I thought about creating a RigidBody for the camera and storing the position there, so that jBullet can apply its physics to the camera. Then, when I need to change something (the position), I could simply change it in the RigidBody. But I didn't find any methods for editing the position.
Can you push me in the right direction or maybe give me an example source code?
I was asking the same question myself a few days ago. My solution was as Sierox said. To create a RigidBody of BoxShape and add that to the DynaicsWorld. To move the camera arund, apply force to its rigidbody. I have damping set to .999 for linear and 1 for angular to stop the camera when no force is applied, i.e. the player stops pressing the button.
I also use body.setAngularFactor(0); so the box isn't tumbling all over the place. Also set the mass really low as not to interfere too much with other objects, but still be able to jump on then and run into them, and otherwise be affected by them.
Remember to convert your x,y, and z coordinates to cartesian a plane so you move in the direction of the camera. i.e.
protected void setCartesian(){//set xyz to a standard plane
yrotrad = (float) (yrot / 180 * Math.PI);
xrotrad = (float) (xrot / 180 * Math.PI);
float pd = (float) (Math.PI/180);
x = (float) (-Math.cos(xrot*pd)*Math.sin(yrot*pd));
z = (float) (-Math.cos(xrot*pd)*Math.cos(yrot*pd));
//y = (float) Math.sin(xrot*pd);
}//..
public void forward(){// move forward from position in direction of camera
setCartesian();
x += (Math.sin(yrotrad))*spd;
z -= (Math.cos(yrotrad))*spd;
//y -= (Math.sin(xrotrad))*spd;
body.applyForce(new Vector3f(x,0,z),getThrow());
}//..
public Vector3f getThrow(){// get relative position of the camera
float nx=x,ny=y,nz=z;
float xrotrad, yrotrad;
yrotrad = (float) (yrot / 180 * Math.PI);
xrotrad = (float) (xrot / 180 * Math.PI);
nx += (Math.sin(yrotrad))*2;
nz -= (Math.cos(yrotrad))*2;
ny -= (Math.sin(xrotrad))*2;
return new Vector3f(nx,ny,nz);
}//..
to jump just use body.setLinearVelocity(new Vector3f(0,jumpHt,0)); and set jumpHt to whatever velocity you wish.
I use getThrow to return a vector for other objects i may be "throwing" on screen or carrying. I hope I answered your question and didn't throw in too much non-essential information.I'll try and find the source that gave me this idea. I believe it was on the Bullet forums.
------- EDIT ------
Sorry to have left that part out
once you have the rigid body functioning properly you just have to get it's coordinates and apply that to your camera for example:
float mat[] = new float[16];
Transform t = new Transform();
t = body.getWorldTransform(t);
t.origin.get(mat);
x = mat[0];
y = mat[1];
z = mat[2];
gl.glRotatef(xrot, 1, 0, 0); //rotate our camera on teh x-axis (left and right)
gl.glRotatef(yrot, 0, 1, 0); //rotate our camera on the y-axis (up and down)
gl.glTranslatef(-x, -y, -z); //translate the screen to the position of our camera
In my case I'm using OpenGL for graphics. xrot and yrot represent the pitch and yaw of your camera. the code above gets the world transform in the form of a matrix and for the purposes of the camera you need only to pull the x,y, and z coordinates then apply the transform.
from here, to move the camera, you can set the linear velocity of the rigid body to move the camera or apply force.
Before you read this answer I would like to mention that I have a problem with the solution stated in my answer. You can follow my question about that problem so that you can have the solution too if you use this answer.
So. First, you need to create a new BoxShape:
CollisionShape cameraHolder = new BoxShape(SIZE OF CAMERAHOLDER);
And add it to your world so that it interacts with all the other objects. Now you need to change all the methods about camera movement (not rotation) so that the methods move your cameraHolder but not your camera. Then set the position of your Camera to the position of the cameraHolder.
Again, if you have a problem where you can't move properly, you can check my question and wait for an answer. You also can find a better way of doing this.
If you have problems or did not understand something about the answer, please state it as a comment.
hi I have image of my house.Top view image.I want to have latitude lotitude info displayed when i click on the image.
I do have latitude longitude value for 1 left top part of image.
Also how to maintain latitude longitude values while zooming in out of the image.
Lat/lon is a geodesic coordinate system (WGS84), which means it is curved coordinates going around the earth - an image is flat, which means typically you can't easily go directly between the two. However it may be the case that an image of your house is so small area, that the calculation error will be small enough to be negligible (depending on what you need it for).
To do what you want to do, you need to find a "degrees per pixel" value which means you need to know the lat/lon for both top/left and bottom right of your image. If you have that it's simple. This assumes you're in the northern hemisphere:
var degreesPerPixelX = bottomX - topX / imageWidth;
var degreesPerPixelY = bottomY - topY / imageHeight;
And an event handler (the getEventOffsetFromImageXXX are not shown).
function onClick (evt) {
var x = getEventOffsetFromImageLeft(evt);
var y = getEventOffsetFromImageTop(evt);
var clickedLon = topX + x * degreesPerPixelX;
var clickedLat = bottomY + y * degreesPerPixelY;
}
The zoom level will affect the top/left bottom/right lon/lat so the calculations need to adjust accordingly.
When Google Maps calculate x/y to lon/lat they internally ALWAYS first convert the lon/lat to the coordinate system Spherical Mercator (EPSG:900913), do the operations in that system and then convert back. However Spherical Mercator has fixed zoom levels, which is probably not right for you. Nevertheless, this is a very worthwhile read.
http://www.maptiler.org/google-maps-coordinates-tile-bounds-projection/
N.b. degreesPerPixel is called resolution in google talk and the unit is meters per pixel. Meter is the unit in Spherical Mercator, which roughly translates to a meter at the equator, but is far from a meter the further north/south you get.
Anyone how abt this code snippet
function longToX(longitudeDegrees)
{
var longitude=longitudeDegrees-baselong;
longitude =degreesToRadians(longitude);
return (radius * longitude);
}
function latToY(latitudeDegrees)
{
var latitude=latitudeDegrees-baselat;
latitude =degreesToRadians(latitude);
var newy = radius/2.0 *
Math.log( (1.0 + Math.sin(latitude)) /
(1.0 - Math.sin(latitude)) );
return newy;
}
function xToLong(xx)
{
var longRadians = xx/radius;
var longDegrees = radiansToDegrees(longRadians);
var rotations = Math.floor((longDegrees + 180)/360)
var longitude = longDegrees - (rotations * 360)
return longitude+baselong;
}
function yToLat(yo)
{
var latitude = (Math.PI/2) - (2 * Math.atan(Math.exp(-1.0 * yo /this.radius)));
return radiansToDegrees(latitude)+baselat;
}