279 lines
7.4 KiB
JavaScript
279 lines
7.4 KiB
JavaScript
|
(function() {
|
||
|
|
||
|
// fixme: make all this work with different types of "points"
|
||
|
// i.e. {lat, lng}, [lat, lng]
|
||
|
|
||
|
function rad(point) {
|
||
|
return {
|
||
|
lat: Ox.rad(point.lat),
|
||
|
lng: Ox.rad(point.lng)
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/*@
|
||
|
Ox.crossesDateline <f> Returns true if a given rectangle crosses the dateline
|
||
|
@*/
|
||
|
Ox.crossesDateline = function(point0, point1) {
|
||
|
return point0.lng > point1.lng;
|
||
|
}
|
||
|
|
||
|
/*@
|
||
|
Ox.getArea <f> Returns the area in square meters of a given rectancle
|
||
|
@*/
|
||
|
Ox.getArea = function(point0, point1) {
|
||
|
/*
|
||
|
area of a ring between two latitudes:
|
||
|
2 * PI * r^2 * abs(sin(lat0) - sin(lat1))
|
||
|
see http://mathforum.org/library/drmath/view/63767.html
|
||
|
*/
|
||
|
/*
|
||
|
2 * Math.PI *
|
||
|
Math.pow(Ox.EARTH_RADIUS, 2) *
|
||
|
Math.abs(Math.sin(Ox.rad(0)) - Math.sin(Ox.rad(1))) *
|
||
|
Math.abs(Ox.rad(0) - Ox.rad(1)) /
|
||
|
(2 * Math.PI)
|
||
|
*/
|
||
|
if (Ox.crossesDateline(point0, point1)) {
|
||
|
point1.lng += 360;
|
||
|
}
|
||
|
var point0 = rad(point0),
|
||
|
point1 = rad(point1);
|
||
|
return Math.pow(Ox.EARTH_RADIUS, 2) *
|
||
|
Math.abs(Math.sin(point0.lat) - Math.sin(point1.lat)) *
|
||
|
Math.abs(point0.lng - point1.lng);
|
||
|
};
|
||
|
|
||
|
/*@
|
||
|
Ox.getBearing <f> Returns the bearing from one point to another
|
||
|
> Ox.getBearing({lat: -45, lng: 0}, {lat: 45, lng: 0})
|
||
|
0
|
||
|
@*/
|
||
|
Ox.getBearing = function(point0, point1) {
|
||
|
var point0 = rad(point0),
|
||
|
point1 = rad(point1),
|
||
|
x = Math.cos(point0.lat) * Math.sin(point1.lat) -
|
||
|
Math.sin(point0.lat) * Math.cos(point1.lat) *
|
||
|
Math.cos(point1.lng - point0.lng),
|
||
|
y = Math.sin(point1.lng - point0.lng) *
|
||
|
Math.cos(point1.lat);
|
||
|
return (Ox.deg(Math.atan2(y, x)) + 360) % 360;
|
||
|
};
|
||
|
|
||
|
/*@
|
||
|
Ox.getCenter <f> Returns the center of a recangle on a spehre
|
||
|
> Ox.getCenter({lat: -45, lng: -90}, {lat: 45, lng: 90})
|
||
|
{lat: 0, lng: 0}
|
||
|
@*/
|
||
|
Ox.getCenter = function(point0, point1) {
|
||
|
var point0 = rad(point0),
|
||
|
point1 = rad(point1),
|
||
|
x = Math.cos(point1.lat) *
|
||
|
Math.cos(point1.lng - point0.lng),
|
||
|
y = Math.cos(point1.lat) *
|
||
|
Math.sin(point1.lng - point0.lng),
|
||
|
d = Math.sqrt(
|
||
|
Math.pow(Math.cos(point0.lat) + x, 2) + Math.pow(y, 2)
|
||
|
),
|
||
|
lat = Ox.deg(
|
||
|
Math.atan2(Math.sin(point0.lat) + Math.sin(point1.lat), d)
|
||
|
),
|
||
|
lng = Ox.deg(
|
||
|
point0.lng + Math.atan2(y, Math.cos(point0.lat) + x)
|
||
|
);
|
||
|
return {lat: lat, lng: lng};
|
||
|
};
|
||
|
|
||
|
/*@
|
||
|
Ox.getDegreesPerMeter <f> Returns degrees per meter at a given latitude
|
||
|
> 360 / Ox.getDegreesPerMeter(0)
|
||
|
Ox.EARTH_CIRCUMFERENCE
|
||
|
@*/
|
||
|
Ox.getDegreesPerMeter = function(lat) {
|
||
|
return 360 / Ox.EARTH_CIRCUMFERENCE / Math.cos(lat * Math.PI / 180);
|
||
|
};
|
||
|
|
||
|
/*@
|
||
|
Ox.getDistance <f> Returns the distance in meters between two points
|
||
|
> Ox.getDistance({lat: -45, lng: -90}, {lat: 45, lng: 90}) * 2
|
||
|
Ox.EARTH_CIRCUMFERENCE
|
||
|
@*/
|
||
|
Ox.getDistance = function(point0, point1) {
|
||
|
var point0 = rad(point0),
|
||
|
point1 = rad(point1);
|
||
|
return Math.acos(
|
||
|
Math.sin(point0.lat) * Math.sin(point1.lat) +
|
||
|
Math.cos(point0.lat) * Math.cos(point1.lat) *
|
||
|
Math.cos(point1.lng - point0.lng)
|
||
|
) * Ox.EARTH_RADIUS;
|
||
|
};
|
||
|
|
||
|
/*@
|
||
|
Ox.getLatLngByXY <f> Returns lat/lng for a given x/y on a 1x1 mercator projection
|
||
|
> Ox.getLatLngByXY({x: 0.5, y: 0.5})
|
||
|
{lat: 0, lng: 0}
|
||
|
@*/
|
||
|
Ox.getLatLngByXY = function(xy) {
|
||
|
function getVal(val) {
|
||
|
return (val - 0.5) * 2 * Math.PI;
|
||
|
}
|
||
|
return {
|
||
|
lat: -Ox.deg(Math.atan(Ox.sinh(getVal(xy.y)))),
|
||
|
lng: Ox.deg(getVal(xy.x))
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/*@
|
||
|
Ox.getMetersPerDegree <f> Returns meters per degree at a given latitude
|
||
|
> Ox.getMetersPerDegree(0) * 360
|
||
|
Ox.EARTH_CIRCUMFERENCE
|
||
|
@*/
|
||
|
Ox.getMetersPerDegree = function(lat) {
|
||
|
return Math.cos(lat * Math.PI / 180) * Ox.EARTH_CIRCUMFERENCE / 360;
|
||
|
};
|
||
|
|
||
|
/*@
|
||
|
Ox.getXYByLatLng <f> Returns x/y on a 1x1 mercator projection for a given lat/lng
|
||
|
> Ox.getXYByLatLng({lat: 0, lng: 0})
|
||
|
{x: 0.5, y: 0.5}
|
||
|
@*/
|
||
|
Ox.getXYByLatLng = function(latlng) {
|
||
|
function getVal(val) {
|
||
|
return (val / (2 * Math.PI) + 0.5)
|
||
|
}
|
||
|
return {
|
||
|
x: getVal(Ox.rad(latlng.lng)),
|
||
|
y: getVal(Ox.asinh(Math.tan(Ox.rad(-latlng.lat))))
|
||
|
};
|
||
|
};
|
||
|
|
||
|
}());
|
||
|
|
||
|
//@ Ox.Line <f> (undocumented)
|
||
|
Ox.Line = function(point0, point1) {
|
||
|
|
||
|
var self = {
|
||
|
points: [point0, point1]
|
||
|
},
|
||
|
that = this;
|
||
|
|
||
|
function rad() {
|
||
|
return self.points.map(function(point) {
|
||
|
return {
|
||
|
lat: Ox.rad(point.lat()),
|
||
|
lng: Ox.rad(point.lng())
|
||
|
};
|
||
|
});
|
||
|
}
|
||
|
|
||
|
that.getArea = function() {
|
||
|
|
||
|
};
|
||
|
|
||
|
that.getBearing = function() {
|
||
|
};
|
||
|
|
||
|
that.getDistance = function() {
|
||
|
var points = rad();
|
||
|
return Math.acos(
|
||
|
Math.sin(point[0].lat) * Math.sin(point[1].lat) +
|
||
|
Math.cos(point[0].lat) * Math.cos(point[1].lat) *
|
||
|
Math.cos(point[1].lng - point[0].lng)
|
||
|
) * Ox.EARTH_RADIUS;
|
||
|
};
|
||
|
|
||
|
that.getMidpoint = function() {
|
||
|
var points = rad(),
|
||
|
x = Math.cos(point[1].lat) *
|
||
|
Math.cos(point[1].lng - point[0].lng),
|
||
|
y = Math.cos(point[1].lat) *
|
||
|
Math.sin(point[1].lng - point[0].lng),
|
||
|
d = Math.sqrt(
|
||
|
Math.pow(Math.cos(point[0].lat) + x, 2) + Math.pow(y, 2)
|
||
|
),
|
||
|
lat = Ox.deg(
|
||
|
Math.atan2(Math.sin(points[0].lat) + Math.sin(points[1].lat), d)
|
||
|
),
|
||
|
lng = Ox.deg(
|
||
|
points[0].lng + Math.atan2(y, math.cos(points[0].lat) + x)
|
||
|
);
|
||
|
return new Point(lat, lng);
|
||
|
};
|
||
|
|
||
|
that.points = function() {
|
||
|
|
||
|
};
|
||
|
|
||
|
return that;
|
||
|
|
||
|
};
|
||
|
|
||
|
//@ Ox.Point <f> (undocumented)
|
||
|
Ox.Point = function(lat, lng) {
|
||
|
|
||
|
var self = {lat: lat, lng: lng},
|
||
|
that = this;
|
||
|
|
||
|
that.lat = function() {
|
||
|
|
||
|
};
|
||
|
|
||
|
that.latlng = function() {
|
||
|
|
||
|
};
|
||
|
|
||
|
that.lng = function() {
|
||
|
|
||
|
};
|
||
|
|
||
|
that.getMetersPerDegree = function() {
|
||
|
return Math.cos(self.lat * Math.PI / 180) *
|
||
|
Ox.EARTH_CIRCUMFERENCE / 360;
|
||
|
}
|
||
|
|
||
|
that.getXY = function() {
|
||
|
return [
|
||
|
getXY(Ox.rad(self.lng)),
|
||
|
getXY(Ox.asinh(Math.tan(Ox.rad(-self.lat))))
|
||
|
];
|
||
|
};
|
||
|
|
||
|
return that;
|
||
|
|
||
|
};
|
||
|
|
||
|
//@ Ox.Rectangle <f> (undocumented)
|
||
|
Ox.Rectangle = function(point0, point1) {
|
||
|
|
||
|
var self = {
|
||
|
points: [
|
||
|
new Point(
|
||
|
Math.min(point0.lat(), point1.lat()),
|
||
|
point0.lng()
|
||
|
),
|
||
|
new Point(
|
||
|
Math.max(point0.lat(), point1.lat()),
|
||
|
point1.lng()
|
||
|
)
|
||
|
]
|
||
|
},
|
||
|
that = this;
|
||
|
|
||
|
that.contains = function(rectangle) {
|
||
|
|
||
|
}
|
||
|
|
||
|
that.crossesDateline = function() {
|
||
|
return self.points[0].lng > self.points[1].lng;
|
||
|
}
|
||
|
|
||
|
that.getCenter = function() {
|
||
|
return new Ox.Line(self.points[0], self.points[1]).getMidpoint();
|
||
|
};
|
||
|
|
||
|
that.intersects = function(rectangle) {
|
||
|
|
||
|
};
|
||
|
|
||
|
return that;
|
||
|
|
||
|
};
|