Leaflet - how to rotate latlong 90degrees? - leaflet

Wrong
Right
Ive got a CRS.simple map made from x y coordinates. When inserted with markers, they go as the 1st image shows, but they should go as they are in the 2nd image. How do i get that rotation to happen?

The CRS.Simple coords are in the format [y,x]
You can switch them with following:
var yx = L.latLng;
var xy = function(x, y) {
if (L.Util.isArray(x)) { // When doing xy([x, y]);
return yx(x[1], x[0]);
}
return yx(y, x); // When doing xy(x, y);
};
https://leafletjs.com/examples/crs-simple/crs-simple.html#this-is-not-the-latlng-youre-looking-for
Update
Mirror the coords with adding a minus:
var yx = L.latLng;
var xy = function(x, y) {
if (L.Util.isArray(x)) { // When doing xy([x, y]);
return yx(-x[1], -x[0]);
}
return yx(-y, -x); // When doing xy(x, y);
};

Related

Fix/offset tiles coordinates on leaflet map

I downloaded some tiles from ArcGIS.Imagery with SAS Planet
The tiles is in folders like
ArcGIS.Imagery/z2/0/x1/0/y2.jpg
And I use this TileLayer to display it on leaflet.
L.TileLayer.customTiles = L.TileLayer.extend({
getTileUrl: function (t) {
console.log('coords', t);
z = t.z,
x = t.x,
y = t.y,
z0 = t.z,
x0 = Math.floor(t.x / 1024),
x1 = Math.floor(t.x % 1024),
y0 = Math.floor(t.y / 1024),
y1 = Math.floor(t.y % 1024)
const d = `ArcGIS.Imagery/z${z0}/${x0}/x${x1}/${y0}/y${y1}.jpg`;
console.log('d', d);
return d;
}
});
L.tileLayer.customTiles = function () {
return new L.TileLayer.customTiles();
}
L.tileLayer.customTiles().addTo(map);
But it looks like there is wrong coordinates. Here is what I got as a result. Any ideas how to fix it? Thanks

How to convert a location on football pitch to coordinates on rectangle?

I have 4 points of football pitch (corner points):
P1(lat, lon, alt), P2(lat, lon, alt), P3(lat, lon, alt), P4(lat, lon, alt).
and a location on the pitch:
L(lat, lon, alt)
I want to convert L(lat, lon, alt) to L(x, y) on a rectangle with size of (W, H).
How to implement this conversion function? (I preferred C# language but implementation language is not important)
The following image describes my problem (I don't know how to implement the Function box):
First off, because output coordinates are 2D, I'm going to assume that we can get rid of altitude information from input coordinates. So input data consist of four points defining the input rectangle:
P1(lat, lon), P2(lat, lon), P3(lat, lon), P4(lat, lon)
and dimensions of the output rectangle: w, h.
I'm also going to ignore the curvature of the Earth (football pitch is small enough). With those assumptions we can implement the conversion function, by performing affine transformation. It would be wasteful to create transformation matrix each time we want to perform a transformation. For that reason we need two functions: first one to create the transformation matrix (called only once), and the second one that will use that matrix to perform transformation itself (called possibly many times, one time for each point we want to transform), something like:
tm = createTransformationMatrix(P1, P2, P4, w, h)
inPoint = (200, 50)
outPoint = transform(inPoint, tm)
Note that we only need three of four input points to unambiguously define a rotated rectangle in 2D euclidean space.
Here is the implementation of createTransformationMatrix and transform functions:
const run = function() {
// Creates transformation matrix to transform
// from rectangle somewhere in 2D space with coordinates p0, px, pi, py
// to rectangle with coordinates (x=0, y=0), (x=w, y=0), (x=w, y=h), (x=0, y=h).
// Note that: p0 is mapped to (x=0, y=0)
// px is mapped to (x=w, y=0)
// py is mapped to (x=0, y=h)
const createTransformationMatrix = function(p0, px, py, w, h) {
// Translate px and py by p0 - pxt and pyt are px and py vectors in coordinate system in which p0 is at the origin
const pxt = {
x: px.x - p0.x,
y: px.y - p0.y,
};
const pyt = {
x: py.x - p0.x,
y: py.y - p0.y,
};
// Create transformation matrix, which is inverse of transformation matrix that:
// 1. Transforms (x=0, y=0) to (x=p0.x, y=p0.y)
// 2. Transforms (x=1, y=0) to (x=p0.x + pxt.x / w, y=p0.y + pxt.y / w)
// 3. Transforms (x=0, y=1) to (x=p0.x + pyt.x / h, y=p0.y + pyt.y / h)
return Matrix.invert3([
[pxt.x / w, pyt.x / h, p0.x],
[pxt.y / w, pyt.y / h, p0.y],
[0 , 0 , 1 ],
]);
};
const transform = function(point, transformationMatrix) {
// Convert point to homogeneous coordinates
const inputVector = [
[point.x],
[point.y],
[1],
];
// Transform inputVector
const outputVector = Matrix.multiply(transformationMatrix, inputVector);
// Convert outputVector back to cartesian coordinates and return
return {
x: outputVector[0][0] / outputVector[2][0],
y: outputVector[1][0] / outputVector[2][0],
};
};
const w = 220;
const h = 115;
const p1 = {x:-79, y:80 };
const p2 = {x:9, y:-96};
const p3 = {x:55, y:-72};
const p4 = {x:-34, y:105};
const tm = createTransformationMatrix(p1, p2, p4, w, h);
const inPoint = {x: 200, y: 50};
const outPoint = transform(inPoint, tm);
console.log(`(${inPoint.x}, ${inPoint.y}) --[transform]--> (${outPoint.x}, ${outPoint.y})`);
}
//// Matrix ////
const Matrix = {};
Matrix.scale = (s, m) => m.map(x => Array.isArray(x) ? Matrix.scale(s, x) : s * x);
Matrix.multiply = function(a, b) {
const aNumRows = a.length, aNumCols = a[0].length;
const bNumRows = b.length, bNumCols = b[0].length;
const m = new Array(aNumRows);
for (let r = 0; r < aNumRows; ++r) {
m[r] = new Array(bNumCols);
for (let c = 0; c < bNumCols; ++c) {
m[r][c] = 0;
for (let i = 0; i < aNumCols; ++i)
m[r][c] += a[r][i] * b[i][c];
}
}
return m;
};
Matrix.invert3 = function(m) {
const [[a, b, c],
[d, e, f],
[g, h, i]] = m;
const det = a*(e*i - f*h) - b*(d*i - f*g) + c*(d*h - e*g);
return Matrix.scale(1/det, [
[e*i - f*h, c*h - b*i, b*f - c*e],
[f*g - d*i, a*i - c*g, c*d - a*f],
[d*h - e*g, b*g - a*h, a*e - b*d],
]);
};
//////////////
run();
I've included all the matrix processing logic, so that this code snippet is self contained, but I would suggest you to instead use some linear algebra library for matrix processing.
I've also made a more visual demo.

How to get the points (coordinates) on 2D Line?

When I plot point1(p1) and point2(p2), the line between p1 and p2 is drawn. I wanna know a set of the points making the line.
For example, I wanna get x, y coordinates (as array type: x[], y[]). Is there any algorithms or code?
Here's what I have come up with:
It is fair to say that we need to use the slope formula, y = m*x + b to find the slope so we can plot our points along that line. We need the following:
(x1, y1)
(x2, y2)
to find the following:
m = (y2 - y1) / (x2 - x1)
b = y1 - (m * x1)
minX = min(x1, x2) used for limiting our lower bound
maxX = max(x1, x2) used for limiting our upper bound
Now that everything is set, we can plot our line pixel by pixel and obtain all (x,y) coordinates we need. The logic is simple:
let x loop from minX to maxX and plug it in y = m*x + b (we already have all the variables except y). Then, store the (x,y) pair.
I have used Java for coding this logically and visually. Also, I used LinkedList instead of arrays (because I we can't know the number of points we will obtain).
I have also drawn what Java would draw (in blue) and my approach (in red). They are almost perfectly the exact output and coordinates. The image below is zoomed 5x the original size.
Note! The above explanation is what you would use if the line is not vertical (because the slope would be undefined, division by zero). If it is, then you will plug y (instead of x) values and find the x (instead of y) value from the following formula x = (y - b) / m (instead of y = m*x + b). Though, the code takes care of vertical lines.
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.LinkedList;
import javax.swing.JFrame;
public class LineDrawing extends Canvas {
int x1 = 5;
int y1 = 10;
int x2 = 105;
int y2 = 100;
double m = ((double) (y2 - y1)) / ((double) (x2 - x1));//slope
double b = y1 - (m * ((double) x1));//vertical shift
//Takes care of the domain we will loop between.
//min and max will be assigned minX and maxX if the line is not vertical.
//minY and maxY are assigned to min and max otherwise.
int minX = Math.min(x1, x2);//minimum x value we should consider
int maxX = Math.max(x1, x2);//maximum x value we should consider
int minY = Math.min(y1, y2);//minimum y value we should consider
int maxY = Math.max(y1, y2);//maximum y value we should consider
int min = 0;
int max = 0;
boolean plugX = true;//if true, the line is not vertical.
LinkedList<Point> points = new LinkedList<>();//Store all points here
public LineDrawing() {
if (x1 == x2) {//plug the y value instead the x, this is a vertical line.
plugX = false;
min = minY;
max = maxY;
} else {//dont change and plug x values.
min = minX;
max = maxX;
}
}
#Override
public void paint(Graphics g) {
super.paint(g);
//Draw the line, using default java drawLine in blue.
g.setColor(Color.BLUE);
g.drawLine(x1, y1, x2, y2);
//change the color to red, it will draw our verison.
g.setColor(Color.RED);
//Draw the points, point by point on screen.
//Plug m, x, and b in the formula y = m*x + b
//to obtain the y value.
//OR
//Plug m, y, and b in the formula x = (y - b) / m
//to obtain the x value if vertical line.
//Then plot (x,y) coordinate on screen and add the point to our linkedList.
for (int i = min; i <= max; i++) {
int obtained = 0;
if (plugX) {//not a vertical line
obtained = (int) Math.round((m * i + b));
System.out.println("x = " + i + " , y = " + obtained);
points.add(new Point(i, obtained));
//Uncomment to see the full blue line.
g.drawLine(i, obtained, i, obtained);
} else {//vertical line
obtained = (int) Math.round((double) (i - b) / (double) m);
System.out.println("x = " + x1 + " , y = " + i);
g.drawLine(x1, i, x1, i);//Uncomment to see the full blue line.
points.add(new Point(x1, i));
}
}
//Print out the number of points as well as the coordinates themselves.
System.out.println("Total points: " + points.size());
for (int i = 0; i < points.size(); i++) {
System.out.println(i + " ( " + points.get(i).x
+ ", " + points.get(i).y + " )");
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(120, 150);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new LineDrawing());
frame.setVisible(true);
}
}

Miller Projection to pixels

I'm trying to use Miller Projection to convert coordinates to pixels.
My method looks like this:
function millerProjection(lat, lng) {
// Create sec() function //
function sec(value) {
return 1/Math.cos(value);
}
// Create fucntion to change degree to radian //
function toRadian(value) {
return value * Math.PI / 180;
}
lng = toRadian(lng);
lat = toRadian(lat);
// Miller Projection
// var x = lng;
// var y = 1.25 * Math.log(Math.tan(Math.PI / 4 + 0.4 * (lat)));
// Mercator Projection
// var x = lng;
// var y = Math.log(Math.tan(lat) + sec(lat));
var mapSet = {
leftLong: toRadian(-180),
rightLong: toRadian(180),
topLat: toRadian(90),
bottomLat: toRadian(-90),
imageWidth: 2057,
imageHeight: 1512,
}
var x = (lng - mapSet.leftLong) * (mapSet.imageWidth / (mapSet.rightLong - mapSet.leftLong));
var y = (mapSet.topLat - lat) * (mapSet.imageHeight / (mapSet.topLat - mapSet.bottomLat));
console.log(`Miller Projection X: ${x} -- Y: ${y}`);
return { x: x, y: y };
}
I'm using this picture as a map:
https://upload.wikimedia.org/wikipedia/commons/5/5f/Miller_projection_SW.jpg
Apparently If I use 0, 0 coordinates it marks the correct location.
If I give it any other coordinates it's not working. Can the map be the problem or maybe there is an issue with the logic I use?
This:
var x = (lng - mapSet.leftLong) * (mapSet.imageWidth / (mapSet.rightLong - mapSet.leftLong));
var y = (mapSet.topLat - lat) * (mapSet.imageHeight / (mapSet.topLat - mapSet.bottomLat));
Assumes a linear x and y transform for both latitude and longitude, but this only occurs for longitude. You can see the different spacings between latitudes in the image you reference.
Let's go back to your commented out projection and use that as it is correct, but needs scaling and translation:
function millerProjection(lat, lng) {
// Create sec() function
function sec(value) {
return 1/Math.cos(value);
}
// Create fucntion to change degree to radians
function toRadian(value) {
return value * Math.PI / 180;
}
lng = toRadian(lng);
lat = toRadian(lat);
// Miller Projection
var x = lng;
var y = 1.25*Math.log(Math.tan(Math.PI/4+0.4*(lat)));
return [x,y];
}
console.log(millerProjection(90,180));
console.log(millerProjection(-90,-180));
The output range for the x axis is -π to + π, and the y axis has a range about 0.733 times that.
Now we can scale and translate. I'll scale first and translate later, but vice versa is just as easy.
To scale, we need to know the width or height of the bounding box or output range. The aspect is fixed, so this is easiest if not specifying both, but rather determining one from the other. If we stretch the outputs unevenly, we no longer have a Miller.
Given a dimension for width we might scale with something like:
var scale = width / Math.PI / 2;
We want to see how many pixels are needed for each radian. Then we can multiple the projection output by the scale to get scaled values. Using the above, we can also validate our projection using a library like d3's geoprojection module:
function millerProjection(lat, lng) {
// Create sec() function
function sec(value) {
return 1/Math.cos(value);
}
// Create fucntion to change degree to radians
function toRadian(value) {
return value * Math.PI / 180;
}
lng = toRadian(lng);
lat = toRadian(lat);
// Miller Projection
var x = lng;
var y = -1.25*Math.log(Math.tan(Math.PI/4+0.4*(lat)));
var width = 360;
var scale = width/Math.PI/2;
x *= scale;
y *= scale;
return [x,y];
}
////
// set up reference scale:
var width = 360;
var height = width / Math.PI / 2.303412543376391; // aspect ratio
// set d3 projection for reference:
var d3Miller = d3.geoMiller()
.fitSize([360,180*0.7331989845], {"type": "Polygon","coordinates": [[[180, 90], [-180, 90], [-90, -180], [180, 90]]] })
.translate([0,0]);
// compare the two:
console.log("90N,180W:")
console.log("Miller: ", ...millerProjection(90,-180));
console.log("d3Miller:", ...d3Miller([-180,90]));
console.log("90S,180E:")
console.log("Miller: ",...millerProjection(-90,180));
console.log("d3Miller:", ...d3Miller([180,-90]));
console.log("90S,180E:")
console.log("Miller: ",...millerProjection(-57,162));
console.log("d3Miller:", ...d3Miller([162,-57]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
I've taken the negative of the latitude (based on your example) because while projected geographic coordinates have y = 0 at the bottom - with y values increasing as one moves up. Conversely, things like (most?) images have y = 0 at the top - with y values increasing as one moves down. D3 anticipates the latter convention so I did not do it for the reference function
Looks good. Our data now has a range of -width/2 to width/2 on the x axis, and again on the y, about 0.733 times that. Let's translate the data so that it occupies the bounding box of with a bottom left coordinate of [0,0] and a top right coordinate of [width,width*0.733]. This is fairly easy, after we scale the data, we add width/2 to the x value, and width/2*0.733 (or slightly more precisely, width/2/*0.7331989845) to the y value:
function millerProjection(lat, lng) {
// Create sec() function
function sec(value) {
return 1/Math.cos(value);
}
// Create fucntion to change degree to radians
function toRadian(value) {
return value * Math.PI / 180;
}
lng = toRadian(lng);
lat = toRadian(lat);
// Miller Projection
var x = lng;
var y = -1.25*Math.log(Math.tan(Math.PI/4+0.4*(lat)));
var width = 2057;
var scale = width/Math.PI/2;
x *= scale;
y *= scale;
x += width/2;
y += width/2*0.7331989845
return [x,y];
}
// compare the two:
console.log("90N,180W:")
console.log("Miller: ", ...millerProjection(90,-180));
console.log("90S,180E:")
console.log("Miller: ",...millerProjection(-90,180));
console.log("45N,45E:")
console.log("Miller: ",...millerProjection(45,45));
console.log("90S,180E:")
console.log("Miller: ",...millerProjection(-57,162));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
Of course, if you are doing image manipulation where the top should be y=0 and y values increase if you move down, flip the sign of the latitude before doing any manipulation.

iPhone opengl es: rotate a matrix about a point

I am doing some work with opengl es on the iPhone and I am stuck at a particular point. All the code samples on the internet show you how a matrix can be rotated about the x axis, y axis or the z axis but no one talks about how a matrix can be rotated about an arbitrary point?
I am using open gl es 2.0. Any help would be appreciated.
Regards,
It sounds like you're asking how to construct a matrix that rotates around one of those axes, but at a different point. The way you do that is to first translate to that point, and then apply the rotation for the axis you want. The order of multiplication of matrixes depends on whether you think of it as the axes moving or the geometry.
If you also want to be able to do a rotation of arbitrary x, y, z angle at the same time, you can use the matrix discussed in this article:
static inline void Matrix3DSetRotationByRadians(Matrix3D matrix, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
{
GLfloat mag = sqrtf((x*x) + (y*y) + (z*z));
if (mag == 0.0)
{
x = 1.0;
y = 0.0;
z = 0.0;
}
else if (mag != 1.0)
{
x /= mag;
y /= mag;
z /= mag;
}
GLfloat c = cosf(angle);
GLfloat s = fastSinf(angle);
matrix[3] = matrix[7] = matrix[11] = matrix[12] = matrix[13] = matrix[14] = 0.0;
matrix[15] = 1.0;
matrix[0] = (x*x)*(1-c) + c;
matrix[1] = (y*x)*(1-c) + (z*s);
matrix[2] = (x*z)*(1-c) - (y*s);
matrix[4] = (x*y)*(1-c)-(z*s);
matrix[5] = (y*y)*(1-c)+c;
matrix[6] = (y*z)*(1-c)+(x*s);
matrix[8] = (x*z)*(1-c)+(y*s);
matrix[9] = (y*z)*(1-c)-(x*s);
matrix[10] = (z*z)*(1-c)+c;
}