I am doing a Javascript application that requires the movement of a certain element in a real world map, in frames.
For each frame, i have the following positions in latitude and longitude for the element, for example for frame 0:
- Latitude: 49.011213
- Longitude: 8.422885
For frame 1:
- Latitude: 49.01121
- Longitude: 8.422887
Frame (Frame 0) needs to be point (0,0) and I want the following ones to be converted to XY Coordinates as well.
Basically, I need a Javascript script that receives the latitude and longitude of a frame and returns the position (x,y) for that frame (in relation to frame 0 with position (0,0)).
I've tried the following but it doesn't work:
function convertSphericalToCartesian(latitude, longitude)
{
// Convert from Degrees to Radians
let latRad = latitude * (Math.PI)/180;
let lonRad = longitude * (Math.PI)/180;
let earthRadius = 6367; // Radius in km
let posX = earthRadius * Math.cos(latRad) * Math.cos(lonRad);
let posY = earthRadius * Math.cos(latRad) * Math.sin(lonRad);
return {x: posX, y: posY};
}
Do you know a better formula for this? Thank you very much.
Any conversion you do will introduce error, since you're trying to map the surface of a sphere to rectangular coordinates. You haven't mentioned what you're doing with this information - the mention of "frames" makes me think of animation or a game involving moving things on a map. If that's the case, then your calculations are going to be closely tied to the map you're using.
In general, the question is: how far apart are your coordinates likely to be?
For small distances, up to a few miles or kilometers, you're probably just fine treating the lat/lon as x/y coordinates. For example, the coordinates you've given in your example are literally only feet apart - don't bother with complicated calculations. And again, how you draw something on a given map at those coordinates depends very much on the map you're using.
For larger distances, the curvature of the Earth becomes important and the map projection you're working with will make a big difference in how those coordinates are calculated. For example, this question discusses the calculations for a Mercator projection.
Related
Currently I am trying to understand a Codpen where this guy has JSON data being fed into a Javascript app that plots coordinates using x and y.
Instead of using longitude and latitude to plot for example Hong Kong, he uses these coordinates.
{"x": 768,"y": 342,"name": "", "country":"Hong Kong"}
I want to be able to put in x and y a longitude and latitude value, but I can not figure out how to multiply or divide, a simple solution to go with his code. I am new to Javascript, but am trying to understand how to plot coordinates more efficiently on this specific project.
Is there a simple equation I could use to be able to plot more easily on this pen.
Thanks.
https://codepen.io/Flamov/pen/MozgXb
The example is basically using the Mercator Projection to convert radius, lat, long into euclidean x, y, z coordinates, as mentioned on lines 860+ of the JSFiddle you provided, and is using this S.O. answer as reference. Since radius is constant throughout the globe, you don't need to repeat that value for each point, it's just hard-coded into the example.
Mercator is a bit confusing because the scale stretches towards infinity as you approach the poles. As an easier alternative, you could use Vector3.setFromSphericalCoords(rad, lat, long) as outlined in the docs and it sets x, y, z for you. The main difference is that this approach doesn't cause distortion near the poles. It takes lat, long in radians:
lat ranges from [0, Pi] (north pole to south pole)
long ranges from [0, 2*Pi] (around the equator)
I am using the Web Mercator to transform longitudes and latitudes to x and y coordinates. However, when I want to inverse this problem the built-in atan() (or Math.atan()) function of JavaScript (and p5.js) gives me trouble. The Web Mercator and the calculated inverse are given below.
formulas
Then, for an example y position in my grid, I have code similar to:
//Sample code for transforming y coordinate to latitude using Web Mercator
//map size: 128x128 -> c_map = 128, zoom level: 1
var ypos = 100;
var c_map = 128;
var temp = PI*(1- ypos/(pow(2, 1)*c_map);
temp = atan(exp(temp) * 180/PI);
lat = 2*(180 / PI)*(temp - PI / 4);
However, this gives me false values for the y coordinate since JavaScript computes the tangent and arctangent in radians.
I have tried several configurations of first transforming the exponent to radians (is the exponent in degrees?) and then transforming the result back to degrees, but to no avail. The y coordinates are given relative to the point [0,0].
What kind of radian/degree transformation do I need to apply where in order to get correct values of my y coordinate?
I've implemented some code to create some code to treat an image of a relatively small location like plane for converting between locations on the image I have stored and incoming Lat/Long information.
Using the formulas provided at https://msdn.microsoft.com/en-us/library/jj635757(v=vs.85).aspx I wrote these lines of code among others
var vector = math.matrix(
[[x1],
[y1],
[x2],
[y2]]);
var matrix = math.matrix(
[[lat1,long1,1,0]
,[-long1,lat1,0,1]
,[lat2,long2,1,0]
,[-long2,lat2,0,1]]);
var solution = math.multiply(math.inv(matrix),vector);
There is an implicit conversion from the vector returned to solution into conversiondata as I put it into and take it back out of my database.
a = parseFloat(conversiondata['A']);
b = parseFloat(conversiondata['B']);
c = parseFloat(conversiondata['C']);
d = parseFloat(conversiondata['D']);
var long = position.coords.longitude;
var lat = position.coords.latitude;
var x = a * lat + b * long + c;
var y = b * lat - a * long + d;
The values x1, x2, y1, y2 are supplied by getting user click data.
The values lat1, lat2, long1, long2 are supplied by the user in response to two clicks on the map image.
When putting x,y back onto the map its not quite in the right position, the position on the map seems to almost be on the opposite side of the line defined by (x1,y1) and (x2,y2). I'm trying to tell what the reason for the inaccuracy is. (I am however assuming for the time being that the apparent reflection is a coincidence)
If someone could help me narrow down what could be going wrong here are things I've considered (the map doesn't reach even a mile in any direction for reference).
The affine transformation simply doesn't work - But acccording to the link provided it includes scaling so that shouldn't be the cause of the problem
There is a problem with my setting of variables - I've been looking at my code too long to see it if it is.
I am losing too much accuracy moving the var data to MySQL as a float or to PHP as a string
I am not giving accurate enough information from click data / lat/long input. - I zoomed i significantly when clicking on the map and getting the lat/long from google maps though
SVG isn't accuracte enough - Though looking at the xml data it keeps the decimals.
The area that I'm working with is too big to simplify by assuming that the local map is a flat plane
Any help is appreciated, thanks for reading this far.
For further reference I put the lat/long data that JavaScript gave me into google maps and i'm comparing accuracy to that rather than my actual location.
Additional reference: I found "landmarks" on the east and west edges of my image and have calculated the longitude difference to be 0.02695 with the length of the image being at least twice the height.
Sample values of a full run-through of values.
Reference Points
Point 1 (x,y) = (619,564)
Point 1 (lat,long) = (X.099546,-Y.465179)
Point 2 (x,y) = (1181,190)
Point 2 (lat,long) = (X.10365341,-Y.457014)
Geolocation
Predicted coordinate (x,y) = (975,262)
Given coordinate(lat,long) = (X.102851,-Y.459996)
Real Blip (x,y) = (1022.7498707999475,351.02335709985346)
Real blip (approximate lat,long) = (X.101964, -Y.459340)
(Real blip lat long is approximate as it is in a body of water with no good landmarks)
For safety's sake I've taken the digits before the decimal out of the lat/long coordinates but I can confirm that all the X's are equal and all the Y's are equal
Additionally I played with the lat long values in Chrome's developer tools, it seems like the axes are a bit rotated approximately 30 degrees from what it should be
After sufficient poking around I figured out that I had ordered lat and long incorrectly. On my map that has not been rotated from N at the top the following code brings me within just a few feet, more than explainable than the lack of precision resulting from relying on user input and the pixel grid.
var matrix = math.matrix(
[[long1,lat1,1,0]
,[-lat1,long1,0,1]
,[long2,lat2,1,0]
,[-lat2,long2,0,1]]);
And
var x = a * long + b * lat + c;
var y = b * long - a * lat + d;
For anyone else that is interested in pursuing this as a potential solution to simplify the math of their app
The drift that occurred was less than 40 feet over a map with a diagonal of 8000 feet and a difference in reference points of around 3000 feet. This means the drift is little over 1% of the distance of the reference points, this includes the effect of human error.
This error should decrease as you work on smaller maps and increase as you work on bigger maps.
I tested it again on a map with a ~90 degree rotation and the code held up
I am working on an application where I am trying to run some algorithms on a map and calculate coords but they are slightly off because I am running the calculations on latitude and longitudes and the final results are getting distorted.
Now I am trying to convert all coordinates to EPSG3857 Web Mercator coordinates like so:
var crs = L.CRS.EPSG3857;
var zoom = terrainAnalysisMap.getZoom();
markerToPoint = crs.latLngToPoint(marker.getLatLng(), zoom);
markerOnRadiusToPoint = crs.latLngToPoint(markerOnRadius.getLatLng(), zoom);
Now I also have a radius which I will have to convert from metres to pixels but I cannot figure out how. How much is 1 metre in pixels? I mean it would depend on the zoom level too, right? So how to go about converting radius to pixels in mapbox and leaflet?
If you happen to have the latitude of the place where you are looking to convert the radius to pixels, you can make use of the "Meters per pixel" function described at http://wiki.openstreetmap.org/wiki/Zoom_levels. Here's my javascript implementation:
var metersPerPixel = function(latitude, zoomLevel) {
var earthCircumference = 40075017;
var latitudeRadians = latitude * (Math.PI/180);
return earthCircumference * Math.cos(latitudeRadians) / Math.pow(2, zoomLevel + 8);
};
var pixelValue = function(latitude, meters, zoomLevel) {
return meters / metersPerPixel(latitude, zoomLevel);
};
A meter is a meter, no matter what your zoom level is. What you need is to convert a meter into degrees since long/lat is a polar coordinate system. With the circumference of Earth being 40,075 km, you get 0.00000898315 deg/m which you need to multiply with the size of the object (1 m) to get the degrees which you have to add to your coordinate to get a point which intersects with the radius of the circle that you want to draw.
But usually, it's easier to just draw a circle with a radius of 10 px around the center coordinate (after you transformed it from world to screen) making sure that the circle is always the same size, no matter of the zoom level. That way, people won't have a problem to see and/or click it.
[EDIT] Your question is related to Parametric equation to place a leaflet marker on the circumference of a circle is not precise?
My suggestion is to forget about world coordinates for the drag/drop problem. Obviously, you already have a circle (which means you must know the center point and the radius in pixels - otherwise, you couldn't draw it).
What you need is to implement the dragging of the marker only in screen coordinates. That should be pretty simple to implement.
When the user releases the mouse, all you have to do is to take the screen coordinate and convert it into long/lat once.
One problem to keep in mind: If you're using something like Mercator projection, the coordinates will be off as you get closer to the poles. To solve this, you need to work with an ellipse (wider than tall) instead of a circle.
I have done this once using this array for pixel/realworld-meter translation
pixelMeterSizes: {
10: 1 / 10,
11: 1 / 9,
12: 1 / 8,
13: 1 / 7,
14: 1/ 5,
15: 1 / 4.773,
16: 1 / 2.387,
17: 1 / 1.193,
18: 1 / 0.596,
19: 1 / 0.298
}
Notice, that above a zoomlevel of 15, i simplified things, because the symbols were getting too small and would not be visible anymore.
I used this as a basic reference http://wiki.openstreetmap.org/wiki/Zoom_levels.
This worked quite good for me, but i am not sure what will happen when dealing with high res displays and such. Guess it could fail in those scenarios.
In Javascript.
I have a map of the world that is 1024 x 1024px - It is a spherical Mercator projection.
Im looking to convert long/lat to x,y for this map. I have a bunch of coordinates that I need to overlay on top of it.
Calculating longitude was very easy, and I am doing it like so:
pos.x = ((long + 180)/ 360) * 1024;
I just need to same for lat --> y
I looked around a saw a lot of reference to openlayers, but didn't see such a conversion.
To boil it down:
var mercator = function(lat, lng) {
return [lng, Math.log( (Math.sin(lat) + 1.0) / Math.cos(lat)) ] ;
};
Lat and long must be expressed in radians of course.
You can find the corresponding formulas in this wikipedia article: Mercator Projection.