Equirectangular map on Web - javascript

I plan to build an online map for markers (pins) of a game and I don't manage to set the correct latitude of my markers.
Original map is a square of 2048*2048px
Then I got markers (many thousands)
Map coordinates are set with a x, y notation from 0 to 100.
0, 0 is the top left corner and 100, 100 is the bottom right corner of the map.
x=50, y=50 is lat = 0°, lng = 0° (the center of the picture).
To convert from my notation to longitude I use this JS function, it works well :
function longitude(x)
{
var lng = 0
if (x < 50) // Negative Longitude (West)
{
lng = (x - 50) / 50 * 180;
}
else // Positive Longitude (East)
{
lng = (50 - x) / 50 * 180;
}
return lng
}
But for latitude it don't work because those engines use a Mercator projection and me not.
If someone have the correct formula, it would be greatly appreciated :(

Welcome to SO!
If you are using Leaflet, you should specify the map option crs and use L.CRS.Simple:
A simple CRS that maps longitude and latitude into x and y directly. May be used for maps of flat surfaces (e.g. game maps). Note that the y axis should still be inverted (going from bottom to top).
This will avoid the Web Mercator projection, especially the latitude which is a special computation as you figured out (see the linked Wikipedia article for the equation).
Then you are left with correctly mapping your x and y coordinates to your need, especially in respect with your map image.
For instance, assuming you set your map image as:
L.imageOverlay("imageUrl", [[0, 0], [256, 256]]).addTo(map);
(so that it fits the equivalent of 1 tile at zoom level 0)
Then you could have a conversion like:
function longitude(x) {
return x / 100 * 256;
}
function latitude(y) {
return 256 - y / 100 * 256; // Still need to revert y.
}

Related

Google Maps Api - when is map.panto(latlng) smooth

EDITED:
I'm trying to work out when Google Maps API map.panTo(Lat, Lng) decides the trip is too far (pixels) for a "smooth" pan.
This is what the manual has to say: -
Changes the center of the map to the given LatLng. If the change is less than both the width and height of the map, the transition will be smoothly animated.
I've established that if there is only x or y vertex movement (only either Lat or Lng value changes but not both) then the check is a simple two-thirds .6666 of the map's viewport width or height. But if both Lat and Lng values change then I'm not sure of the formula.
An example of what we know: -
If we travel from Perth to somewhere up near Yeppoon: -
Perth: Lat: -31.9523 Lng: 115.8613 xPixel: 13465 yPixel: 9728
Yeppoon: Lat: -22.9523 Lng: 150.2093 xPixel 15028, yPixel: 9265
X/vertical movement: 15028 - 13465 = 1563
Y/horizontal movement: 9265 - 9728 = -463
Then, for that same trip, the following viewport sizes yield smooth pans; 1 pixel width or height less forces a hard pan: -
Viewport
Width: 1337 1435 1236
Height: 492 448 574
What is the formula for viewport pan borderline?
It should be obvious but I just can't see it
The only other information I have is: -
Google Title size at zero zoom = 256
The zoom I'm using is 6 = multiplier 64
X Pixel Formula = 256 * (0.5 + Longitude / 360) * 64
let siny = Math.sin((Latitude * Math.PI) / 180);
// Truncating to 0.9999 effectively limits latitude to 89.189. This is
// about a third of a tile past the edge of the world tile.
siny = Math.min(Math.max(siny, -0.9999), 0.9999);
Y Pixel Formula = 256 * (0.5 - Math.log((1 + siny) / (1 - siny)) / (4 * Math.PI))
Make any sense?
EDITEND
Please copy https://richardmaher.github.io/Brotkrumen/testmap.html if it makes life easier (has to be run locally due to API key)
See console for debugging info.
Shrink browser to Width 615px and you'll smooth scrolling/panning stop.
Ready to answer any other questions
See also https://issuetracker.google.com/issues/228349799
Can someone please explain in pseudocode, or at least less ambiguous language, the API Refence Manual definition for smooth transition requirements of the panTo() method: -
panTo panTo(latLng)
Parameters: latLng: LatLng|LatLngLiteral The
new center latitude/longitude of the map.
Return Value: None
Changes the center of the map to the given LatLng. If the change is less
than both the width and height of the map, the transition will be smoothly
animated.
Specifically, what is "the change" in this context?
Example: -
Zoom Level = 6
LatLng1 = lat: -31.9523, lng: 115.8613 Pixel X = 13464 Pixel Y = 9728
LatLng2 = lat: -33.8688, lng: 151.2093 Pixel X = 15073 Pixel Y = 9831
Current Map Center is LatLng1 and panning to LatLng2
I make the "change" to be horizontal 1609px and vertical 103px
If the map's DIV container is at least 616px wide and 344px high the pan is smooth if not it jumps.
Can someone please help me heuristicly marry up those figures with an algoithm?
panTo() changes the map's midpoint and if the coordinates of the midpoint changes within the viewport, the transition will be smoothly animated.
Viewport contains the recommended viewport for displaying the returned result, specified as two latitude, longitude values defining the southwest and northeast corner of the viewport bounding box. Generally the viewport is used to frame a result when displaying it to a user.
Ok I think the answer to the specific question is just down to rounding but I don't need to chase that one as, in the last month or so, Google has decouple its rehoming of the markers from the smooth pan test. It is now an arbitrary 100000px limit before it stops maintaing the DOM for markers.
The complete story can be found here (remember due to the API key hard-coding you need to copy testmap.html to your local file system before trying to run it.)
The TL;DR version and core logic is in this function: -
function makeDestCenter(){
console.log("Panning to new Center " + map.getZoom());
var home = map.getCenter();
var zoom = map.getZoom();
var scale = 1 << zoom;
var proj = map.getProjection();
var homePoint = proj.fromLatLngToPoint(home);
var startPixelX = Math.round(homePoint.x * scale);
var startPixelY = Math.round(homePoint.y * scale);
var destPoint = proj.fromLatLngToPoint(dest[destIndex]);
var destPixelX = Math.round(destPoint.x * scale);
var destPixelY = Math.round(destPoint.y * scale);
var xTrip = Math.abs(destPixelX - startPixelX);
var yTrip = Math.abs(destPixelY - startPixelY);
console.log("sX " + startPixelX + " dX " + destPixelX + " sY " + startPixelY + " dY " + destPixelY);
if ((xTrip > MAX_TRIP) || (yTrip > MAX_TRIP)) {
google.maps.event.addListenerOnce(map, 'idle', makeDestCenter);
map.setZoom(--zoom);
} else {
if (xTrip == TOO_SMALL && yTrip == TOO_SMALL) {
google.maps.event.addListenerOnce(map, 'idle', makeDestCenter);
map.setZoom(++zoom);
} else {
map.panTo(dest[destIndex]);
}
}
}

How to get a fixed number of coordinates between two points polyline in Javascript

I'm looking for a way to find multiple coordinates between 2 points in Javascript (I don't need distance or mid point).
Say I have these 2 points:
Point A (left) lat: 39.091868 long: -9.263187
Point B (right) lat:
39.089815 long: -9.261857
I want to generate for instance 20 coordinates between points A and B.
I tried using this piece of Javacript code (Found on an old thread):
Point = function(x, y) {
this.x = x;
this.y = y;
}
var pointA = new Point(39.091868, -9.263187);
var pointB = new Point(39.089815, -9.261857);
var numberOfPoints = 20;
var points = new Array();
for (var i = 0; i < numberOfPoints; i++) {
points.push(new Point((Math.abs(pointA.x - pointB.x) / 10) * i + pointB.y, (Math.abs(pointA.y - pointB.y) / numberOfPoints) * i + pointB.y));
}
console.log(points);
When I run the code above, it prints out 20 values of the points array between point A and B. However, it only seems to store the correct longitude values of the points. (x and y both show longitude values).
For example, the first item of the points array is:
Point x: -9.261857 y: -9.261857
How should I adjust the calculation inside the for loop so that each point between point A and B also stores a correct latitude value? Or does anyone know another way to calculate a fixed number of coordinates between 2 points in Javascript?
Any kind of help is appreciated
Because the earth is not flat, the calculation can be good for small distance.
Just calculate the point.
1 Degree is approximal 110 km. so you may do just Pitagoras calculation for small distance and multiply by 110 km, and do iterative loop.
For accuracy you may use open-layers library (most of it is free), but you should provide map with projection. Make things a bit complicated.
One of the function is find the coordinate of a specific point, give distance and azimuth.
Need to find the the azimuth first of two points.
Use O/L: openlayers sphere getlength & bearing.
O/L distance
Open layers

Converting Latitude and Longitude to coordinates on image

I am trying to convert a location to a point on image. I am using fabricjs to draw the map, the map object I made out of an image of the earth. I convert the image to an array, where land is 1 and 0 is water.
My problem is that I cant convert the location to a point properly.
I am using this formula which works, but not for the image I am using.
function getMapCoordsByLatAndLng(latitude, longitude, mapWidth, mapHeight){
var x = (longitude + 180 )* (mapWidth / 360);
var latRad = latitude* Math.PI /180;
var mercN = Math.log(Math.tan(( Math.PI /4)+(latRad/2)));
var y = (mapHeight / 2) - ( mapWidth *mercN/(2 * Math.PI));
return {
x: x,
y: y
};
}
Here is my result for now.
I have used two locations - Los Angeles and Sydney. As you can see the coordinates on the map are not correct.
Here is a fiddle with all the code.
Edit:
I ended up following a suggestion to use equirectangular projection instead of mercator one.
Thus the algorythm for finding the coordinates on the canvas changed.
function getMapCoords(latitude, longitude, mapWidth, mapHeight){
return {
x: parseInt((longitude + 180.0) * (mapWidth / 360.0)),
y: parseInt(((latitude * -1.0) + 90.0) * (mapHeight / 180.0))
}
}
I have updated my code and the result can be found here.
Your map is off and the coordinates for Sydney are plain wrong.
With better coordinates:
Seems image has more than 360 degrees. You need to slice it properly.
Then divide by 360.
Or if you want to use it as it is, you need to add offsets.

How to get Latitude and Longitude Bounds from Google Maps x y and zoom parameters

I have seen some questions with similar titles, but they seem to be referring to x and y pixel coordinates.
I am asking about the actual tile numbers of x and y from Google Maps getTile() function:
To clarify the question...
Given the x, y, and zoom parameters in the getTile() function, how can I find the latitude and longitude bounds of the tile?
CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) {
var x = coord.x,
y = coord.y,
url = "http://mt1.google.com/vt/lyrs=y&x="+x+"&y="+y+"&z="+zoom;
//other stuff
}
The only reason at the moment that I need this is that I want to determine the maximum zoom level at this tile projection. From this link: Maximum Zoom, it states that in order to find the maximum zoom, I will need a latitude and longitude value using getMaxZoomAtLatLng(). So if I can get the bounds, then I can use any latitude and longitude points within the bounds to find my max Zoom.
Alternatives I have thought of were creating an image and checking if the src url had an error (this seems like a terrible idea to me, as I would be making many bad requests just to check if imagery existed).
var img = new Image;
img.onload = function() {/*imagery exists*/ }
img.onerror = function() {/*past maximum zoom*/ }
img.src = url;
EDIT:
After further investigation, I realize that the getMaxZoomAtLatLng() function is using an ajax call which will not fit into my plans. But I still am interested in how to find the latitude and longitude boundaries of a given tile ( that could be useful for other applications ).
Assuming a basic google-map using mercator-projection and a tileSize of 256x256:
The number of tiles on each(x-axis and y-axis) is Math.pow(2,zoom), so on zoom 0 the map is using 1 tile, on zoom 1 4 tiles, on zoom 2 16 tiles and so on.
First calculate the southWest/northeast-points of the tile.
the size of a tile (in points) is 256/Math.pow(2,zoom)
southWest-point:
x = tile.x * tileSizeInPoints
y = (tile.y * tileSizeInPoints) + tileSizeInPoints
northEast-point:
x = (tile.x * tileSizeInPoints) + tileSizeInPoints
y = tile.y * tileSizeInPoints
These points must be translated to LatLngs. When you use a map you may use the method fromLatLngToPoint of the maps projection.
For a custom implementation take a look at https://developers.google.com/maps/documentation/javascript/examples/map-coordinates.
A possible API-independant implementation:
MERCATOR={
fromLatLngToPoint:function(latLng){
var siny = Math.min(Math.max(Math.sin(latLng.lat* (Math.PI / 180)),
-.9999),
.9999);
return {
x: 128 + latLng.lng * (256/360),
y: 128 + 0.5 * Math.log((1 + siny) / (1 - siny)) * -(256 / (2 * Math.PI))
};
},
fromPointToLatLng: function(point){
return {
lat: (2 * Math.atan(Math.exp((point.y - 128) / -(256 / (2 * Math.PI)))) -
Math.PI / 2)/ (Math.PI / 180),
lng: (point.x - 128) / (256 / 360)
};
},
getTileAtLatLng:function(latLng,zoom){
var t=Math.pow(2,zoom),
s=256/t,
p=this.fromLatLngToPoint(latLng);
return {x:Math.floor(p.x/s),y:Math.floor(p.y/s),z:zoom};
},
getTileBounds:function(tile){
tile=this.normalizeTile(tile);
var t=Math.pow(2,tile.z),
s=256/t,
sw={x:tile.x*s,
y:(tile.y*s)+s},
ne={x:tile.x*s+s,
y:(tile.y*s)};
return{sw:this.fromPointToLatLng(sw),
ne:this.fromPointToLatLng(ne)
}
},
normalizeTile:function(tile){
var t=Math.pow(2,tile.z);
tile.x=((tile.x%t)+t)%t;
tile.y=((tile.y%t)+t)%t;
return tile;
}
}
call MERCATOR.getTileBounds() by supplying a single object as argument with the following format:
{
x:tileIndexX,
y:tileIndexY,
z:zoom
}
Demo: http://jsfiddle.net/doktormolle/55Nke/
I think Google maps tiling system is similar to the Bings maps tiling system. The tiles start from the upper left in the lower right and each tile is 256x256:http://msdn.microsoft.com/en-us/library/bb259689.aspx.
Not sure if this entirely helps with the bounds, but to find an easy display of the tile coordinates I went here:
https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
And to typescript on the line:
const chicago = new google.maps.LatLng(-33.76781028848151, 150.73644505329204
);
Change the lat/long to wherever you want... And they have a nice UI that calculates all the changes on zoom.

Google Maps polygon

I have a lat/long coordinate point and I'm drawing a polygon (hexagon) around it on a Google map. Here's my code to calculate the hexagon coordinates:
for (var i = 0; i < 6; i++) {
x = lat + r * Math.sin(i * 2 * Math.PI / 6);
y = lng + r * Math.cos(i * 2 * Math.PI / 6);
}
This calculates all coordinates in a regular hexagon and I can draw it on the map without a problem if its center is near (0 lat, 0 long). The problem is when I want to draw it far from (0, 0) this gets into an elongated shape. I'm guessing it's because the earth is not flat and Google maps takes that into account. So I probably need to change the radius in my calculation to reflect this, has anyone any idea how it is done?
Examples of various regular polygons far from (0,0)

Categories

Resources