Replace ol/Sphere with ol/sphere

This commit is contained in:
Tim Schaub
2017-12-20 08:44:01 -08:00
parent 43342cd133
commit 54b8622114
9 changed files with 330 additions and 402 deletions

View File

@@ -4,14 +4,12 @@
import {inherits} from './index.js';
import _ol_GeolocationProperty_ from './GeolocationProperty.js';
import _ol_Object_ from './Object.js';
import _ol_Sphere_ from './Sphere.js';
import _ol_events_ from './events.js';
import EventType from './events/EventType.js';
import Polygon from './geom/Polygon.js';
import _ol_has_ from './has.js';
import {toRadians} from './math.js';
import {get as getProjection, getTransformFromProjections, identityTransform} from './proj.js';
import _ol_proj_EPSG4326_ from './proj/EPSG4326.js';
/**
@@ -74,12 +72,6 @@ var Geolocation = function(opt_options) {
*/
this.transform_ = identityTransform;
/**
* @private
* @type {ol.Sphere}
*/
this.sphere_ = new _ol_Sphere_(_ol_proj_EPSG4326_.RADIUS);
/**
* @private
* @type {number|undefined}
@@ -175,8 +167,7 @@ Geolocation.prototype.positionChange_ = function(position) {
this.set(_ol_GeolocationProperty_.POSITION, projectedPosition);
this.set(_ol_GeolocationProperty_.SPEED,
coords.speed === null ? undefined : coords.speed);
var geometry = Polygon.circular(
this.sphere_, this.position_, coords.accuracy);
var geometry = Polygon.circular(this.position_, coords.accuracy);
geometry.applyTransform(this.transform_);
this.set(_ol_GeolocationProperty_.ACCURACY_GEOMETRY, geometry);
this.changed();

View File

@@ -9,6 +9,7 @@ import GeometryType from '../geom/GeometryType.js';
import LinearRing from '../geom/LinearRing.js';
import Point from '../geom/Point.js';
import SimpleGeometry from '../geom/SimpleGeometry.js';
import {offset as sphereOffset} from '../sphere.js';
import _ol_geom_flat_area_ from '../geom/flat/area.js';
import _ol_geom_flat_closest_ from '../geom/flat/closest.js';
import _ol_geom_flat_contains_ from '../geom/flat/contains.js';
@@ -369,22 +370,23 @@ Polygon.prototype.setFlatCoordinates = function(layout, flatCoordinates, ends) {
/**
* Create an approximation of a circle on the surface of a sphere.
* @param {ol.Sphere} sphere The sphere.
* @param {ol.Coordinate} center Center (`[lon, lat]` in degrees).
* @param {number} radius The great-circle distance from the center to
* the polygon vertices.
* @param {number=} opt_n Optional number of vertices for the resulting
* polygon. Default is `32`.
* @param {number=} opt_sphereRadius Optional radius for the sphere (defaults to
* the Earth's mean radius using the WGS84 ellipsoid).
* @return {ol.geom.Polygon} The "circular" polygon.
* @api
*/
Polygon.circular = function(sphere, center, radius, opt_n) {
Polygon.circular = function(center, radius, opt_n, opt_sphereRadius) {
var n = opt_n ? opt_n : 32;
/** @type {Array.<number>} */
var flatCoordinates = [];
var i;
for (i = 0; i < n; ++i) {
extend(flatCoordinates, sphere.offset(center, radius, 2 * Math.PI * i / n));
extend(flatCoordinates, sphereOffset(center, radius, 2 * Math.PI * i / n, opt_sphereRadius));
}
flatCoordinates.push(flatCoordinates[0], flatCoordinates[1]);
var polygon = new Polygon(null);

View File

@@ -1,7 +1,7 @@
/**
* @module ol/proj
*/
import Sphere from './Sphere.js';
import {getDistance} from './sphere.js';
import {applyTransform} from './extent.js';
import {modulo} from './math.js';
import EPSG3857 from './proj/EPSG3857.js';
@@ -21,13 +21,6 @@ import {add as addTransformFunc, clear as clearTransformFuncs, get as getTransfo
export var METERS_PER_UNIT = Units.METERS_PER_UNIT;
/**
* A place to store the mean radius of the Earth.
* @type {ol.Sphere}
*/
var SPHERE = new Sphere(Sphere.DEFAULT_RADIUS);
/**
* @param {Array.<number>} input Input coordinate array.
* @param {Array.<number>=} opt_output Output array of coordinate values.
@@ -150,10 +143,8 @@ export function getPointResolution(projection, resolution, point, opt_units) {
point[0], point[1] + resolution / 2
];
vertices = toEPSG4326(vertices, vertices, 2);
var width = SPHERE.haversineDistance(
vertices.slice(0, 2), vertices.slice(2, 4));
var height = SPHERE.haversineDistance(
vertices.slice(4, 6), vertices.slice(6, 8));
var width = getDistance(vertices.slice(0, 2), vertices.slice(2, 4));
var height = getDistance(vertices.slice(4, 6), vertices.slice(6, 8));
pointResolution = (width + height) / 2;
var metersPerUnit = opt_units ?
Units.METERS_PER_UNIT[opt_units] :

View File

@@ -1,113 +1,69 @@
/**
* @module ol/Sphere
*/
* @license
* Latitude/longitude spherical geodesy formulae taken from
* http://www.movable-type.co.uk/scripts/latlong.html
* Licensed under CC-BY-3.0.
*/
/**
* @license
* Latitude/longitude spherical geodesy formulae taken from
* http://www.movable-type.co.uk/scripts/latlong.html
* Licensed under CC-BY-3.0.
* @module ol/Sphere
*/
import {toRadians, toDegrees} from './math.js';
import GeometryType from './geom/GeometryType.js';
/**
* Object literal with options for the {@link ol.Sphere.getLength} or
* {@link ol.Sphere.getArea} functions.
* Object literal with options for the {@link getLength} or
* {@link getArea} functions.
* @typedef {{projection: (ol.ProjectionLike|undefined),
* radius: (number|undefined)}}
*/
export var SphereMetricOptions;
/**
* @classdesc
* Class to create objects that can be used with {@link
* ol.geom.Polygon.circular}.
*
* For example to create a sphere whose radius is equal to the semi-major
* axis of the WGS84 ellipsoid:
*
* ```js
* var wgs84Sphere= new ol.Sphere(6378137);
* ```
*
* @constructor
* @param {number} radius Radius.
* @api
*/
var _ol_Sphere_ = function(radius) {
/**
* @type {number}
*/
this.radius = radius;
};
/**
* Returns the geodesic area for a list of coordinates.
*
* [Reference](https://trs-new.jpl.nasa.gov/handle/2014/40409)
* Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
* Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
* Laboratory, Pasadena, CA, June 2007
*
* @param {Array.<ol.Coordinate>} coordinates List of coordinates of a linear
* ring. If the ring is oriented clockwise, the area will be positive,
* otherwise it will be negative.
* @return {number} Area.
* @api
*/
_ol_Sphere_.prototype.geodesicArea = function(coordinates) {
return _ol_Sphere_.getArea_(coordinates, this.radius);
};
/**
* Returns the distance from c1 to c2 using the haversine formula.
*
* @param {ol.Coordinate} c1 Coordinate 1.
* @param {ol.Coordinate} c2 Coordinate 2.
* @return {number} Haversine distance.
* @api
*/
_ol_Sphere_.prototype.haversineDistance = function(c1, c2) {
return _ol_Sphere_.getDistance_(c1, c2, this.radius);
};
/**
* Returns the coordinate at the given distance and bearing from `c1`.
*
* @param {ol.Coordinate} c1 The origin point (`[lon, lat]` in degrees).
* @param {number} distance The great-circle distance between the origin
* point and the target point.
* @param {number} bearing The bearing (in radians).
* @return {ol.Coordinate} The target point.
*/
_ol_Sphere_.prototype.offset = function(c1, distance, bearing) {
var lat1 = toRadians(c1[1]);
var lon1 = toRadians(c1[0]);
var dByR = distance / this.radius;
var lat = Math.asin(
Math.sin(lat1) * Math.cos(dByR) +
Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
var lon = lon1 + Math.atan2(
Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
return [toDegrees(lon), toDegrees(lat)];
};
/**
* The mean Earth radius (1/3 * (2a + b)) for the WGS84 ellipsoid.
* https://en.wikipedia.org/wiki/Earth_radius#Mean_radius
* @type {number}
*/
_ol_Sphere_.DEFAULT_RADIUS = 6371008.8;
export var DEFAULT_RADIUS = 6371008.8;
/**
* Get the great circle distance (in meters) between two geographic coordinates.
* @param {Array} c1 Starting coordinate.
* @param {Array} c2 Ending coordinate.
* @param {number=} opt_radius The sphere radius to use. Defaults to the Earth's
* mean radius using the WGS84 ellipsoid.
* @return {number} The great circle distance between the points (in meters).
* @api
*/
export function getDistance(c1, c2, opt_radius) {
var radius = opt_radius || DEFAULT_RADIUS;
var lat1 = toRadians(c1[1]);
var lat2 = toRadians(c2[1]);
var deltaLatBy2 = (lat2 - lat1) / 2;
var deltaLonBy2 = toRadians(c2[0] - c1[0]) / 2;
var a = Math.sin(deltaLatBy2) * Math.sin(deltaLatBy2) +
Math.sin(deltaLonBy2) * Math.sin(deltaLonBy2) *
Math.cos(lat1) * Math.cos(lat2);
return 2 * radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
/**
* Get the cumulative great circle length of linestring coordinates (geographic).
* @param {Array} coordinates Linestring coordinates.
* @param {number} radius The sphere radius to use.
* @return {number} The length (in meters).
*/
function getLengthInternal(coordinates, radius) {
var length = 0;
for (var i = 0, ii = coordinates.length; i < ii - 1; ++i) {
length += getDistance(coordinates[i], coordinates[i + 1], radius);
}
return length;
}
/**
@@ -122,17 +78,19 @@ _ol_Sphere_.DEFAULT_RADIUS = 6371008.8;
* @param {(ol.ProjectionLike|undefined)} opt_options.projection Projection of
* the geometry. By default, the geometry is assumed to be in EPSG:3857
* (Web Mercator).
* @param {(number|undefined)} opt_options.radius Sphere radius. By default,
* the radius of the earth is used (Clarke 1866 Authalic Sphere).
* @param {(number|undefined)} opt_options.radius Sphere radius. Defaults to
* the Earth's mean radius using the WGS84 ellipsoid.
* @return {number} The spherical length (in meters).
* @api
*/
_ol_Sphere_.getLength = function(geometry, opt_options) {
export function getLength(geometry, opt_options) {
var options = opt_options || {};
var radius = options.radius || _ol_Sphere_.DEFAULT_RADIUS;
var radius = options.radius || DEFAULT_RADIUS;
var projection = options.projection || 'EPSG:3857';
geometry = geometry.clone().transform(projection, 'EPSG:4326');
var type = geometry.getType();
if (type !== GeometryType.GEOMETRY_COLLECTION) {
geometry = geometry.clone().transform(projection, 'EPSG:4326');
}
var length = 0;
var coordinates, coords, i, ii, j, jj;
switch (type) {
@@ -143,14 +101,14 @@ _ol_Sphere_.getLength = function(geometry, opt_options) {
case GeometryType.LINE_STRING:
case GeometryType.LINEAR_RING: {
coordinates = /** @type {ol.geom.SimpleGeometry} */ (geometry).getCoordinates();
length = _ol_Sphere_.getLength_(coordinates, radius);
length = getLengthInternal(coordinates, radius);
break;
}
case GeometryType.MULTI_LINE_STRING:
case GeometryType.POLYGON: {
coordinates = /** @type {ol.geom.SimpleGeometry} */ (geometry).getCoordinates();
for (i = 0, ii = coordinates.length; i < ii; ++i) {
length += _ol_Sphere_.getLength_(coordinates[i], radius);
length += getLengthInternal(coordinates[i], radius);
}
break;
}
@@ -159,7 +117,7 @@ _ol_Sphere_.getLength = function(geometry, opt_options) {
for (i = 0, ii = coordinates.length; i < ii; ++i) {
coords = coordinates[i];
for (j = 0, jj = coords.length; j < jj; ++j) {
length += _ol_Sphere_.getLength_(coords[j], radius);
length += getLengthInternal(coords[j], radius);
}
}
break;
@@ -167,7 +125,7 @@ _ol_Sphere_.getLength = function(geometry, opt_options) {
case GeometryType.GEOMETRY_COLLECTION: {
var geometries = /** @type {ol.geom.GeometryCollection} */ (geometry).getGeometries();
for (i = 0, ii = geometries.length; i < ii; ++i) {
length += _ol_Sphere_.getLength(geometries[i], opt_options);
length += getLength(geometries[i], opt_options);
}
break;
}
@@ -176,101 +134,7 @@ _ol_Sphere_.getLength = function(geometry, opt_options) {
}
}
return length;
};
/**
* Get the cumulative great circle length of linestring coordinates (geographic).
* @param {Array} coordinates Linestring coordinates.
* @param {number} radius The sphere radius to use.
* @return {number} The length (in meters).
*/
_ol_Sphere_.getLength_ = function(coordinates, radius) {
var length = 0;
for (var i = 0, ii = coordinates.length; i < ii - 1; ++i) {
length += _ol_Sphere_.getDistance_(coordinates[i], coordinates[i + 1], radius);
}
return length;
};
/**
* Get the great circle distance between two geographic coordinates.
* @param {Array} c1 Starting coordinate.
* @param {Array} c2 Ending coordinate.
* @param {number} radius The sphere radius to use.
* @return {number} The great circle distance between the points (in meters).
*/
_ol_Sphere_.getDistance_ = function(c1, c2, radius) {
var lat1 = toRadians(c1[1]);
var lat2 = toRadians(c2[1]);
var deltaLatBy2 = (lat2 - lat1) / 2;
var deltaLonBy2 = toRadians(c2[0] - c1[0]) / 2;
var a = Math.sin(deltaLatBy2) * Math.sin(deltaLatBy2) +
Math.sin(deltaLonBy2) * Math.sin(deltaLonBy2) *
Math.cos(lat1) * Math.cos(lat2);
return 2 * radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
};
/**
* Get the spherical area of a geometry. This is the area (in meters) assuming
* that polygon edges are segments of great circles on a sphere.
* @param {ol.geom.Geometry} geometry A geometry.
* @param {SphereMetricOptions=} opt_options Options for the area
* calculation. By default, geometries are assumed to be in 'EPSG:3857'.
* You can change this by providing a `projection` option.
* @return {number} The spherical area (in square meters).
* @api
*/
_ol_Sphere_.getArea = function(geometry, opt_options) {
var options = opt_options || {};
var radius = options.radius || _ol_Sphere_.DEFAULT_RADIUS;
var projection = options.projection || 'EPSG:3857';
geometry = geometry.clone().transform(projection, 'EPSG:4326');
var type = geometry.getType();
var area = 0;
var coordinates, coords, i, ii, j, jj;
switch (type) {
case GeometryType.POINT:
case GeometryType.MULTI_POINT:
case GeometryType.LINE_STRING:
case GeometryType.MULTI_LINE_STRING:
case GeometryType.LINEAR_RING: {
break;
}
case GeometryType.POLYGON: {
coordinates = /** @type {ol.geom.Polygon} */ (geometry).getCoordinates();
area = Math.abs(_ol_Sphere_.getArea_(coordinates[0], radius));
for (i = 1, ii = coordinates.length; i < ii; ++i) {
area -= Math.abs(_ol_Sphere_.getArea_(coordinates[i], radius));
}
break;
}
case GeometryType.MULTI_POLYGON: {
coordinates = /** @type {ol.geom.SimpleGeometry} */ (geometry).getCoordinates();
for (i = 0, ii = coordinates.length; i < ii; ++i) {
coords = coordinates[i];
area += Math.abs(_ol_Sphere_.getArea_(coords[0], radius));
for (j = 1, jj = coords.length; j < jj; ++j) {
area -= Math.abs(_ol_Sphere_.getArea_(coords[j], radius));
}
}
break;
}
case GeometryType.GEOMETRY_COLLECTION: {
var geometries = /** @type {ol.geom.GeometryCollection} */ (geometry).getGeometries();
for (i = 0, ii = geometries.length; i < ii; ++i) {
area += _ol_Sphere_.getArea(geometries[i], opt_options);
}
break;
}
default: {
throw new Error('Unsupported geometry type: ' + type);
}
}
return area;
};
}
/**
@@ -287,7 +151,7 @@ _ol_Sphere_.getArea = function(geometry, opt_options) {
* @param {number} radius The sphere radius.
* @return {number} Area (in square meters).
*/
_ol_Sphere_.getArea_ = function(coordinates, radius) {
function getAreaInternal(coordinates, radius) {
var area = 0, len = coordinates.length;
var x1 = coordinates[len - 1][0];
var y1 = coordinates[len - 1][1];
@@ -300,5 +164,92 @@ _ol_Sphere_.getArea_ = function(coordinates, radius) {
y1 = y2;
}
return area * radius * radius / 2.0;
};
export default _ol_Sphere_;
}
/**
* Get the spherical area of a geometry. This is the area (in meters) assuming
* that polygon edges are segments of great circles on a sphere.
* @param {ol.geom.Geometry} geometry A geometry.
* @param {SphereMetricOptions=} opt_options Options for the area
* calculation. By default, geometries are assumed to be in 'EPSG:3857'.
* You can change this by providing a `projection` option.
* @return {number} The spherical area (in square meters).
* @api
*/
export function getArea(geometry, opt_options) {
var options = opt_options || {};
var radius = options.radius || DEFAULT_RADIUS;
var projection = options.projection || 'EPSG:3857';
var type = geometry.getType();
if (type !== GeometryType.GEOMETRY_COLLECTION) {
geometry = geometry.clone().transform(projection, 'EPSG:4326');
}
var area = 0;
var coordinates, coords, i, ii, j, jj;
switch (type) {
case GeometryType.POINT:
case GeometryType.MULTI_POINT:
case GeometryType.LINE_STRING:
case GeometryType.MULTI_LINE_STRING:
case GeometryType.LINEAR_RING: {
break;
}
case GeometryType.POLYGON: {
coordinates = /** @type {ol.geom.Polygon} */ (geometry).getCoordinates();
area = Math.abs(getAreaInternal(coordinates[0], radius));
for (i = 1, ii = coordinates.length; i < ii; ++i) {
area -= Math.abs(getAreaInternal(coordinates[i], radius));
}
break;
}
case GeometryType.MULTI_POLYGON: {
coordinates = /** @type {ol.geom.SimpleGeometry} */ (geometry).getCoordinates();
for (i = 0, ii = coordinates.length; i < ii; ++i) {
coords = coordinates[i];
area += Math.abs(getAreaInternal(coords[0], radius));
for (j = 1, jj = coords.length; j < jj; ++j) {
area -= Math.abs(getAreaInternal(coords[j], radius));
}
}
break;
}
case GeometryType.GEOMETRY_COLLECTION: {
var geometries = /** @type {ol.geom.GeometryCollection} */ (geometry).getGeometries();
for (i = 0, ii = geometries.length; i < ii; ++i) {
area += getArea(geometries[i], opt_options);
}
break;
}
default: {
throw new Error('Unsupported geometry type: ' + type);
}
}
return area;
}
/**
* Returns the coordinate at the given distance and bearing from `c1`.
*
* @param {ol.Coordinate} c1 The origin point (`[lon, lat]` in degrees).
* @param {number} distance The great-circle distance between the origin
* point and the target point.
* @param {number} bearing The bearing (in radians).
* @param {number=} opt_radius The sphere radius to use. Defaults to the Earth's
* mean radius using the WGS84 ellipsoid.
* @return {ol.Coordinate} The target point.
*/
export function offset(c1, distance, bearing, opt_radius) {
var radius = opt_radius || DEFAULT_RADIUS;
var lat1 = toRadians(c1[1]);
var lon1 = toRadians(c1[0]);
var dByR = distance / radius;
var lat = Math.asin(
Math.sin(lat1) * Math.cos(dByR) +
Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
var lon = lon1 + Math.atan2(
Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
return [toDegrees(lon), toDegrees(lat)];
}