Merge pull request #7643 from tschaub/sphere

Replace ol/Sphere with ol/sphere
This commit is contained in:
Tim Schaub
2017-12-21 00:08:46 -07:00
committed by GitHub
15 changed files with 413 additions and 435 deletions

View File

@@ -35,6 +35,52 @@ register(proj4);
The map and sources no longer accept a `logo` option. Instead, if you wish to append a logo to your map, add the desired markup directly in your HTML. In addition, you can use the `attributions` property of a source to display arbitrary markup per-source with the attribution control.
#### Replacement of `ol/Sphere` constructor with `ol/sphere` functions
The `ol/Sphere` constructor has been removed. If you were using the `getGeodesicArea` method, use the `getArea` function instead. If you were using the `haversineDistance` method, use the `getDistance` function instead.
Examples before:
```js
// using ol@4
import Sphere from 'ol/sphere';
var sphere = new Sphere(Sphere.DEFAULT_RADIUS);
var area = sphere.getGeodesicArea(polygon);
var distance = sphere.haversineDistance(g1, g2);
```
Examples after:
```js
// using ol@5
import {circular as circularPolygon} from 'ol/geom/Polygon';
import {getArea, getDistance} from 'ol/sphere';
var area = getArea(polygon);
var distance = getDistance(g1, g2);
var circle = circularPolygon(center, radius);
```
#### New signature for the `circular` function for creating polygons
The `circular` function exported from `ol/geom/Polygon` no longer requires a `Sphere` as the first argument.
Example before:
```js
// using ol@4
import Polygon from 'ol/geom/polygon';
import Sphere from 'ol/sphere';
var poly = Polygon.circular(new Sphere(Sphere.DEFAULT_RADIUS), center, radius);
```
Example after:
```js
// using ol@5
import {circular as circularPolygon} from 'ol/geom/Polygon';
var poly = circularPolygon(center, radius);
```
#### Removal of optional this arguments.
The following methods did get the optional this (i.e. opt_this) arguments removed. Please use closures, the es6 arrow function or the bind method to achieve this effect (Bind is explained here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).

View File

@@ -3,7 +3,7 @@ layout: example.html
title: Measure
shortdesc: Using a draw interaction to measure lengths and areas.
docs: >
<p>The <code>ol.Sphere.getLength()</code> and <code>ol.Sphere.getArea()</code>
<p>The <code>getLength()</code> and <code>getArea()</code>
functions calculate spherical lengths and areas for geometries. Lengths are
calculated by assuming great circle segments between geometry coordinates.
Areas are calculated as if edges of polygons were great circle segments.</p>
@@ -11,7 +11,7 @@ docs: >
methods return measures of projected (planar) geometries. These can be very
different than on-the-ground measures in certain situations — in northern
and southern latitudes using Web Mercator for example. For better results,
use the functions on <code>ol.Sphere</code>.</p>
use the functions in the <code>ol/sphere</code> module.</p>
tags: "draw, edit, measure, vector"
---
<div id="map" class="map"></div>

View File

@@ -1,7 +1,7 @@
import _ol_Map_ from '../src/ol/Map.js';
import _ol_Observable_ from '../src/ol/Observable.js';
import _ol_Overlay_ from '../src/ol/Overlay.js';
import _ol_Sphere_ from '../src/ol/Sphere.js';
import {getArea, getLength} from '../src/ol/sphere.js';
import _ol_View_ from '../src/ol/View.js';
import LineString from '../src/ol/geom/LineString.js';
import Polygon from '../src/ol/geom/Polygon.js';
@@ -144,7 +144,7 @@ var draw; // global so we can remove it later
* @return {string} The formatted length.
*/
var formatLength = function(line) {
var length = _ol_Sphere_.getLength(line);
var length = getLength(line);
var output;
if (length > 100) {
output = (Math.round(length / 1000 * 100) / 100) +
@@ -163,7 +163,7 @@ var formatLength = function(line) {
* @return {string} Formatted area.
*/
var formatArea = function(polygon) {
var area = _ol_Sphere_.getArea(polygon);
var area = getArea(polygon);
var output;
if (area > 10000) {
output = (Math.round(area / 1000000 * 100) / 100) +

View File

@@ -1,8 +1,7 @@
import _ol_Feature_ from '../src/ol/Feature.js';
import _ol_Map_ from '../src/ol/Map.js';
import _ol_Sphere_ from '../src/ol/Sphere.js';
import _ol_View_ from '../src/ol/View.js';
import Polygon from '../src/ol/geom/Polygon.js';
import {circular as circularPolygon} from '../src/ol/geom/Polygon.js';
import TileLayer from '../src/ol/layer/Tile.js';
import _ol_layer_Vector_ from '../src/ol/layer/Vector.js';
import _ol_source_TileWMS_ from '../src/ol/source/TileWMS.js';
@@ -57,13 +56,11 @@ var map3857 = new _ol_Map_({
})
});
var wgs84Sphere = new _ol_Sphere_(6378137);
var radius = 800000;
var x, y;
for (x = -180; x < 180; x += 30) {
for (y = -90; y < 90; y += 30) {
var circle4326 = Polygon.circular(wgs84Sphere, [x, y], radius, 64);
var circle4326 = circularPolygon([x, y], radius, 64);
var circle3857 = circle4326.clone().transform('EPSG:4326', 'EPSG:3857');
vectorLayer4326.getSource().addFeature(new _ol_Feature_(circle4326));
vectorLayer3857.getSource().addFeature(new _ol_Feature_(circle3857));

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 {circular as circularPolygon} 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 = circularPolygon(this.position_, coords.accuracy);
geometry.applyTransform(this.transform_);
this.set(_ol_GeolocationProperty_.ACCURACY_GEOMETRY, geometry);
this.changed();

View File

@@ -15,7 +15,7 @@ import _ol_coordinate_ from './coordinate.js';
import {inAndOut} from './easing.js';
import {getForViewAndSize, getCenter, getHeight, getWidth, isEmpty} from './extent.js';
import GeometryType from './geom/GeometryType.js';
import Polygon from './geom/Polygon.js';
import {fromExtent as polygonFromExtent} from './geom/Polygon.js';
import SimpleGeometry from './geom/SimpleGeometry.js';
import {clamp, modulo} from './math.js';
import _ol_obj_ from './obj.js';
@@ -884,10 +884,10 @@ _ol_View_.prototype.fit = function(geometryOrExtent, opt_options) {
24); // Invalid extent or geometry provided as `geometry`
assert(!isEmpty(geometryOrExtent),
25); // Cannot fit empty extent provided as `geometry`
geometry = Polygon.fromExtent(geometryOrExtent);
geometry = polygonFromExtent(geometryOrExtent);
} else if (geometryOrExtent.getType() === GeometryType.CIRCLE) {
geometryOrExtent = geometryOrExtent.getExtent();
geometry = Polygon.fromExtent(geometryOrExtent);
geometry = polygonFromExtent(geometryOrExtent);
geometry.rotate(this.getRotation(), getCenter(geometryOrExtent));
} else {
geometry = geometryOrExtent;

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';
@@ -366,32 +367,35 @@ Polygon.prototype.setFlatCoordinates = function(layout, flatCoordinates, ends) {
this.changed();
};
export default Polygon;
/**
* 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) {
export function circular(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);
polygon.setFlatCoordinates(
GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]);
return polygon;
};
}
/**
@@ -400,7 +404,7 @@ Polygon.circular = function(sphere, center, radius, opt_n) {
* @return {ol.geom.Polygon} The polygon.
* @api
*/
Polygon.fromExtent = function(extent) {
export function fromExtent(extent) {
var minX = extent[0];
var minY = extent[1];
var maxX = extent[2];
@@ -411,7 +415,7 @@ Polygon.fromExtent = function(extent) {
polygon.setFlatCoordinates(
GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]);
return polygon;
};
}
/**
@@ -423,7 +427,7 @@ Polygon.fromExtent = function(extent) {
* @return {ol.geom.Polygon} Polygon geometry.
* @api
*/
Polygon.fromCircle = function(circle, opt_sides, opt_angle) {
export function fromCircle(circle, opt_sides, opt_angle) {
var sides = opt_sides ? opt_sides : 32;
var stride = circle.getStride();
var layout = circle.getLayout();
@@ -435,10 +439,9 @@ Polygon.fromCircle = function(circle, opt_sides, opt_angle) {
}
var ends = [flatCoordinates.length];
polygon.setFlatCoordinates(layout, flatCoordinates, ends);
Polygon.makeRegular(
polygon, circle.getCenter(), circle.getRadius(), opt_angle);
makeRegular(polygon, circle.getCenter(), circle.getRadius(), opt_angle);
return polygon;
};
}
/**
@@ -449,7 +452,7 @@ Polygon.fromCircle = function(circle, opt_sides, opt_angle) {
* @param {number=} opt_angle Start angle for the first vertex of the polygon in
* radians. Default is 0.
*/
Polygon.makeRegular = function(polygon, center, radius, opt_angle) {
export function makeRegular(polygon, center, radius, opt_angle) {
var flatCoordinates = polygon.getFlatCoordinates();
var layout = polygon.getLayout();
var stride = polygon.getStride();
@@ -464,5 +467,4 @@ Polygon.makeRegular = function(polygon, center, radius, opt_angle) {
flatCoordinates[offset + 1] = center[1] + (radius * Math.sin(angle));
}
polygon.setFlatCoordinates(layout, flatCoordinates, ends);
};
export default Polygon;
}

View File

@@ -18,7 +18,7 @@ import MultiLineString from '../geom/MultiLineString.js';
import MultiPoint from '../geom/MultiPoint.js';
import MultiPolygon from '../geom/MultiPolygon.js';
import Point from '../geom/Point.js';
import Polygon from '../geom/Polygon.js';
import Polygon, {fromCircle, makeRegular} from '../geom/Polygon.js';
import DrawEventType from '../interaction/DrawEventType.js';
import _ol_interaction_Pointer_ from '../interaction/Pointer.js';
import _ol_interaction_Property_ from '../interaction/Property.js';
@@ -795,10 +795,10 @@ Draw.createRegularPolygon = function(opt_sides, opt_angle) {
var radius = Math.sqrt(
_ol_coordinate_.squaredDistance(center, end));
var geometry = opt_geometry ? /** @type {ol.geom.Polygon} */ (opt_geometry) :
Polygon.fromCircle(new Circle(center), opt_sides);
fromCircle(new Circle(center), opt_sides);
var angle = opt_angle ? opt_angle :
Math.atan((end[1] - center[1]) / (end[0] - center[0]));
Polygon.makeRegular(geometry, center, radius, angle);
makeRegular(geometry, center, radius, angle);
return geometry;
}
);

View File

@@ -10,7 +10,7 @@ import Event from '../events/Event.js';
import {boundingExtent, getArea} from '../extent.js';
import GeometryType from '../geom/GeometryType.js';
import Point from '../geom/Point.js';
import Polygon from '../geom/Polygon.js';
import {fromExtent as polygonFromExtent} from '../geom/Polygon.js';
import _ol_interaction_ExtentEventType_ from '../interaction/ExtentEventType.js';
import _ol_interaction_Pointer_ from '../interaction/Pointer.js';
import _ol_layer_Vector_ from '../layer/Vector.js';
@@ -376,7 +376,7 @@ _ol_interaction_Extent_.prototype.createOrUpdateExtentFeature_ = function(extent
if (!extent) {
extentFeature = new _ol_Feature_({});
} else {
extentFeature = new _ol_Feature_(Polygon.fromExtent(extent));
extentFeature = new _ol_Feature_(polygonFromExtent(extent));
}
this.extentFeature_ = extentFeature;
this.extentOverlay_.getSource().addFeature(extentFeature);
@@ -384,7 +384,7 @@ _ol_interaction_Extent_.prototype.createOrUpdateExtentFeature_ = function(extent
if (!extent) {
extentFeature.setGeometry(undefined);
} else {
extentFeature.setGeometry(Polygon.fromExtent(extent));
extentFeature.setGeometry(polygonFromExtent(extent));
}
}
return extentFeature;

View File

@@ -10,7 +10,7 @@ import EventType from '../events/EventType.js';
import {boundingExtent, createEmpty} from '../extent.js';
import {TRUE, FALSE} from '../functions.js';
import GeometryType from '../geom/GeometryType.js';
import Polygon from '../geom/Polygon.js';
import {fromCircle} from '../geom/Polygon.js';
import _ol_interaction_Pointer_ from '../interaction/Pointer.js';
import _ol_obj_ from '../obj.js';
import _ol_source_Vector_ from '../source/Vector.js';
@@ -435,7 +435,7 @@ _ol_interaction_Snap_.prototype.updateFeature_ = function(feature) {
* @private
*/
_ol_interaction_Snap_.prototype.writeCircleGeometry_ = function(feature, geometry) {
var polygon = Polygon.fromCircle(geometry);
var polygon = fromCircle(geometry);
var coordinates = polygon.getCoordinates()[0];
var i, ii, segment, segmentData;
for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {

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)];
}

View File

@@ -1,10 +1,10 @@
import * as _ol_extent_ from '../../../../src/ol/extent.js';
import Circle from '../../../../src/ol/geom/Circle.js';
import LinearRing from '../../../../src/ol/geom/LinearRing.js';
import Polygon from '../../../../src/ol/geom/Polygon.js';
import Polygon, {fromCircle, fromExtent} from '../../../../src/ol/geom/Polygon.js';
describe('ol.geom.Polygon', function() {
describe('ol/geom/Polygon', function() {
it('can be constructed with a null geometry', function() {
expect(function() {
@@ -561,10 +561,10 @@ describe('ol.geom.Polygon', function() {
});
});
describe('ol.geom.Polygon.fromExtent', function() {
describe('fromExtent()', function() {
it('creates the correct polygon', function() {
var extent = [1, 2, 3, 5];
var polygon = Polygon.fromExtent(extent);
var polygon = fromExtent(extent);
var flatCoordinates = polygon.getFlatCoordinates();
expect(flatCoordinates).to.eql(
[1, 2, 1, 5, 3, 5, 3, 2, 1, 2]);
@@ -574,11 +574,11 @@ describe('ol.geom.Polygon', function() {
});
});
describe('ol.geom.Polygon.fromCircle', function() {
describe('fromCircle()', function() {
it('creates a regular polygon', function() {
var circle = new Circle([0, 0, 0], 1, 'XYZ');
var polygon = Polygon.fromCircle(circle);
var polygon = fromCircle(circle);
var coordinates = polygon.getLinearRing(0).getCoordinates();
expect(coordinates[0].length).to.eql(3);
expect(coordinates[0][2]).to.eql(0);
@@ -599,7 +599,7 @@ describe('ol.geom.Polygon', function() {
it('creates a regular polygon with custom sides and angle', function() {
var circle = new Circle([0, 0], 1);
var polygon = Polygon.fromCircle(circle, 4, Math.PI / 2);
var polygon = fromCircle(circle, 4, Math.PI / 2);
var coordinates = polygon.getLinearRing(0).getCoordinates();
expect(coordinates[4]).to.eql(coordinates[0]);
expect(coordinates[0][0]).to.roughlyEqual(0, 1e-9);

View File

@@ -1,7 +1,7 @@
import _ol_Map_ from '../../../../src/ol/Map.js';
import _ol_View_ from '../../../../src/ol/View.js';
import * as _ol_extent_ from '../../../../src/ol/extent.js';
import Polygon from '../../../../src/ol/geom/Polygon.js';
import {fromExtent as polygonFromExtent} from '../../../../src/ol/geom/Polygon.js';
import DragZoom from '../../../../src/ol/interaction/DragZoom.js';
import _ol_layer_Vector_ from '../../../../src/ol/layer/Vector.js';
import _ol_render_Box_ from '../../../../src/ol/render/Box.js';
@@ -72,7 +72,7 @@ describe('ol.interaction.DragZoom', function() {
var box = new _ol_render_Box_();
var extent = [-110, 40, -90, 60];
box.geometry_ = Polygon.fromExtent(extent);
box.geometry_ = polygonFromExtent(extent);
interaction.box_ = box;
interaction.onBoxEnd();
@@ -94,7 +94,7 @@ describe('ol.interaction.DragZoom', function() {
var box = new _ol_render_Box_();
var extent = [-11.25, -11.25, 11.25, 11.25];
box.geometry_ = Polygon.fromExtent(extent);
box.geometry_ = polygonFromExtent(extent);
interaction.box_ = box;
map.getView().setResolution(0.25);

View File

@@ -1,97 +1,173 @@
// See http://www.movable-type.co.uk/scripts/latlong.html
// FIXME add tests for offset
import _ol_Sphere_ from '../../../src/ol/Sphere.js';
import {getArea, getDistance, getLength} from '../../../src/ol/sphere.js';
import WKT from '../../../src/ol/format/WKT.js';
import GeometryCollection from '../../../src/ol/geom/GeometryCollection.js';
import LineString from '../../../src/ol/geom/LineString.js';
import MultiLineString from '../../../src/ol/geom/MultiLineString.js';
import MultiPoint from '../../../src/ol/geom/MultiPoint.js';
import Point from '../../../src/ol/geom/Point.js';
import _ol_proj_EPSG4326_ from '../../../src/ol/proj/EPSG4326.js';
describe('ol.Sphere', function() {
describe('ol/sphere', function() {
var sphere = new _ol_Sphere_(6371);
var expected = [{
c1: [0, 0],
c2: [0, 0],
haversineDistance: 0
}, {
c1: [0, 0],
c2: [45, 45],
haversineDistance: 6671.695598673525
}, {
c1: [0, 0],
c2: [-45, 45],
haversineDistance: 6671.695598673525
}, {
c1: [0, 0],
c2: [-45, -45],
haversineDistance: 6671.695598673525
}, {
c1: [0, 0],
c2: [45, -45],
haversineDistance: 6671.695598673525
}, {
c1: [45, 45],
c2: [45, 45],
haversineDistance: 0
}, {
c1: [45, 45],
c2: [-45, 45],
haversineDistance: 6671.695598673525
}, {
c1: [45, 45],
c2: [-45, -45],
haversineDistance: 13343.391197347048
}, {
c1: [45, 45],
c2: [45, -45],
haversineDistance: 10007.543398010286
}, {
c1: [-45, 45],
c2: [-45, 45],
haversineDistance: 0
}, {
c1: [-45, 45],
c2: [-45, -45],
haversineDistance: 10007.543398010286
}, {
c1: [-45, 45],
c2: [45, -45],
haversineDistance: 13343.391197347048
}, {
c1: [-45, -45],
c2: [-45, -45],
haversineDistance: 0
}, {
c1: [-45, -45],
c2: [45, -45],
haversineDistance: 6671.695598673525
}, {
c1: [45, -45],
c2: [45, -45],
haversineDistance: 0
}];
describe('getDistance()', function() {
describe('haversineDistance', function() {
var expected = [{
c1: [0, 0],
c2: [0, 0],
distance: 0
}, {
c1: [0, 0],
c2: [45, 45],
distance: 6671704.814011975
}, {
c1: [0, 0],
c2: [-45, 45],
distance: 6671704.814011975
}, {
c1: [0, 0],
c2: [-45, -45],
distance: 6671704.814011975
}, {
c1: [0, 0],
c2: [45, -45],
distance: 6671704.814011975
}, {
c1: [45, 45],
c2: [45, 45],
distance: 0
}, {
c1: [45, 45],
c2: [-45, 45],
distance: 6671704.814011975
}, {
c1: [45, 45],
c2: [-45, -45],
distance: 13343409.628023949
}, {
c1: [45, 45],
c2: [45, -45],
distance: 10007557.221017962
}, {
c1: [-45, 45],
c2: [-45, 45],
distance: 0
}, {
c1: [-45, 45],
c2: [-45, -45],
distance: 10007557.221017962
}, {
c1: [-45, 45],
c2: [45, -45],
distance: 13343409.628023949
}, {
c1: [-45, -45],
c2: [-45, -45],
distance: 0
}, {
c1: [-45, -45],
c2: [45, -45],
distance: 6671704.814011975
}, {
c1: [45, -45],
c2: [45, -45],
distance: 0
}];
it('results match Chris Veness\'s reference implementation', function() {
var e, i;
for (i = 0; i < expected.length; ++i) {
e = expected[i];
expect(sphere.haversineDistance(e.c1, e.c2)).to.roughlyEqual(
e.haversineDistance, 1e-9);
}
expected.forEach(function(e, i) {
it('calculates the distance between two points: ' + i, function() {
expect(getDistance(e.c1, e.c2)).to.roughlyEqual(e.distance, 1e-6);
});
});
});
describe('getLength()', function() {
var cases = [{
geometry: new Point([0, 0]),
length: 0
}, {
geometry: new MultiPoint([[0, 0], [1, 1]]),
length: 0
}, {
geometry: new LineString([
[12801741.441226462, -3763310.627144653],
[14582853.293918837, -2511525.2348457114],
[15918687.18343812, -2875744.624352243],
[16697923.618991036, -4028802.0261344076]
]),
length: 4407939.124914191
}, {
geometry: new GeometryCollection([
new LineString([
[12801741.441226462, -3763310.627144653],
[14582853.293918837, -2511525.2348457114],
[15918687.18343812, -2875744.624352243],
[16697923.618991036, -4028802.0261344076]
]),
new LineString([
[12801741.441226462, -3763310.627144653],
[14582853.293918837, -2511525.2348457114],
[15918687.18343812, -2875744.624352243],
[16697923.618991036, -4028802.0261344076]
])
]),
length: 2 * 4407939.124914191
}, {
geometry: new LineString([
[115, -32],
[131, -22],
[143, -25],
[150, -34]
]),
options: {projection: 'EPSG:4326'},
length: 4407939.124914191
}, {
geometry: new MultiLineString([
[
[115, -32],
[131, -22],
[143, -25],
[150, -34]
], [
[115, -32],
[131, -22],
[143, -25],
[150, -34]
]
]),
options: {projection: 'EPSG:4326'},
length: 2 * 4407939.124914191
}, {
geometry: new GeometryCollection([
new LineString([
[115, -32],
[131, -22],
[143, -25],
[150, -34]
]),
new LineString([
[115, -32],
[131, -22],
[143, -25],
[150, -34]
])
]),
options: {projection: 'EPSG:4326'},
length: 2 * 4407939.124914191
}];
cases.forEach(function(c, i) {
it('works for case ' + i, function() {
var c = cases[i];
var length = getLength(c.geometry, c.options);
expect(length).to.roughlyEqual(c.length, 1e-8);
});
});
});
describe('Vincenty area', function() {
describe('getArea()', function() {
var geometry;
var expectedArea = 145652224192.4434;
before(function(done) {
afterLoadText('spec/ol/format/wkt/illinois.wkt', function(wkt) {
try {
@@ -104,100 +180,24 @@ describe('ol.Sphere', function() {
});
});
it('results match the expected area of Ilinois', function() {
var coords = geometry.getPolygon(0).getLinearRing(0).getCoordinates();
var sphere = new _ol_Sphere_(_ol_proj_EPSG4326_.RADIUS);
expect(sphere.geodesicArea(coords)).to.equal(145978332359.37125);
it('calculates the area of Ilinois', function() {
var area = getArea(geometry, {projection: 'EPSG:4326'});
expect(area).to.equal(expectedArea);
});
it('calculates the area of a projected geometry', function() {
var projected = geometry.clone().transform('EPSG:4326', 'EPSG:3857');
var area = getArea(projected);
expect(area).to.roughlyEqual(expectedArea, 1e-3);
});
it('calculates the area of a projected geometry collection', function() {
var part = geometry.clone().transform('EPSG:4326', 'EPSG:3857');
var collection = new GeometryCollection([part, part.clone()]);
var area = getArea(collection);
expect(area).to.roughlyEqual(2 * expectedArea, 1e-3);
});
});
});
describe('ol.Sphere.getLength()', function() {
var cases = [{
geometry: new Point([0, 0]),
length: 0
}, {
geometry: new MultiPoint([[0, 0], [1, 1]]),
length: 0
}, {
geometry: new LineString([
[12801741.441226462, -3763310.627144653],
[14582853.293918837, -2511525.2348457114],
[15918687.18343812, -2875744.624352243],
[16697923.618991036, -4028802.0261344076]
]),
length: 4407939.124914191
}, {
geometry: new LineString([
[115, -32],
[131, -22],
[143, -25],
[150, -34]
]),
options: {projection: 'EPSG:4326'},
length: 4407939.124914191
}, {
geometry: new MultiLineString([
[
[115, -32],
[131, -22],
[143, -25],
[150, -34]
], [
[115, -32],
[131, -22],
[143, -25],
[150, -34]
]
]),
options: {projection: 'EPSG:4326'},
length: 2 * 4407939.124914191
}, {
geometry: new GeometryCollection([
new LineString([
[115, -32],
[131, -22],
[143, -25],
[150, -34]
]),
new LineString([
[115, -32],
[131, -22],
[143, -25],
[150, -34]
])
]),
options: {projection: 'EPSG:4326'},
length: 2 * 4407939.124914191
}];
cases.forEach(function(c, i) {
it('works for case ' + i, function() {
var c = cases[i];
var length = _ol_Sphere_.getLength(c.geometry, c.options);
expect(length).to.roughlyEqual(c.length, 1e-8);
});
});
});
describe('ol.Sphere.getArea()', function() {
var geometry;
before(function(done) {
afterLoadText('spec/ol/format/wkt/illinois.wkt', function(wkt) {
try {
var format = new WKT();
geometry = format.readGeometry(wkt);
} catch (e) {
done(e);
}
done();
});
});
it('calculates the area of Ilinois', function() {
var area = _ol_Sphere_.getArea(geometry, {projection: 'EPSG:4326'});
expect(area).to.equal(145652224192.4434);
});
});