kopia lustrzana https://github.com/robhawkes/vizicities
207 wiersze
5.5 KiB
JavaScript
Executable File
207 wiersze
5.5 KiB
JavaScript
Executable File
import {latLon as LatLon} from './LatLon';
|
|
import {point as Point} from './Point';
|
|
|
|
var Geo = {};
|
|
|
|
// Radius / WGS84 semi-major axis
|
|
Geo.R = 6378137;
|
|
Geo.MAX_LATITUDE = 85.0511287798;
|
|
|
|
// WGS84 eccentricity
|
|
Geo.ECC = 0.081819191;
|
|
Geo.ECC2 = 0.081819191 * 0.081819191;
|
|
|
|
// Multiplier is used to expand or compress the WebGL coordinate space relative
|
|
// to the EPSG:3857 / Pseudo-Mercator coordinate space (metres).
|
|
//
|
|
// This is useful for keeping coordinate calculations to smaller numbers and
|
|
// helps increase accuracy of things like the z-buffer, object intersection,
|
|
// and camera near and far clipping.
|
|
//
|
|
// A multiplier of 1 would mean a 1:1 mapping between WebGL and EPSG:3857
|
|
// coordinates (1 EPSG:3857 metre === 1 WebGL unit)
|
|
//
|
|
// A multiplier of 0.1 would mean a 1:0.1 mapping between WebGL and EPSG:3857
|
|
// coordinates (1 EPSG:3857 metre === 0.1 WebGL units)
|
|
Geo.multiplier = 1;
|
|
|
|
Geo.project = function(latlon) {
|
|
var d = Math.PI / 180;
|
|
var max = Geo.MAX_LATITUDE;
|
|
var lat = Math.max(Math.min(max, latlon.lat), -max);
|
|
var sin = Math.sin(lat * d);
|
|
|
|
return Point(
|
|
Geo.R * latlon.lon * d,
|
|
Geo.R * Math.log((1 + sin) / (1 - sin)) / 2
|
|
);
|
|
},
|
|
|
|
Geo.unproject = function(point) {
|
|
var d = 180 / Math.PI;
|
|
|
|
return LatLon(
|
|
(2 * Math.atan(Math.exp(point.y / Geo.R)) - (Math.PI / 2)) * d,
|
|
point.x * d / Geo.R
|
|
);
|
|
};
|
|
|
|
// Converts geo coords to pixel / WebGL ones
|
|
// This just reverses the Y axis to match WebGL
|
|
Geo.latLonToPoint = function(latlon) {
|
|
var projected = Geo.project(latlon);
|
|
projected.y *= -1;
|
|
|
|
projected.x *= Geo.multiplier;
|
|
projected.y *= Geo.multiplier;
|
|
|
|
return projected;
|
|
};
|
|
|
|
// Converts pixel / WebGL coords to geo coords
|
|
// This just reverses the Y axis to match WebGL
|
|
Geo.pointToLatLon = function(point) {
|
|
var _point = Point(point.x, point.y * -1);
|
|
|
|
_point.x /= Geo.multiplier;
|
|
_point.y /= Geo.multiplier;
|
|
|
|
return Geo.unproject(_point);
|
|
};
|
|
|
|
// Scale factor for converting between real metres and projected metres
|
|
//
|
|
// projectedMetres = realMetres * pointScale
|
|
// realMetres = projectedMetres / pointScale
|
|
//
|
|
// Accurate scale factor uses proper Web Mercator scaling
|
|
// See pg.9: http://www.hydrometronics.com/downloads/Web%20Mercator%20-%20Non-Conformal,%20Non-Mercator%20(notes).pdf
|
|
// See: http://jsfiddle.net/robhawkes/yws924cf/
|
|
Geo.pointScale = function(latlon, accurate) {
|
|
var rad = Math.PI / 180;
|
|
|
|
var k;
|
|
|
|
if (!accurate) {
|
|
k = 1 / Math.cos(latlon.lat * rad);
|
|
|
|
// [scaleX, scaleY]
|
|
return [k, k];
|
|
} else {
|
|
var lat = latlon.lat * rad;
|
|
var lon = latlon.lon * rad;
|
|
|
|
var a = Geo.R;
|
|
|
|
var sinLat = Math.sin(lat);
|
|
var sinLat2 = sinLat * sinLat;
|
|
|
|
var cosLat = Math.cos(lat);
|
|
|
|
// Radius meridian
|
|
var p = a * (1 - Geo.ECC2) / Math.pow(1 - Geo.ECC2 * sinLat2, 3 / 2);
|
|
|
|
// Radius prime meridian
|
|
var v = a / Math.sqrt(1 - Geo.ECC2 * sinLat2);
|
|
|
|
// Scale N/S
|
|
var h = (a / p) / cosLat;
|
|
|
|
// Scale E/W
|
|
k = (a / v) / cosLat;
|
|
|
|
// [scaleX, scaleY]
|
|
return [k, h];
|
|
}
|
|
};
|
|
|
|
// Convert real metres to projected units
|
|
//
|
|
// Latitude scale is chosen because it fluctuates more than longitude
|
|
Geo.metresToProjected = function(metres, pointScale) {
|
|
return metres * pointScale[1];
|
|
};
|
|
|
|
// Convert projected units to real metres
|
|
//
|
|
// Latitude scale is chosen because it fluctuates more than longitude
|
|
Geo.projectedToMetres = function(projectedUnits, pointScale) {
|
|
return projectedUnits / pointScale[1];
|
|
};
|
|
|
|
// Convert real metres to a value in world (WebGL) units
|
|
Geo.metresToWorld = function(metres, pointScale) {
|
|
// Transform metres to projected metres using the latitude point scale
|
|
//
|
|
// Latitude scale is chosen because it fluctuates more than longitude
|
|
var projectedMetres = Geo.metresToProjected(metres, pointScale);
|
|
return projectedMetres * Geo.multiplier;
|
|
};
|
|
|
|
// Convert world (WebGL) units to a value in real metres
|
|
Geo.worldToMetres = function(worldUnits, pointScale) {
|
|
var projectedUnits = worldUnits;
|
|
var realMetres = Geo.projectedToMetres(projectedUnits, pointScale);
|
|
|
|
return realMetres / Geo.multiplier;
|
|
};
|
|
|
|
// Returns the world width in pixels for a given zoom, assuming tile dimensions
|
|
// of 256x256 pixels
|
|
Geo.scale = function(zoom) {
|
|
return 256 * Math.pow(2, zoom);
|
|
};
|
|
|
|
// Returns zoom level for a given scale value
|
|
// This only works with a scale value that is based on map pixel width
|
|
Geo.zoom = function(scale) {
|
|
return Math.log(scale / 256) / Math.LN2;
|
|
};
|
|
|
|
// Distance between two geographical points using spherical law of cosines
|
|
// approximation or Haversine
|
|
//
|
|
// See: http://www.movable-type.co.uk/scripts/latlong.html
|
|
Geo.distance = function(latlon1, latlon2, accurate) {
|
|
var rad = Math.PI / 180;
|
|
|
|
var lat1;
|
|
var lat2;
|
|
|
|
var a;
|
|
|
|
if (!accurate) {
|
|
lat1 = latlon1.lat * rad;
|
|
lat2 = latlon2.lat * rad;
|
|
|
|
a = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlon2.lon - latlon1.lon) * rad);
|
|
|
|
return Geo.R * Math.acos(Math.min(a, 1));
|
|
} else {
|
|
lat1 = latlon1.lat * rad;
|
|
lat2 = latlon2.lat * rad;
|
|
|
|
var lon1 = latlon1.lon * rad;
|
|
var lon2 = latlon2.lon * rad;
|
|
|
|
var deltaLat = lat2 - lat1;
|
|
var deltaLon = lon2 - lon1;
|
|
|
|
var halfDeltaLat = deltaLat / 2;
|
|
var halfDeltaLon = deltaLon / 2;
|
|
|
|
a = Math.sin(halfDeltaLat) * Math.sin(halfDeltaLat) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(halfDeltaLon) * Math.sin(halfDeltaLon);
|
|
|
|
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
|
|
return Geo.R * c;
|
|
}
|
|
};
|
|
|
|
Geo.bounds = (function() {
|
|
var d = Geo.R * Math.PI * Geo.multiplier;
|
|
return [[-d, -d], [d, d]];
|
|
})();
|
|
|
|
export default Geo;
|