leaflet-mapbox-vector-tile
Version:
A Leaflet Plugin that renders Mapbox Vector Tiles on HTML5 Canvas.
1,766 lines (1,528 loc) • 1.3 MB
JavaScript
/* The JSTS Topology Suite is a collection of JavaScript classes that
implement the fundamental operations required to validate a given
geo-spatial data set to a known topological specification.
Copyright (C) 2011 The Authors
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* ======================================================================
jsts.js
====================================================================== */
/* Copyright (c) 2011 by The Authors.
* Published under the LGPL 2.1 license.
* See /license-notice.txt for the full text of the license notice.
* See /license.txt for the full text of the license.
*/
/** @namespace */
jsts = {
version: '0.13.4',
/** @namespace */
algorithm: {
/** @namespace */
distance: {},
/** @namespace */
locate: {}
},
/** @namespace */
error: {},
/** @namespace */
geom: {
/** @namespace */
util: {}
},
/** @namespace */
geomgraph: {
/** @namespace */
index: {}
},
/** @namespace */
index: {
/** @namespace */
bintree: {},
/** @namespace */
chain: {},
/** @namespace */
kdtree: {},
/** @namespace */
quadtree: {},
/** @namespace */
strtree: {}
},
/** @namespace */
io: {},
/** @namespace */
noding: {
/** @namespace */
snapround: {}
},
/** @namespace */
operation: {
/** @namespace */
buffer: {},
/** @namespace */
distance: {},
/** @namespace */
overlay: {
/** @namespace */
snap: {}
},
/** @namespace */
polygonize: {},
/** @namespace */
relate: {},
/** @namespace */
union: {},
/** @namespace */
valid: {}
},
/** @namespace */
planargraph: {},
/** @namespace */
simplify: {},
/** @namespace */
triangulate: {
/** @namespace */
quadedge: {}
},
/** @namespace */
util: {}
};
/**
* Implement String.trim if native support is missing.
*/
if(typeof String.prototype.trim !== 'function') {
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '');
};
}
/**
* Global function intended for use as a generic abstract method.
* @private
*/
jsts.abstractFunc = function() {
throw new jsts.error.AbstractMethodInvocationError();
};
jsts.error = {};
/**
* @constructor
*/
jsts.error.IllegalArgumentError = function(message) {
this.name = 'IllegalArgumentError';
this.message = message;
};
jsts.error.IllegalArgumentError.prototype = new Error();
/**
* @constructor
*/
jsts.error.TopologyError = function(message, pt) {
this.name = 'TopologyError';
this.message = pt ? message + ' [ ' + pt + ' ]' : message;
};
jsts.error.TopologyError.prototype = new Error();
/**
* @constructor
*/
jsts.error.AbstractMethodInvocationError = function() {
this.name = 'AbstractMethodInvocationError';
this.message = 'Abstract method called, should be implemented in subclass.';
};
jsts.error.AbstractMethodInvocationError.prototype = new Error();
/**
* @constructor
*/
jsts.error.NotImplementedError = function() {
this.name = 'NotImplementedError';
this.message = 'This method has not yet been implemented.';
};
jsts.error.NotImplementedError.prototype = new Error();
/**
* @constructor
*/
jsts.error.NotRepresentableError = function(message) {
this.name = 'NotRepresentableError';
this.message = message;
};
jsts.error.NotRepresentableError.prototype = new Error();
/**
* @constructor message
*/
jsts.error.LocateFailureError = function(message) {
this.name = 'LocateFailureError';
this.message = message;
};
jsts.error.LocateFailureError.prototype = new Error();
if (typeof module !== "undefined") module.exports = jsts;
/* ======================================================================
jsts/geom/GeometryFilter.js
====================================================================== */
/* Copyright (c) 2011 by The Authors.
* Published under the LGPL 2.1 license.
* See /license-notice.txt for the full text of the license notice.
* See /license.txt for the full text of the license.
*/
/**
* <code>GeometryCollection</code> classes support the concept of applying a
* <code>GeometryFilter</code> to the <code>Geometry</code>. The filter is
* applied to every element <code>Geometry</code>. A
* <code>GeometryFilter</code> can either record information about the
* <code>Geometry</code> or change the <code>Geometry</code> in some way.
* <code>GeometryFilter</code> is an example of the Gang-of-Four Visitor
* pattern.
*/
jsts.geom.GeometryFilter = function() {
};
/**
* Performs an operation with or on <code>geom</code>.
*
* @param {Geometry}
* geom a <code>Geometry</code> to which the filter is applied.
*/
jsts.geom.GeometryFilter.prototype.filter = function(geom) {
throw new jsts.error.AbstractMethodInvocationError();
};
/* ======================================================================
jsts/geom/util/PolygonExtracter.js
====================================================================== */
/* Copyright (c) 2011 by The Authors.
* Published under the LGPL 2.1 license.
* See /license-notice.txt for the full text of the license notice.
* See /license.txt for the full text of the license.
*/
/**
* @requires jsts/geom/GeometryFilter.js
*/
/**
* Extracts all the {@link Polygon} elements from a {@link Geometry}.
*
* Constructs a PolygonExtracterFilter with a list in which to store Polygons
* found.
*
* @param {Array}
* comps
* @extends {jsts.geom.GeometryFilter}
* @see GeometryExtracter
* @constructor
*/
jsts.geom.util.PolygonExtracter = function(comps) {
this.comps = comps;
};
jsts.geom.util.PolygonExtracter.prototype = new jsts.geom.GeometryFilter();
/**
* @private
*/
jsts.geom.util.PolygonExtracter.prototype.comps = null;
/**
* Extracts the {@link Polygon} elements from a single {@link Geometry} and adds
* them to the provided {@link List}.
*
* @param {jsts.geom.Geometry}
* geom the geometry from which to extract.
* @param {Array}
* list the list to add the extracted elements to.
* @return {Array}
*/
jsts.geom.util.PolygonExtracter.getPolygons = function(geom, list) {
if (list === undefined) {
list = [];
}
if (geom instanceof jsts.geom.Polygon) {
list.push(geom);
} else if (geom instanceof jsts.geom.GeometryCollection) {
geom.apply(new jsts.geom.util.PolygonExtracter(list));
}
// skip non-Polygonal elemental geometries
return list;
};
/**
* @param {jsts.geom.Geometry}
* geom
*/
jsts.geom.util.PolygonExtracter.prototype.filter = function(geom) {
if (geom instanceof jsts.geom.Polygon)
this.comps.push(geom);
};
/* ======================================================================
jsts/io/WKTParser.js
====================================================================== */
/* Copyright (c) 2011 by The Authors.
* Published under the LGPL 2.1 license.
* See /license-notice.txt for the full text of the license notice.
* See /license.txt for the full text of the license.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the Clear BSD license.
* See http://svn.openlayers.org/trunk/openlayers/license.txt for the
* full text of the license.
*/
/**
* Class for reading and writing Well-Known Text.
*
* NOTE: Adapted from OpenLayers 2.11 implementation.
*/
/**
* Create a new parser for WKT
*
* @param {}
* geometryFactory
* @return An instance of WKTParser.
*/
jsts.io.WKTParser = function(geometryFactory) {
this.geometryFactory = geometryFactory || new jsts.geom.GeometryFactory();
this.regExes = {
'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
'emptyTypeStr': /^\s*(\w+)\s*EMPTY\s*$/,
'spaces': /\s+/,
'parenComma': /\)\s*,\s*\(/,
'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
'trimParens': /^\s*\(?(.*?)\)?\s*$/
};
};
/**
* Deserialize a WKT string and return a geometry. Supports WKT for POINT,
* MULTIPOINT, LINESTRING, LINEARRING, MULTILINESTRING, POLYGON, MULTIPOLYGON,
* and GEOMETRYCOLLECTION.
*
* @param {String}
* wkt A WKT string.
* @return {jsts.geom.Geometry} A geometry instance.
*/
jsts.io.WKTParser.prototype.read = function(wkt) {
var geometry, type, str;
wkt = wkt.replace(/[\n\r]/g, ' ');
var matches = this.regExes.typeStr.exec(wkt);
if (wkt.search('EMPTY') !== -1) {
matches = this.regExes.emptyTypeStr.exec(wkt);
matches[2] = undefined;
}
if (matches) {
type = matches[1].toLowerCase();
str = matches[2];
if (this.parse[type]) {
geometry = this.parse[type].apply(this, [str]);
}
}
if (geometry === undefined)
throw new Error('Could not parse WKT ' + wkt);
return geometry;
};
/**
* Serialize a geometry into a WKT string.
*
* @param {jsts.geom.Geometry}
* geometry A feature or array of features.
* @return {String} The WKT string representation of the input geometries.
*/
jsts.io.WKTParser.prototype.write = function(geometry) {
return this.extractGeometry(geometry);
};
/**
* Entry point to construct the WKT for a single Geometry object.
*
* @param {jsts.geom.Geometry}
* geometry
*
* @return {String} A WKT string of representing the geometry.
*/
jsts.io.WKTParser.prototype.extractGeometry = function(geometry) {
var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
if (!this.extract[type]) {
return null;
}
var wktType = type.toUpperCase();
var data;
if (geometry.isEmpty()) {
data = wktType + ' EMPTY';
} else {
data = wktType + '(' + this.extract[type].apply(this, [geometry]) + ')';
}
return data;
};
/**
* Object with properties corresponding to the geometry types. Property values
* are functions that do the actual data extraction.
*/
jsts.io.WKTParser.prototype.extract = {
'coordinate': function(coordinate) {
return coordinate.x + ' ' + coordinate.y;
},
/**
* Return a space delimited string of point coordinates.
*
* @param {jsts.geom.Point}
* point
* @return {String} A string of coordinates representing the point.
*/
'point': function(point) {
return point.coordinate.x + ' ' + point.coordinate.y;
},
/**
* Return a comma delimited string of point coordinates from a multipoint.
*
* @param {jsts.geom.MultiPoint>}
* multipoint
* @return {String} A string of point coordinate strings representing the
* multipoint.
*/
'multipoint': function(multipoint) {
var array = [];
for ( var i = 0, len = multipoint.geometries.length; i < len; ++i) {
array.push('(' +
this.extract.point.apply(this, [multipoint.geometries[i]]) + ')');
}
return array.join(',');
},
/**
* Return a comma delimited string of point coordinates from a line.
*
* @param {jsts.geom.LineString>}
* linestring
* @return {String} A string of point coordinate strings representing the
* linestring.
*/
'linestring': function(linestring) {
var array = [];
for ( var i = 0, len = linestring.points.length; i < len; ++i) {
array.push(this.extract.coordinate.apply(this, [linestring.points[i]]));
}
return array.join(',');
},
/**
* Return a comma delimited string of linestring strings from a
* multilinestring.
*
* @param {jsts.geom.MultiLineString>}
* multilinestring
* @return {String} A string of of linestring strings representing the
* multilinestring.
*/
'multilinestring': function(multilinestring) {
var array = [];
for ( var i = 0, len = multilinestring.geometries.length; i < len; ++i) {
array.push('(' +
this.extract.linestring.apply(this, [multilinestring.geometries[i]]) +
')');
}
return array.join(',');
},
/**
* Return a comma delimited string of linear ring arrays from a polygon.
*
* @param {jsts.geom.Polygon>}
* polygon
* @return {String} An array of linear ring arrays representing the polygon.
*/
'polygon': function(polygon) {
var array = [];
array.push('(' + this.extract.linestring.apply(this, [polygon.shell]) + ')');
for ( var i = 0, len = polygon.holes.length; i < len; ++i) {
array.push('(' + this.extract.linestring.apply(this, [polygon.holes[i]]) + ')');
}
return array.join(',');
},
/**
* Return an array of polygon arrays from a multipolygon.
*
* @param {jsts.geom.MultiPolygon>}
* multipolygon
* @return {String} An array of polygon arrays representing the multipolygon.
*/
'multipolygon': function(multipolygon) {
var array = [];
for ( var i = 0, len = multipolygon.geometries.length; i < len; ++i) {
array.push('(' + this.extract.polygon.apply(this, [multipolygon.geometries[i]]) + ')');
}
return array.join(',');
},
/**
* Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an
* geometrycollection.
*
* @param {jsts.geom.GeometryCollection>}
* collection
* @return {String} internal WKT representation of the collection.
*/
'geometrycollection': function(collection) {
var array = [];
for ( var i = 0, len = collection.geometries.length; i < len; ++i) {
array.push(this.extractGeometry.apply(this, [collection.geometries[i]]));
}
return array.join(',');
}
};
/**
* Object with properties corresponding to the geometry types. Property values
* are functions that do the actual parsing.
*/
jsts.io.WKTParser.prototype.parse = {
/**
* Return point geometry given a point WKT fragment.
*
* @param {String}
* str A WKT fragment representing the point.
* @return {jsts.geom.Point} A point geometry.
* @private
*/
'point': function(str) {
if (str === undefined) {
return this.geometryFactory.createPoint(null);
}
var coords = str.trim().split(this.regExes.spaces);
return this.geometryFactory.createPoint(new jsts.geom.Coordinate(coords[0],
coords[1]));
},
/**
* Return a multipoint geometry given a multipoint WKT fragment.
*
* @param {String}
* A WKT fragment representing the multipoint.
* @return {jsts.geom.Point} A multipoint feature.
* @private
*/
'multipoint': function(str) {
if (str === undefined) {
return this.geometryFactory.createMultiPoint(null);
}
var point;
var points = str.trim().split(',');
var components = [];
for ( var i = 0, len = points.length; i < len; ++i) {
point = points[i].replace(this.regExes.trimParens, '$1');
components.push(this.parse.point.apply(this, [point]));
}
return this.geometryFactory.createMultiPoint(components);
},
/**
* Return a linestring geometry given a linestring WKT fragment.
*
* @param {String}
* A WKT fragment representing the linestring.
* @return {jsts.geom.LineString} A linestring geometry.
* @private
*/
'linestring': function(str) {
if (str === undefined) {
return this.geometryFactory.createLineString(null);
}
var points = str.trim().split(',');
var components = [];
var coords;
for ( var i = 0, len = points.length; i < len; ++i) {
coords = points[i].trim().split(this.regExes.spaces);
components.push(new jsts.geom.Coordinate(coords[0], coords[1]));
}
return this.geometryFactory.createLineString(components);
},
/**
* Return a linearring geometry given a linearring WKT fragment.
*
* @param {String}
* A WKT fragment representing the linearring.
* @return {jsts.geom.LinearRing} A linearring geometry.
* @private
*/
'linearring': function(str) {
if (str === undefined) {
return this.geometryFactory.createLinearRing(null);
}
var points = str.trim().split(',');
var components = [];
var coords;
for ( var i = 0, len = points.length; i < len; ++i) {
coords = points[i].trim().split(this.regExes.spaces);
components.push(new jsts.geom.Coordinate(coords[0], coords[1]));
}
return this.geometryFactory.createLinearRing(components);
},
/**
* Return a multilinestring geometry given a multilinestring WKT fragment.
*
* @param {String}
* A WKT fragment representing the multilinestring.
* @return {jsts.geom.MultiLineString} A multilinestring geometry.
* @private
*/
'multilinestring': function(str) {
if (str === undefined) {
return this.geometryFactory.createMultiLineString(null);
}
var line;
var lines = str.trim().split(this.regExes.parenComma);
var components = [];
for ( var i = 0, len = lines.length; i < len; ++i) {
line = lines[i].replace(this.regExes.trimParens, '$1');
components.push(this.parse.linestring.apply(this, [line]));
}
return this.geometryFactory.createMultiLineString(components);
},
/**
* Return a polygon geometry given a polygon WKT fragment.
*
* @param {String}
* A WKT fragment representing the polygon.
* @return {jsts.geom.Polygon} A polygon geometry.
* @private
*/
'polygon': function(str) {
if (str === undefined) {
return this.geometryFactory.createPolygon(null);
}
var ring, linestring, linearring;
var rings = str.trim().split(this.regExes.parenComma);
var shell;
var holes = [];
for ( var i = 0, len = rings.length; i < len; ++i) {
ring = rings[i].replace(this.regExes.trimParens, '$1');
linestring = this.parse.linestring.apply(this, [ring]);
linearring = this.geometryFactory.createLinearRing(linestring.points);
if (i === 0) {
shell = linearring;
} else {
holes.push(linearring);
}
}
return this.geometryFactory.createPolygon(shell, holes);
},
/**
* Return a multipolygon geometry given a multipolygon WKT fragment.
*
* @param {String}
* A WKT fragment representing the multipolygon.
* @return {jsts.geom.MultiPolygon} A multipolygon geometry.
* @private
*/
'multipolygon': function(str) {
if (str === undefined) {
return this.geometryFactory.createMultiPolygon(null);
}
var polygon;
var polygons = str.trim().split(this.regExes.doubleParenComma);
var components = [];
for ( var i = 0, len = polygons.length; i < len; ++i) {
polygon = polygons[i].replace(this.regExes.trimParens, '$1');
components.push(this.parse.polygon.apply(this, [polygon]));
}
return this.geometryFactory.createMultiPolygon(components);
},
/**
* Return a geometrycollection given a geometrycollection WKT fragment.
*
* @param {String}
* A WKT fragment representing the geometrycollection.
* @return {jsts.geom.GeometryCollection}
* @private
*/
'geometrycollection': function(str) {
if (str === undefined) {
return this.geometryFactory.createGeometryCollection(null);
}
// separate components of the collection with |
str = str.replace(/,\s*([A-Za-z])/g, '|$1');
var wktArray = str.trim().split('|');
var components = [];
for ( var i = 0, len = wktArray.length; i < len; ++i) {
components.push(jsts.io.WKTParser.prototype.read.apply(this,
[wktArray[i]]));
}
return this.geometryFactory.createGeometryCollection(components);
}
};
/* ======================================================================
jsts/index/ItemVisitor.js
====================================================================== */
/* Copyright (c) 2011 by The Authors.
* Published under the LGPL 2.1 license.
* See /license-notice.txt for the full text of the license notice.
* See /license.txt for the full text of the license.
*/
/**
* A visitor for items in an index.
*
* @interface
*/
jsts.index.ItemVisitor = function() {
};
/**
* @param {Object} item
* @public
*/
jsts.index.ItemVisitor.prototype.visitItem = function() {
throw new jsts.error.AbstractMethodInvocationError();
};
/* ======================================================================
jsts/algorithm/CGAlgorithms.js
====================================================================== */
/* Copyright (c) 2011 by The Authors.
* Published under the LGPL 2.1 license.
* See /license-notice.txt for the full text of the license notice.
* See /license.txt for the full text of the license.
*/
/**
* Specifies and implements various fundamental Computational Geometric
* algorithms. The algorithms supplied in this class are robust for
* double-precision floating point.
*
* @constructor
*/
jsts.algorithm.CGAlgorithms = function() {
};
/**
* A value that indicates an orientation of clockwise, or a right turn.
*/
jsts.algorithm.CGAlgorithms.CLOCKWISE = -1;
/**
* A value that indicates an orientation of clockwise, or a right turn.
*/
jsts.algorithm.CGAlgorithms.RIGHT = jsts.algorithm.CGAlgorithms.CLOCKWISE;
/**
* A value that indicates an orientation of counterclockwise, or a left turn.
*/
jsts.algorithm.CGAlgorithms.COUNTERCLOCKWISE = 1;
/**
* A value that indicates an orientation of counterclockwise, or a left turn.
*/
jsts.algorithm.CGAlgorithms.LEFT = jsts.algorithm.CGAlgorithms.COUNTERCLOCKWISE;
/**
* A value that indicates an orientation of collinear, or no turn (straight).
*/
jsts.algorithm.CGAlgorithms.COLLINEAR = 0;
/**
* A value that indicates an orientation of collinear, or no turn (straight).
*/
jsts.algorithm.CGAlgorithms.STRAIGHT = jsts.algorithm.CGAlgorithms.COLLINEAR;
/**
* Returns the index of the direction of the point <code>q</code> relative to
* a vector specified by <code>p1-p2</code>.
*
* @param {jsts.geom.Coordinate}
* p1 the origin point of the vector.
* @param {jsts.geom.Coordinate}
* p2 the final point of the vector.
* @param {jsts.geom.Coordinate}
* q the point to compute the direction to.
*
* @return {Number} 1 if q is counter-clockwise (left) from p1-p2.
* @return {Number} -1 if q is clockwise (right) from p1-p2.
* @return {Number} 0 if q is collinear with p1-p2.
*/
jsts.algorithm.CGAlgorithms.orientationIndex = function(p1, p2, q) {
/**
* MD - 9 Aug 2010 It seems that the basic algorithm is slightly orientation
* dependent, when computing the orientation of a point very close to a line.
* This is possibly due to the arithmetic in the translation to the origin.
*
* For instance, the following situation produces identical results in spite
* of the inverse orientation of the line segment:
*
* Coordinate p0 = new Coordinate(219.3649559090992, 140.84159161824724);
* Coordinate p1 = new Coordinate(168.9018919682399, -5.713787599646864);
*
* Coordinate p = new Coordinate(186.80814046338352, 46.28973405831556); int
* orient = orientationIndex(p0, p1, p); int orientInv = orientationIndex(p1,
* p0, p);
*
* A way to force consistent results is to normalize the orientation of the
* vector using the following code. However, this may make the results of
* orientationIndex inconsistent through the triangle of points, so it's not
* clear this is an appropriate patch.
*
*/
var dx1, dy1, dx2, dy2;
dx1 = p2.x - p1.x;
dy1 = p2.y - p1.y;
dx2 = q.x - p2.x;
dy2 = q.y - p2.y;
return jsts.algorithm.RobustDeterminant.signOfDet2x2(dx1, dy1, dx2, dy2);
};
/**
* Tests whether a point lies inside or on a ring. The ring may be oriented in
* either direction. A point lying exactly on the ring boundary is considered to
* be inside the ring.
* <p>
* This method does <i>not</i> first check the point against the envelope of
* the ring.
*
* @param {jsts.geom.Coordinate}
* p point to check for ring inclusion.
* @param {Array{jsts.geom.Coordinate}}
* ring an array of coordinates representing the ring (which must have
* first point identical to last point)
* @return {Boolean} true if p is inside ring.
*
* @see locatePointInRing
*/
jsts.algorithm.CGAlgorithms.isPointInRing = function(p, ring) {
return jsts.algorithm.CGAlgorithms.locatePointInRing(p, ring) !== jsts.geom.Location.EXTERIOR;
};
/**
* Determines whether a point lies in the interior, on the boundary, or in the
* exterior of a ring. The ring may be oriented in either direction.
* <p>
* This method does <i>not</i> first check the point against the envelope of
* the ring.
*
* @param {jsts.geom.Coordinate}
* p point to check for ring inclusion.
* @param {Array{jsts.geom.Coordinate}}
* ring an array of coordinates representing the ring (which must have
* first point identical to last point)
* @return {jsts.geom.Location} the {@link Location} of p relative to the ring.
*/
jsts.algorithm.CGAlgorithms.locatePointInRing = function(p, ring) {
return jsts.algorithm.RayCrossingCounter.locatePointInRing(p, ring);
};
/**
* Tests whether a point lies on the line segments defined by a list of
* coordinates.
*
* @param {jsts.geom.Coordinate}
* p the coordinate to test.
* @param {Array{jsts.geom.Coordinate}}
* pt An array of coordinates defining line segments
* @return {Boolean} true if the point is a vertex of the line or lies in the
* interior of a line segment in the linestring.
*/
jsts.algorithm.CGAlgorithms.isOnLine = function(p, pt) {
var lineIntersector, i, il, p0, p1;
lineIntersector = new jsts.algorithm.RobustLineIntersector();
for (i = 1, il = pt.length; i < il; i++) {
p0 = pt[i - 1];
p1 = pt[i];
lineIntersector.computeIntersection(p, p0, p1);
if (lineIntersector.hasIntersection()) {
return true;
}
}
return false;
};
/**
* Computes whether a ring defined by an array of {@link Coordinate}s is
* oriented counter-clockwise.
* <ul>
* <li>The list of points is assumed to have the first and last points equal.
* <li>This will handle coordinate lists which contain repeated points.
* </ul>
* This algorithm is <b>only</b> guaranteed to work with valid rings. If the
* ring is invalid (e.g. self-crosses or touches), the computed result may not
* be correct.
*
* @param {Array{jsts.geom.Coordinate}}
* ring an array of Coordinates forming a ring
* @return {Boolean} true if the ring is oriented counter-clockwise.
* @throws IllegalArgumentException
* if there are too few points to determine orientation (< 3)
*/
jsts.algorithm.CGAlgorithms.isCCW = function(ring) {
var nPts, hiPt, hiIndex, p, iPrev, iNext, prev, next, i, disc, isCCW;
// # of points without closing endpoint
nPts = ring.length - 1;
// sanity check
if (nPts < 3) {
throw new jsts.IllegalArgumentError(
'Ring has fewer than 3 points, so orientation cannot be determined');
}
// find highets point
hiPt = ring[0];
hiIndex = 0;
i = 1;
for (i; i <= nPts; i++) {
p = ring[i];
if (p.y > hiPt.y) {
hiPt = p;
hiIndex = i;
}
}
// find distinct point before highest point
iPrev = hiIndex;
do {
iPrev = iPrev - 1;
if (iPrev < 0) {
iPrev = nPts;
}
} while (ring[iPrev].equals2D(hiPt) && iPrev !== hiIndex);
// find distinct point after highest point
iNext = hiIndex;
do {
iNext = (iNext + 1) % nPts;
} while (ring[iNext].equals2D(hiPt) && iNext !== hiIndex);
prev = ring[iPrev];
next = ring[iNext];
/**
* This check catches cases where the ring contains an A-B-A configuration of
* points. This can happen if the ring does not contain 3 distinct points
* (including the case where the input array has fewer than 4 elements), or it
* contains coincident line segments.
*/
if (prev.equals2D(hiPt) || next.equals2D(hiPt) || prev.equals2D(next)) {
return false;
}
disc = jsts.algorithm.CGAlgorithms.computeOrientation(prev, hiPt, next);
/**
* If disc is exactly 0, lines are collinear. There are two possible cases:
* (1) the lines lie along the x axis in opposite directions (2) the lines lie
* on top of one another
*
* (1) is handled by checking if next is left of prev ==> CCW (2) will never
* happen if the ring is valid, so don't check for it (Might want to assert
* this)
*/
isCCW = false;
if (disc === 0) {
// poly is CCW if prev x is right of next x
isCCW = (prev.x > next.x);
} else {
// if area is positive, points are ordered CCW
isCCW = (disc > 0);
}
return isCCW;
};
/**
* Computes the orientation of a point q to the directed line segment p1-p2. The
* orientation of a point relative to a directed line segment indicates which
* way you turn to get to q after travelling from p1 to p2.
*
* @param {jsts.geom.Coordinate}
* p1 First coordinate of the linesegment.
* @param {jsts.geom.Coordinate}
* p2 Second coordinate of the linesegment.
* @param {jsts.geom.Coordinate}
* q The point to calculate orientation of.
*
* @return {Number} 1 if q is counter-clockwise from p1-p2.
* @return {Number} -1 if q is clockwise from p1-p2.
* @return {Number} 0 if q is collinear with p1-p2.
*/
jsts.algorithm.CGAlgorithms.computeOrientation = function(p1, p2, q) {
return jsts.algorithm.CGAlgorithms.orientationIndex(p1, p2, q);
};
/**
* Computes the distance from a point p to a line segment AB
*
* Note: NON-ROBUST!
*
* @param {jsts.geom.Coordinate}
* p the point to compute the distance for.
* @param {jsts.geom.Coordinate}
* A one point of the line.
* @param {jsts.geom.Coordinate}
* B another point of the line (must be different to A).
* @return {Number} the distance from p to line segment AB.
*/
jsts.algorithm.CGAlgorithms.distancePointLine = function(p, A, B) {
if (!(A instanceof jsts.geom.Coordinate)) {
jsts.algorithm.CGAlgorithms.distancePointLine2.apply(this, arguments);
}
// if start = end, then just compute distance to one of the endpoints
if (A.x === B.x && A.y === B.y) {
return p.distance(A);
}
// otherwise use comp.graphics.algorithms Frequently Asked Questions method
/*(1) AC dot AB
r = ---------
||AB||^2
r has the following meaning:
r=0 P = A
r=1 P = B
r<0 P is on the backward extension of AB
r>1 P is on the forward extension of AB
0<r<1 P is interior to AB
*/
var r, s;
r = ((p.x - A.x) * (B.x - A.x) + (p.y - A.y) * (B.y - A.y)) /
((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
if (r <= 0.0) {
return p.distance(A);
}
if (r >= 1.0) {
return p.distance(B);
}
/*(2)
(Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
s = -----------------------------
L^2
Then the distance from C to P = |s|*L.
*/
s = ((A.y - p.y) * (B.x - A.x) - (A.x - p.x) * (B.y - A.y)) /
((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
return Math.abs(s) *
Math.sqrt(((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y)));
};
/**
* Computes the perpendicular distance from a point p to the (infinite) line
* containing the points AB
*
* @param {jsts.geom.Coordinate}
* p the point to compute the distance for.
* @param {jsts.geom.Coordinate}
* A one point of the line.
* @param {jsts.geom.Coordinate}
* B another point of the line (must be different to A).
* @return {Number} the distance from p to line AB.
*/
jsts.algorithm.CGAlgorithms.distancePointLinePerpendicular = function(p, A, B) {
// use comp.graphics.algorithms Frequently Asked Questions method
/*(2)
(Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
s = -----------------------------
L^2
Then the distance from C to P = |s|*L.
*/
var s = ((A.y - p.y) * (B.x - A.x) - (A.x - p.x) * (B.y - A.y)) /
((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
return Math.abs(s) *
Math.sqrt(((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y)));
};
/**
* Computes the distance from a point to a sequence of line segments.
*
* @param {jsts.geom.Coordinate}
* p a point.
* @param {Array{jsts.geom.Coordinate}}
* line a sequence of contiguous line segments defined by their
* vertices
* @return {Number} the minimum distance between the point and the line
* segments.
*/
jsts.algorithm.CGAlgorithms.distancePointLine2 = function(p, line) {
var minDistance, i, il, dist;
if (line.length === 0) {
throw new jsts.error.IllegalArgumentError(
'Line array must contain at least one vertex');
}
minDistance = p.distance(line[0]);
for (i = 0, il = line.length - 1; i < il; i++) {
dist = jsts.algorithm.CGAlgorithms.distancePointLine(p, line[i],
line[i + 1]);
if (dist < minDistance) {
minDistance = dist;
}
}
return minDistance;
};
/**
* Computes the distance from a line segment AB to a line segment CD
*
* Note: NON-ROBUST!
*
* @param {jsts.geom.Coordinate}
* A a point of one line.
* @param {jsts.geom.Coordinate}
* B the second point of (must be different to A).
* @param {jsts.geom.Coordinate}
* C one point of the line.
* @param {jsts.geom.Coordinate}
* D another point of the line (must be different to A).
* @return {Number} the distance.
*/
jsts.algorithm.CGAlgorithms.distanceLineLine = function(A, B, C, D) {
// check for zero-length segments
if (A.equals(B)) {
return jsts.algorithm.CGAlgorithms.distancePointLine(A, C, D);
}
if (C.equals(D)) {
return jsts.algorithm.CGAlgorithms.distancePointLine(D, A, B);
}
// AB and CD are line segments
/* from comp.graphics.algo
Solving the above for r and s yields
(Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy)
r = ----------------------------- (eqn 1)
(Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
(Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
s = ----------------------------- (eqn 2)
(Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
Let P be the position vector of the intersection point, then
P=A+r(B-A) or
Px=Ax+r(Bx-Ax)
Py=Ay+r(By-Ay)
By examining the values of r & s, you can also determine some other
limiting conditions:
If 0<=r<=1 & 0<=s<=1, intersection exists
r<0 or r>1 or s<0 or s>1 line segments do not intersect
If the denominator in eqn 1 is zero, AB & CD are parallel
If the numerator in eqn 1 is also zero, AB & CD are collinear.
*/
var r_top, r_bot, s_top, s_bot, s, r;
r_top = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y);
r_bot = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x);
s_top = (A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y);
s_bot = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x);
if ((r_bot === 0) || (s_bot === 0)) {
return Math.min(jsts.algorithm.CGAlgorithms.distancePointLine(A, C, D),
Math.min(jsts.algorithm.CGAlgorithms.distancePointLine(B, C, D), Math
.min(jsts.algorithm.CGAlgorithms.distancePointLine(C, A, B),
jsts.algorithm.CGAlgorithms.distancePointLine(D, A, B))));
}
s = s_top / s_bot;
r = r_top / r_bot;
if ((r < 0) || (r > 1) || (s < 0) || (s > 1)) {
// no intersection
return Math.min(jsts.algorithm.CGAlgorithms.distancePointLine(A, C, D),
Math.min(jsts.algorithm.CGAlgorithms.distancePointLine(B, C, D), Math
.min(jsts.algorithm.CGAlgorithms.distancePointLine(C, A, B),
jsts.algorithm.CGAlgorithms.distancePointLine(D, A, B))));
}
return 0.0; // intersection exists
};
/**
* Computes the signed area for a ring. The signed area is positive if the ring
* is oriented CW, negative if the ring is oriented CCW, and zero if the ring is
* degenerate or flat.
*
* @param {Array{jsts.geom.Coordinate}}
* ring the coordinates forming the ring
* @return {Number} the signed area of the ring.
*/
jsts.algorithm.CGAlgorithms.signedArea = function(ring) {
if (ring.length < 3) {
return 0.0;
}
var sum, i, il, bx, by, cx, cy;
sum = 0.0;
for (i = 0, il = ring.length - 1; i < il; i++) {
bx = ring[i].x;
by = ring[i].y;
cx = ring[i + 1].x;
cy = ring[i + 1].y;
sum += (bx + cx) * (cy - by);
}
return -sum / 2.0;
};
/**
* Computes the signed area for a ring. The signed area is:
* <ul>
* <li>positive if the ring is oriented CW
* <li>negative if the ring is oriented CCW
* <li>zero if the ring is degenerate or flat
* </ul>
*
* @param {Array{jsts.geom.Coordinate}}
* ring the coordinates forming the ring
* @return {Number} the signed area of the ring.
*/
jsts.algorithm.CGAlgorithms.signedArea = function(ring) {
var n, sum, p, bx, by, i, cx, cy;
n = ring.length;
if (n < 3) {
return 0.0;
}
sum = 0.0;
p = ring[0];
bx = p.x;
by = p.y;
for (i = 1; i < n; i++) {
p = ring[i];
cx = p.x;
cy = p.y;
sum += (bx + cx) * (cy - by);
bx = cx;
by = cy;
}
return -sum / 2.0;
};
/**
* Computes the length of a linestring specified by a sequence of points.
*
* NOTE: This is renamed from length() to computeLength() because 'length' is a
* reserved keyword in javascript.
*
* @param {Array{jsts.geom.Coordinate}}
* pts the points specifying the linestring
* @return {Number} the length of the linestring.
*/
jsts.algorithm.CGAlgorithms.computeLength = function(pts) {
// optimized for processing CoordinateSequences
var n = pts.length, len, x0, y0, x1, y1, dx, dy, p, i, il;
if (n <= 1) {
return 0.0;
}
len = 0.0;
p = pts[0];
x0 = p.x;
y0 = p.y;
i = 1, il = n;
for (i; i < n; i++) {
p = pts[i];
x1 = p.x;
y1 = p.y;
dx = x1 - x0;
dy = y1 - y0;
len += Math.sqrt(dx * dx + dy * dy);
x0 = x1;
y0 = y1;
}
return len;
};
/**
* @see {jsts.algorithm.CGAlgorithms.computeLength} Since 'length' is a reserved
* keyword in javascript this function does not act as a function. Please
* use 'computeLength' instead.
*/
jsts.algorithm.CGAlgorithms.length = function() {};
/* ======================================================================
jsts/algorithm/Angle.js
====================================================================== */
/* Copyright (c) 2011 by The Authors.
* Published under the LGPL 2.1 license.
* See /license-notice.txt for the full text of the license notice.
* See /license.txt for the full text of the license.
*/
/**
* Utility functions for working with angles.
* Unless otherwise noted, methods in this class express angles in radians.
*
* @requires jsts/algorithm/CGAlgorithms.js
*
* @constructor
*/
jsts.algorithm.Angle = function() {
};
/**
* Pi*2
*/
jsts.algorithm.Angle.PI_TIMES_2 = 2.0 * Math.PI;
/**
* Pi/2
*/
jsts.algorithm.Angle.PI_OVER_2 = Math.PI / 2.0;
/**
* Pi/4
*/
jsts.algorithm.Angle.PI_OVER_4 = Math.PI / 4.0;
/**
* Constant representing counterclockwise orientation
*/
jsts.algorithm.Angle.COUNTERCLOCKWISE = jsts.algorithm.CGAlgorithms.COUNTERCLOCKWISE;
/**
* Constant representing clockwise orientation
*/
jsts.algorithm.Angle.CLOCKWISE = jsts.algorithm.CGAlgorithms.CLOCKWISE;
/**
* Constant representing no orientation
*/
jsts.algorithm.Angle.NONE = jsts.algorithm.CGAlgorithms.COLLINEAR;
/**
* Converts from radians to degrees.
*
* @param {Number}
* radians an angle in radians.
* @return {Number}
* the angle in degrees.
*/
jsts.algorithm.Angle.toDegrees = function(radians) {
return (radians * 180) / Math.PI;
};
/**
* Converts from degrees to radians.
*
* @param {Number}
* angleDegrees an angle in degrees.
* @return {Number}
* the angle in radians.
*/
jsts.algorithm.Angle.toRadians = function(angleDegrees) {
return (angleDegrees * Math.PI) / 180.0;
};
/**
* Returns the angle
* Calls correct angle* depending on argument
*
* @return {Number}
* The angle in radians.
*/
jsts.algorithm.Angle.angle = function() {
if (arguments.length === 1) {
return jsts.algorithm.Angle.angleFromOrigo(arguments[0]);
}else {
return jsts.algorithm.Angle.angleBetweenCoords(arguments[0], arguments[1]);
}
};
/**
* Returns the angle of the vector from p0 to p1,
* relative to the positive X-axis.
* The angle is normalized to be in the range [ -Pi, Pi ].
*
* @param {jsts.geom.Coordinate}
* p0 a coordinate.
* @param {jsts.geom.Coordinate}
* p1 a coordinate.
* @return {Number}
* the normalized angle (in radians) that p0-p1 makes with the positive
* x-axis.
*/
jsts.algorithm.Angle.angleBetweenCoords = function(p0, p1) {
var dx, dy;
dx = p1.x - p0.x;
dy = p1.y - p0.y;
return Math.atan2(dy, dx);
};
/**
* Returns the angle that the vector from (0,0) to p,
* relative to the positive X-axis.
* The angle is normalized to be in the range ( -Pi, Pi ].
*
* @param {jsts.geom.Coordinate}
* p a coordinate.
* @return {Number}
* the normalized angle (in radians) that p makes with the positive
* x-axis.
*/
jsts.algorithm.Angle.angleFromOrigo = function(p) {
return Math.atan2(p.y, p.x);
};
/**
* Tests whether the angle between p0-p1-p2 is acute.
* An angle is acute if it is less than 90 degrees.
* <p>
* Note: this implementation is not precise (determistic) for angles very close to 90 degrees.
*
* @param {jsts.geom.Coordinate}
* p0 an endpoint of the angle.
* @param {jsts.geom.Coordinate}
* p1 the base of the angle.
* @param {jsts.geom.Coordinate}
* p2 the other endpoint of the angle.
* @return {Boolean}
* true if the angle is acute.
*/
jsts.algorithm.Angle.isAcute = function(p0, p1, p2) {
var dx0, dy0, dx1, dy1, dotprod;
//relies on fact that A dot B is positive if A ang B is acute
dx0 = p0.x - p1.x;
dy0 = p0.y - p1.y;
dx1 = p2.x - p1.x;
dy1 = p2.y - p1.y;
dotprod = dx0 * dx1 + dy0 * dy1;
return dotprod > 0;
};
/**
* Tests whether the angle between p0-p1-p2 is obtuse.
* An angle is obtuse if it is greater than 90 degrees.
* <p>
* Note: this implementation is not precise (determistic) for angles very close to 90 degrees.
*
* @param {jsts.geom.Coordinate}
* p0 an endpoint of the angle.
* @param {jsts.geom.Coordinate}
* p1 the base of the angle.
* @param {jsts.geom.Coordinate}
* p2 the other endpoint of the angle.
* @return {Boolean}
* true if the angle is obtuse.
*/
jsts.algorithm.Angle.isObtuse = function(p0, p1, p2) {
var dx0, dy0, dx1, dy1, dotprod;
//relies on fact that A dot B is negative iff A ang B is obtuse
dx0 = p0.x - p1.x;
dy0 = p0.y - p1.y;
dx1 = p2.x - p1.x;
dy1 = p2.y - p1.y;
dotprod = dx0 * dx1 + dy0 * dy1;
return dotprod < 0;
};
/**
* Returns the unoriented smallest angle between two vectors.
* The computed angle will be in the range [0, Pi).
*
* @param {jsts.geom.Coordinate}
* tip1 the tip of one vector.
* @param {jsts.geom.Coordinate}
* tail the tail of each vector.
* @param {jsts.geom.Coordinate}
* tip2 the tip of the other vector.
* @return {Number}
* the angle between tail-tip1 and tail-tip2.
*/
jsts.algorithm.Angle.angleBetween = function(tip1, tail, tip2) {
var a1, a2;
a1 = jsts.algorithm.Angle.angle(tail, tip1);
a2 = jsts.algorithm.Angle.angle(tail, tip2);
return jsts.algorithm.Angle.diff(a1, a2);
};
/**
* Returns the oriented smallest angle between two vectors.
* The computed angle will be in the range (-Pi, Pi].
* A positive result corresponds to a counterclockwise rotation
* from v1 to v2;
* a negative result corresponds to a clockwise rotation.
*
* @param {jsts.geom.Coordinate}
* tip1 the tip of v1.
* @param {jsts.geom.Coordinate}
* tail the tail of each vector.
* @param {jsts.geom.Coordinate}
* tip2 the tip of v2.
* @return {Number}
* the angle between v1 and v2, relative to v1.
*/
jsts.algorithm.Angle.angleBetweenOriented = function(tip1, tail, tip2) {
var a1, a2, angDel;
a1 = jsts.algorithm.Angle.angle(tail, tip1);
a2 = jsts.algorithm.Angle.angle(tail, tip2);
angDel = a2 - a1;
// normalize, maintaining orientation
if (angDel <= -Math.PI) {
return angDel + jsts.algorithm.Angle.PI_TIMES_2;
}
if (angDel > Math.PI) {
return angDel - jsts.algorithm.Angle.PI_TIMES_2;
}
return angDel;
};
/**
* Computes the interior angle between two segments of a ring. The ring is
* assumed to be oriented in a clockwise direction. The computed angle will be
* in the range [0, 2Pi]
*
* @param {jsts.geom.Coordinate}
* p0 a point of the ring.
* @param {jsts.geom.Coordinate}
* p1 the next point of the ring.
* @param {jsts.geom.Coordinate}
* p2 the next point of the ring.
* @return {Number}
* the interior angle based at <code>p1.</code>
*/
jsts.algorithm.Angle.interiorAngle = function(p0, p1, p2) {
var anglePrev, angleNext;
anglePrev = jsts.algorithm.Angle.angle(p1, p0);
angleNext = jsts.algorithm.Angle.angle(p1, p2);
return Math.abs(angleNext - anglePrev);
};
/**
* Returns whether an angle must turn clockwise or counterclockwise
* to overlap another angle.
*
* @param {Number}
* ang1 an angle (in radians).
* @param {Number}
* ang2 an angle (in radians).
* @return {Number}
* whether a1 must turn CLOCKWISE, COUNTERCLOCKWISE or NONE to
* overlap a2.
*/
jsts.algorithm.Angle.getTurn = function(ang1, ang2) {
var crossproduct = Math.sin(ang2 - ang1);
if (crossproduct > 0) {
return jsts.algorithm.Angle.COUNTERCLOCKWISE;
}
if (crossproduct < 0) {
return jsts.algorithm.Angle.CLOCKWISE;
}
return jsts.algorithm.Angle.NONE;
};
/**
* Computes the normalized value of an angle, which is the
* equivalent angle in the range ( -Pi, Pi ].
*
* @param {Number}
* angle the angle to normalize.
* @return {Number}
* an equivalent angle in the range (-Pi, Pi].
*/
jsts.algorithm.Angle.normalize = function(angle) {
while (angle > Math.PI) {
angle -= jsts.algorithm.Angle.PI_TIMES_2;
}
while (angle <= -Math.PI) {
angle += jsts.algorithm.Angle.PI_TIMES_2;
}
return angle;
};
/**
* Computes the normalized positive value of an angle, which is the
* equivalent angle in the range [ 0, 2*Pi ).
* E.g.:
* <ul>
* <li>normalizePositive(0.0) = 0.0
* <li>normalizePositive(-PI) = PI
* <li>normalizePositive(-2PI) = 0.0
* <li>normalizePositive(-3PI) = PI
* <li>normalizePositive(-4PI) = 0
* <li>normalizePositive(PI) = PI
* <li>normalizePositive(2PI) = 0.0
* <li>normalizePositive(3PI) = PI
* <li>normalizePositive(4PI) = 0.0
* </ul>
*
* @param {Number}
* angle the angle to normalize, in radians.
* @return {Number}
* an equivalent positive angle.
*/
jsts.algorithm.Angle.normalizePositive = function(angle) {
if (angle < 0.0) {
while (angle < 0.0) {
angle += jsts.algorithm.Angle.PI_TIMES_2;
}
// in case round-off error bumps the value over
if (angle >= jsts.algorithm.Angle.PI_TIMES_2) {
angle = 0.0;
}
}
else {
while (angle >= jsts.algorithm.Angle.PI_TIMES_2) {
angle -= jsts.algorithm.Angle.PI_TIMES_2;
}
// in case round-off error bumps the value under
if (angle < 0.0) {
angle = 0.0;
}
}
return angle;
};
/**
* Computes the unoriented smallest difference between two angles.
* The angles are assumed to be normalized to the range [-Pi, Pi].
* The result will be in the range [0, Pi].
*
* @param {Number}
* ang1 the angle of one vector (in [-Pi, Pi] ).
* @param {Number}
* ang2 the angle of the other vector (in range [-Pi, Pi] ).
* @return {Number}
* the angle (in radians) between the two vectors (in range [0, Pi] ).
*/
jsts.algorithm.Angle.diff = function(ang1, ang2) {
var delAngle;
if (ang1 < ang2) {
delAngle = ang2 - ang1;
} else {
delAngle = ang1 - ang2;
}
if (delAngle > Math.PI) {
delAngle = (2 * Math.PI) - delAngle;
}
return delAngle;
};
/* ======================================================================
jsts/geom/GeometryComponentFilter.js
====================================================================== */
/* Copyright (c) 2011 by The Authors.
* Published under the LGPL 2.1 license.
* See /license-notice.txt for the full text of the license notice.
* See /license.txt for the full text of the license.
*/
/**
* <code>Geometry</code> classes support the concept of applying a
* <code>GeometryComponentFilter</code> filter to the <code>Geometry</code>.
* The filter is applied to every component of the <code>Geometry</code> which
* is itself a <code>Geometry</code> and which does not itself contain any
* components. (For instance, all the {@link LinearRing}s in {@link Polygon}s
* are visited, but in a {@link MultiPolygon} the {@link Polygon}s themselves
* are not visited.) Thus the only classes of Geometry which must be handled as
* arguments to {@link #filter} are {@link LineString}s, {@link LinearRing}s
* and {@link Point}s.
* <p>
* A <code>GeometryComponentFilter</code> filter can either record information
* about the <code>Geometry</code> or change the <code>Geometry