JavaScript calculate surface area - javascript

I am trying to code with JavaScript a function to calculate the surface area of a sphere. The radius using a NumericUpDown.
I have tried to use what I did in a different assignment and it is not working like the other did. I am new at this and have not had any help.
function calcSurface() {
var radius = document.getElementByID("radius").value;
radius = math.abs(radius);
Surface = 4*Math.PI*Math.pow(radius);
}

Math.abs() needs a capital M, and getElementById() needs a lower-case d. Math.pow takes two arguments, the base and the exponent, while you're only supplying one: the radius (which is the base).
Try this:
function calcSurface() {
var radius = document.getElementById("radius").value;
radius = Math.abs(radius);
var surface= 4 * Math.PI * Math.pow(radius, 2);
}

You can use this function
function calculateSurface() {
let radius = document.getElementById("radius").value;
radius = Math.abs(radius);
return 4 * Math.PI * Math.pow(radius, 2);
}
And if you have some div in your html like
<div id='result'></div>
You can do this to get the result in your div
document.getElementById('result').innerText = calculateSurface();

Related

calculating intersection point of quadratic bezier curve

This is definitely pushing the limits for my trig knowledge.
Is there a formula for calculating an intersection point between a quadratic bezier curve and a line?
Example:
in the image below, I have P1, P2, C (which is the control point) and X1, X2 (which for my particular calculation is just a straight line on the X axis.)
What I would like to be able to know is the X,Y position of T as well as the angle of the tangent at T. at the intersection point between the red curve and the black line.
After doing a little research and finding this question, I know I can use:
t = 0.5; // given example value
x = (1 - t) * (1 - t) * p[0].x + 2 * (1 - t) * t * p[1].x + t * t * p[2].x;
y = (1 - t) * (1 - t) * p[0].y + 2 * (1 - t) * t * p[1].y + t * t * p[2].y;
to calculate my X,Y position at any given point along the curve. So using that I could just loop through a bunch of points along the curve, checking to see if any are on my intersecting X axis. And from there try to calculate my tangent angle. But that really doesn't seem like the best way to do it. Any math guru's out there know what the best way is?
I'm thinking that perhaps it's a bit more complicated than I want it to be.
If you only need an intersection with a straight line in the x-direction you already know the y-coordinate of the intersection. To get the x-coordinate do something like this:
The equation for your line is simply y = b
Setting it equal to your y-equation of the beziér function y(t) gets you:
b = (1 - t) * (1 - t) * p[0].y + 2 * (1 - t) * t * p[1].y + t * t * p[2].y
Solving* for t gets you:
t = (p[0].y - p[1].y - sqrt(b*a + p[1].y*p[1].y - p[0].y*p[2].y)) / a
with a = p[0].y - 2*p[1].y + p[2].y
Insert the resulting t into your x-equation of the beziér function x(t) to get the x-coordinate and you're done.
You may have to pay attention to some special cases, like when no solution exists, because the argument of the square root may then become negative or the denominator (a) might become zero, or something like that.
Leave a comment if you need more help or the intersection with arbitrary lines.
(*) I used wolfram alpha to solve the equation because I'm lazy: Wolfram alpha solution.
Quadratic curve formula:
y=ax^2+bx+c // where a,b,c are known
Line formula:
// note: this `B` is not the same as the `b` in the quadratic formula ;-)
y=m*x+B // where m,B are known.
The curve & line intersect where both equations are true for the same [x,y]:
Here's annotated code and a Demo:
// canvas vars
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// linear interpolation utility
var lerp=function(a,b,x){ return(a+x*(b-a)); };
// qCurve & line defs
var p1={x:125,y:200};
var p2={x:250,y:225};
var p3={x:275,y:100};
var a1={x:30,y:125};
var a2={x:300,y:175};
// calc the intersections
var points=calcQLintersects(p1,p2,p3,a1,a2);
// plot the curve, line & solution(s)
var textPoints='Intersections: ';
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
ctx.quadraticCurveTo(p2.x,p2.y,p3.x,p3.y);
ctx.moveTo(a1.x,a1.y);
ctx.lineTo(a2.x,a2.y);
ctx.stroke();
ctx.beginPath();
for(var i=0;i<points.length;i++){
var p=points[i];
ctx.moveTo(p.x,p.y);
ctx.arc(p.x,p.y,4,0,Math.PI*2);
ctx.closePath();
textPoints+=' ['+parseInt(p.x)+','+parseInt(p.y)+']';
}
ctx.font='14px verdana';
ctx.fillText(textPoints,10,20);
ctx.fillStyle='red';
ctx.fill();
///////////////////////////////////////////////////
function calcQLintersects(p1, p2, p3, a1, a2) {
var intersections=[];
// inverse line normal
var normal={
x: a1.y-a2.y,
y: a2.x-a1.x,
}
// Q-coefficients
var c2={
x: p1.x + p2.x*-2 + p3.x,
y: p1.y + p2.y*-2 + p3.y
}
var c1={
x: p1.x*-2 + p2.x*2,
y: p1.y*-2 + p2.y*2,
}
var c0={
x: p1.x,
y: p1.y
}
// Transform to line
var coefficient=a1.x*a2.y-a2.x*a1.y;
var a=normal.x*c2.x + normal.y*c2.y;
var b=(normal.x*c1.x + normal.y*c1.y)/a;
var c=(normal.x*c0.x + normal.y*c0.y + coefficient)/a;
// solve the roots
var roots=[];
d=b*b-4*c;
if(d>0){
var e=Math.sqrt(d);
roots.push((-b+Math.sqrt(d))/2);
roots.push((-b-Math.sqrt(d))/2);
}else if(d==0){
roots.push(-b/2);
}
// calc the solution points
for(var i=0;i<roots.length;i++){
var minX=Math.min(a1.x,a2.x);
var minY=Math.min(a1.y,a2.y);
var maxX=Math.max(a1.x,a2.x);
var maxY=Math.max(a1.y,a2.y);
var t = roots[i];
if (t>=0 && t<=1) {
// possible point -- pending bounds check
var point={
x:lerp(lerp(p1.x,p2.x,t),lerp(p2.x,p3.x,t),t),
y:lerp(lerp(p1.y,p2.y,t),lerp(p2.y,p3.y,t),t)
}
var x=point.x;
var y=point.y;
// bounds checks
if(a1.x==a2.x && y>=minY && y<=maxY){
// vertical line
intersections.push(point);
}else if(a1.y==a2.y && x>=minX && x<=maxX){
// horizontal line
intersections.push(point);
}else if(x>=minX && y>=minY && x<=maxX && y<=maxY){
// line passed bounds check
intersections.push(point);
}
}
}
return intersections;
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<h4>Calculate intersections of QBez-Curve and Line</h4>
<canvas id="canvas" width=350 height=350></canvas>
calculate line's tangθ with x-coordinate
then intersection of the curve's (x, y) should be the same tangθ
so solution is
a = line's x distance from (line.x,0) to (0,0)
(curve.x + a) / curve.y = tangθ (θ can get from the line intersection with x-coordidate)

Calculating the Right Ascension and Declination of the Sun

I've been following this guide, porting it to javascript:
http://www.saao.ac.za/public-info/sun-moon-stars/sun-index/how-to-calculate-altaz/
Everything was going swimmingly up until 9.(right ascension) and 10.(declination). I can't recreate the answers they give for these.
(9) find alpha the right ascension of the sun:
(a) for cape town:
lambda = 326.186
epsilon = 23.4396
alpha = arctan (tan(lambda) x cos(epsilon)) // in same quadrant as lambda
// THEIR RESULT
alpha = 328.428
// MY RESULT
var DEGREES = function (val) {
return val / (Math.PI / 180);
};
var alpha = Math.atan(Math.tan(lambda) * Math.sin(epsilon));
alpha = 0.495;
alpha = DEGREES(0.495) = 28.39;
I also tried:
var alpha = Math.atan2(Math.tan(lambda) * Math.sin(epsilon), lambda);
alpha = DEGREES(result) = 1.321;
Not even close!
And 10(a), the declination
delta = arcsin (sin(lambda) x sin(epsilon))
// THEIR RESULT
(a) delta = -12.789
// MY RESULT
var result = Math.asin(Math.sin(eclipticLong) * Math.sin(obliq));
result = DEGREES(result);
result = -10.966;
As you can see I'm clutching at straws as I don't really have a clue about this. Any help would be much appreciated.
Well, the biggest issue I see is here:
alpha = arctan (tan(lambda) x cos(epsilon)) // in same quadrant as lambda
...
var alpha = Math.atan(Math.tan(lambda) * Math.sin(epsilon));
You went from using cosine to sine in the second expression.
Now, this on the face of it doesn't lead to the same result, so lets dig in a bit deeper. For clarity, I'm going to use these functions and constants:
var lambda = 326.186;
var epsilon = 23.4396;
function rad (v) { return v * Math.PI / 180 }
function deg (v) { return v * 180 / Math.PI }
Javascript math functions take a radial coordinate, so lets give this a try:
var result = deg(Math.atan(Math.tan(rad(lambda)) * Math.cos(rad(epsilon))));
console.log(result); // -.31.5717
Thanks to the magic of how degrees work, this is the same answer as 360 + -31.5717 = 328.428.

Google Maps - How to get the distance between two point in metre?

I have these coordinate :
(45.463688, 9.18814)
(46.0438317, 9.75936230000002)
and I need (trought Google API V3, I think) to get the distance between those 2 points in metre. How can I do it?
If you're looking to use the v3 google maps API, here is a function to use:
Note: you must add &libraries=geometry to your script source
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry"></script>
<script>
var p1 = new google.maps.LatLng(45.463688, 9.18814);
var p2 = new google.maps.LatLng(46.0438317, 9.75936230000002);
alert(calcDistance(p1, p2));
//calculates distance between two points in km's
function calcDistance(p1, p2) {
return (google.maps.geometry.spherical.computeDistanceBetween(p1, p2) / 1000).toFixed(2);
}
</script>
I think you could do without any specific API, and calculate distance with plain Javascript:
This site has good info about geographical calculations and Javascript sample for distance calculation.
Ok, quick glance at Google API page and it seems, you could do it by:
Call DirectionsService().route() to get DirectionsResult with routes
For one or each route go through its legs property and calculate sum of distances
http://www.csgnetwork.com/gpsdistcalc.html
Has nothing to do with coding btw
EDIT
if you're looking for some code
Calculate distance between two points in google maps V3
This is primarily done with math outside of the google api, so you shouldn't need to use the API for anything other than finding the correct coordinates.
Knowing that, here's a link to a previously answered question relating to Javascript.
Calculate distance between 2 GPS coordinates
Try this
CLLocationCoordinate2D coord1;
coord1.latitude = 45.463688;
coord1.longitude = 9.18814;
CLLocation *loc1 = [[[CLLocation alloc] initWithLatitude:coord1.latitude longitude:coord1.longitude] autorelease];
CLLocationCoordinate2D coord2;
coord2.latitude = 46.0438317;
coord2.longitude = 9.75936230000002;
CLLocation *loc2 = [[[CLLocation alloc] initWithLatitude:46.0438317 longitude:9.75936230000002] autorelease];
CLLocationDistance d1 = [loc1 distanceFromLocation:loc2];
Try this:
const const toRadians = (val) => {
return val * Math.PI / 180;
}
const toDegrees = (val) => {
return val * 180 / Math.PI;
}
// Calculate a point winthin a circle
// circle ={center:LatLong, radius: number} // in metres
const pointInsideCircle = (point, circle) => {
let center = circle.center;
let distance = distanceBetween(point, center);
//alert(distance);
return distance < circle.radius;
};
const distanceBetween = (point1, point2) => {
//debugger;
var R = 6371e3; // metres
var φ1 = toRadians(point1.latitude);
var φ2 = toRadians(point2.latitude);
var Δφ = toRadians(point2.latitude - point1.latitude);
var Δλ = toRadians(point2.longitude - point1.longitude);
var a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
References: http://www.movable-type.co.uk/scripts/latlong.html
Are you referring to distance as in length of the entire path or you want to know only the displacement (straight line distance)? I see no one is pointing the difference between distance and displacement here. For distance calculate each route point given by JSON/XML data, as for displacement there is a built-in solution using Spherical class.
Are you referring to distance as in length of the entire path or you want to know only the displacement (straight line distance)? I see no one is pointing the difference between distance and displacement here. For distance calculate each route point given by JSON/XML data, as for displacement there is a built-in solution using Spherical class. The idea is if your are referring to distance then you just use the computeDistanceBetween on each path point and concatenate it.
//calculates distance between two points in km's
function calcDistance(p1, p2) {
return (google.maps.geometry.spherical.computeDistanceBetween(p1, p2) / 1000).toFixed(2);
}

Find column, row on 2D isometric grid from x,y screen space coords (Convert equation to function)

I'm trying to find the row, column in a 2d isometric grid of a screen space point (x, y)
Now I pretty much know what I need to do which is find the length of the vectors in red in the pictures above and then compare it to the length of the vector that represent the bounds of the grid (which is represented by the black vectors)
Now I asked for help over at mathematics stack exchange to get the equation for figuring out what the parallel vectors are of a point x,y compared to the black boundary vectors. Link here Length of Perpendicular/Parallel Vectors
but im having trouble converting this to a function
Ideally i need enough of a function to get the length of both red vectors from three sets of points, the x,y of the end of the 2 black vectors and the point at the end of the red vectors.
Any language is fine but ideally javascript
What you need is a base transformation:
Suppose the coordinates of the first black vector are (x1, x2) and the coordinates of the second vector are (y1, y2).
Therefore, finding the red vectors that get at a point (z1, z2) is equivalent to solving the following linear system:
x1*r1 + y1*r2 = z1
x2*r1 + y2*r2 = z2
or in matrix form:
A x = b
/x1 y1\ |r1| = |z1|
\x2 y2/ |r2| |z2|
x = inverse(A)*b
For example, lets have the black vector be (2, 1) and (2, -1). The corresponding matrix A will be
2 2
1 -1
and its inverse will be
1/4 1/2
1/4 -1/2
So a point (x, y) in the original coordinates will be able to be represened in the alternate base, bia the following formula:
(x, y) = (1/4 * x + 1/2 * y)*(2,1) + (1/4 * x -1/2 * y)*(2, -1)
What exactly is the point of doing it like this? Any isometric grid you display usually contains cells of equal size, so you can skip all the vector math and simply do something like:
var xStep = 50,
yStep = 30, // roughly matches your image
pointX = 2*xStep,
pointY = 0;
Basically the points on any isometric grid fall onto the intersections of a non-isometric grid. Isometric grid controller:
screenPositionToIsoXY : function(o, w, h){
var sX = ((((o.x - this.canvas.xPosition) - this.screenOffsetX) / this.unitWidth ) * 2) >> 0,
sY = ((((o.y - this.canvas.yPosition) - this.screenOffsetY) / this.unitHeight) * 2) >> 0,
isoX = ((sX + sY - this.cols) / 2) >> 0,
isoY = (((-1 + this.cols) - (sX - sY)) / 2) >> 0;
// isoX = ((sX + sY) / isoGrid.width) - 1
// isoY = ((-2 + isoGrid.width) - sX - sY) / 2
return $.extend(o, {
isoX : Math.constrain(isoX, 0, this.cols - (w||0)),
isoY : Math.constrain(isoY, 0, this.rows - (h||0))
});
},
// ...
isoToUnitGrid : function(isoX, isoY){
var offset = this.grid.offset(),
isoX = $.uD(isoX) ? this.isoX : isoX,
isoY = $.uD(isoY) ? this.isoY : isoY;
return {
x : (offset.x + (this.grid.unitWidth / 2) * (this.grid.rows - this.isoWidth + isoX - isoY)) >> 0,
y : (offset.y + (this.grid.unitHeight / 2) * (isoX + isoY)) >> 0
};
},
Okay so with the help of other answers (sorry guys neither quite provided the answer i was after)
I present my function for finding the grid position on an iso 2d grid using a world x,y coordinate where the world x,y is an offset screen space coord.
WorldPosToGridPos: function(iPosX, iPosY){
var d = (this.mcBoundaryVectors.upper.x * this.mcBoundaryVectors.lower.y) - (this.mcBoundaryVectors.upper.y * this.mcBoundaryVectors.lower.x);
var a = ((iPosX * this.mcBoundaryVectors.lower.y) - (this.mcBoundaryVectors.lower.x * iPosY)) / d;
var b = ((this.mcBoundaryVectors.upper.x * iPosY) - (iPosX * this.mcBoundaryVectors.upper.y)) / d;
var cParaUpperVec = new Vector2(a * this.mcBoundaryVectors.upper.x, a * this.mcBoundaryVectors.upper.y);
var cParaLowerVec = new Vector2(b * this.mcBoundaryVectors.lower.x, b * this.mcBoundaryVectors.lower.y);
var iGridWidth = 40;
var iGridHeight = 40;
var iGridX = Math.floor((cParaLowerVec.length() / this.mcBoundaryVectors.lower.length()) * iGridWidth);
var iGridY = Math.floor((cParaUpperVec.length() / this.mcBoundaryVectors.upper.length()) * iGridHeight);
return {gridX: iGridX, gridY: iGridY};
},
The first line is best done once in an init function or similar to save doing the same calculation over and over, I just included it for completeness.
The mcBoundaryVectors are two vectors defining the outer limits of the x and y axis of the isometric grid (The black vectors shown in the picture above).
Hope this helps anyone else in the future

Mercator longitude and latitude calculations to x and y on a cropped map (of the UK)

I have this image. It's a map of the UK (not including Southern Ireland):
I have successfully managed to get a latitude and longitude and plot it onto this map by taking the leftmost longitude and rightmost longitude of the UK and using them to work out where to put the point on the map.
This is the code (for use in Processing.js but could be used as js or anything):
// Size of the map
int width = 538;
int height = 811;
// X and Y boundaries
float westLong = -8.166667;
float eastLong = 1.762833;
float northLat = 58.666667;
float southLat = 49.95;
void drawPoint(float latitude, float longitude){
fill(#000000);
x = width * ((westLong-longitude)/(westLong-eastLong));
y = (height * ((northLat-latitude)/(northLat-southLat)));
console.log(x + ", " + y);
ellipseMode(RADIUS);
ellipse(x, y, 2, 2);
}
However, I haven't been able to implement a Mercator projection on these values. The plots are reasonably accurate but they are not good enough and this projection would solve it.
I can't figure out how to do it. All the examples I find are explaining how to do it for the whole world. This is a good resource of examples explaining how to implement the projection but I haven't been able to get it to work.
Another resource is the Extreme points of the United Kingdom where I got the latitude and longitude values of the bounding box around the UK. They are also here:
northLat = 58.666667;
northLong = -3.366667;
eastLat = 52.481167;
eastLong = 1.762833;
southLat = 49.95;
southLong = -5.2;
westLat = 54.45;
westLong = -8.166667;
If anyone could help me with this, I would greatly appreciate it!
Thanks
I wrote a function which does exactly what you were looking for. I know it's a bit late, but maybe there are some other people interested in.
You need a map which is a mercator projection and you need to know the lat / lon positions of your map.
You get great customized mercator maps with perfect matching lat / lon positions from TileMill which is a free software from MapBox!
I'm using this script and tested it with some google earth positions. It worked perfect on a pixel level. Actually I didnt test this on different or larger maps. I hope it helps you!
Raphael ;)
<?php
$mapWidth = 1500;
$mapHeight = 1577;
$mapLonLeft = 9.8;
$mapLonRight = 10.2;
$mapLonDelta = $mapLonRight - $mapLonLeft;
$mapLatBottom = 53.45;
$mapLatBottomDegree = $mapLatBottom * M_PI / 180;
function convertGeoToPixel($lat, $lon)
{
global $mapWidth, $mapHeight, $mapLonLeft, $mapLonDelta, $mapLatBottom, $mapLatBottomDegree;
$x = ($lon - $mapLonLeft) * ($mapWidth / $mapLonDelta);
$lat = $lat * M_PI / 180;
$worldMapWidth = (($mapWidth / $mapLonDelta) * 360) / (2 * M_PI);
$mapOffsetY = ($worldMapWidth / 2 * log((1 + sin($mapLatBottomDegree)) / (1 - sin($mapLatBottomDegree))));
$y = $mapHeight - (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);
return array($x, $y);
}
$position = convertGeoToPixel(53.7, 9.95);
echo "x: ".$position[0]." / ".$position[1];
?>
Here is the image I created with TileMill and which I used in this example:
In addition to what Raphael Wichmann has posted (Thanks, by the way!),
here is the reverse function, in actionscript :
function convertPixelToGeo(tx:Number, ty:Number):Point
{
/* called worldMapWidth in Raphael's Code, but I think that's the radius since it's the map width or circumference divided by 2*PI */
var worldMapRadius:Number = mapWidth / mapLonDelta * 360/(2 * Math.PI);
var mapOffsetY:Number = ( worldMapRadius / 2 * Math.log( (1 + Math.sin(mapLatBottomRadian) ) / (1 - Math.sin(mapLatBottomRadian)) ));
var equatorY:Number = mapHeight + mapOffsetY;
var a:Number = (equatorY-ty)/worldMapRadius;
var lat:Number = 180/Math.PI * (2 * Math.atan(Math.exp(a)) - Math.PI/2);
var long:Number = mapLonLeft+tx/mapWidth*mapLonDelta;
return new Point(lat,long);
}
Here's another Javascript implementation. This is a simplification of #Rob Willet's solution above. Instead of requiring computed values as parameters to the function, it only requires essential values and computes everything from them:
function convertGeoToPixel(latitude, longitude,
mapWidth, // in pixels
mapHeight, // in pixels
mapLngLeft, // in degrees. the longitude of the left side of the map (i.e. the longitude of whatever is depicted on the left-most part of the map image)
mapLngRight, // in degrees. the longitude of the right side of the map
mapLatBottom) // in degrees. the latitude of the bottom of the map
{
const mapLatBottomRad = mapLatBottom * Math.PI / 180
const latitudeRad = latitude * Math.PI / 180
const mapLngDelta = (mapLngRight - mapLngLeft)
const worldMapWidth = ((mapWidth / mapLngDelta) * 360) / (2 * Math.PI)
const mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomRad)) / (1 - Math.sin(mapLatBottomRad))))
const x = (longitude - mapLngLeft) * (mapWidth / mapLngDelta)
const y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitudeRad)) / (1 - Math.sin(latitudeRad)))) - mapOffsetY)
return {x, y} // the pixel x,y value of this point on the map image
}
I've converted the PHP code provided by Raphael to JavaScript and can confirm it worked and this code works myself. All credit to Raphael.
/*
var mapWidth = 1500;
var mapHeight = 1577;
var mapLonLeft = 9.8;
var mapLonRight = 10.2;
var mapLonDelta = mapLonRight - mapLonLeft;
var mapLatBottom = 53.45;
var mapLatBottomDegree = mapLatBottom * Math.PI / 180;
*/
function convertGeoToPixel(latitude, longitude ,
mapWidth , // in pixels
mapHeight , // in pixels
mapLonLeft , // in degrees
mapLonDelta , // in degrees (mapLonRight - mapLonLeft);
mapLatBottom , // in degrees
mapLatBottomDegree) // in Radians
{
var x = (longitude - mapLonLeft) * (mapWidth / mapLonDelta);
latitude = latitude * Math.PI / 180;
var worldMapWidth = ((mapWidth / mapLonDelta) * 360) / (2 * Math.PI);
var mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomDegree)) / (1 - Math.sin(mapLatBottomDegree))));
var y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitude)) / (1 - Math.sin(latitude)))) - mapOffsetY);
return { "x": x , "y": y};
}
I think it's worthwhile to keep in mind that not all flat maps are Mercator projections. Without knowing more about that map in particular, it's hard to be sure. You may find that most maps of a small area of the world are more likely to be a conical type projection, where the area of interest on the map is "flatter" than would be on a global Mercator projection. This is especially more important the further you get away from the equator (and the UK is far enough away for it to matter).
You may be able to get "close enough" using the calculations you're trying, but for best accuracy you may want to either use a map with a well-defined projection, or create your own map.
I know the question was asked a while ago, but the Proj4JS library is ideal for transforming between different map projections in JavaScript.
UK maps tend to use the OSGB's National Grid which is based on a Transverse Mercator projection. Ie. like a conventional Mercator but turned 90 degrees, so that the "equator" becomes a meridian.
#Xarinko Actionscript snippet in Javascript (with some testing values)
var mapWidth = 1500;
var mapHeight = 1577;
var mapLonLeft = 9.8;
var mapLonRight = 10.2;
var mapLonDelta = mapLonRight - mapLonLeft;
var mapLatBottom = 53.45;
var mapLatBottomRadian = mapLatBottom * Math.PI / 180;
function convertPixelToGeo(tx, ty)
{
/* called worldMapWidth in Raphael's Code, but I think that's the radius since it's the map width or circumference divided by 2*PI */
var worldMapRadius = mapWidth / mapLonDelta * 360/(2 * Math.PI);
var mapOffsetY = ( worldMapRadius / 2 * Math.log( (1 + Math.sin(mapLatBottomRadian) ) / (1 - Math.sin(mapLatBottomRadian)) ));
var equatorY = mapHeight + mapOffsetY;
var a = (equatorY-ty)/worldMapRadius;
var lat = 180/Math.PI * (2 * Math.atan(Math.exp(a)) - Math.PI/2);
var long = mapLonLeft+tx/mapWidth*mapLonDelta;
return [lat,long];
}
convertPixelToGeo(241,444)
C# implementation:
private Point ConvertGeoToPixel(
double latitude, double longitude, // The coordinate to translate
int imageWidth, int imageHeight, // The dimensions of the target space (in pixels)
double mapLonLeft, double mapLonRight, double mapLatBottom // The bounds of the target space (in geo coordinates)
) {
double mapLatBottomRad = mapLatBottom * Math.PI / 180;
double latitudeRad = latitude * Math.PI / 180;
double mapLonDelta = mapLonRight - mapLonLeft;
double worldMapWidth = (imageWidth / mapLonDelta * 360) / (2 * Math.PI);
double mapOffsetY = worldMapWidth / 2 * Math.Log((1 + Math.Sin(mapLatBottomRad)) / (1 - Math.Sin(mapLatBottomRad)));
double x = (longitude - mapLonLeft) * (imageWidth / mapLonDelta);
double y = imageHeight - ((worldMapWidth / 2 * Math.Log((1 + Math.Sin(latitudeRad)) / (1 - Math.Sin(latitudeRad)))) - mapOffsetY);
return new Point()
{
X = Convert.ToInt32(x),
Y = Convert.ToInt32(y)
};
}
If you want to avoid some of the messier aspects of lat/lng projections intrinsic to Proj4JS, you can use D3, which offers many baked-in projections and renders beautifully. Here's an interactive example of several flavors of Azimuthal projections. I prefer Albers for USA maps.
If D3 is not an end-user option -- say, you need to support IE 7/8 -- you can render in D3 and then snag the xy coordinates from the resultant SVG file that D3 generates. You can then render those xy coordinates in Raphael.
This function works great for me because I want to define the mapHeight based on the map I want to plot. I'm generating PDF maps. All I need to do is pass in the map's max Lat , min Lon and it returns the pixels size for the map as [height,width].
convertGeoToPixel(maxlatitude, maxlongitude)
One note in the final step where $y is set, do not subtract the calculation from the mapHeight if your coordinate system 'xy' starts at the bottom/left , like with PDFs, this will invert the map.
$y = (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);

Categories