com.vividsolutions.jts.linearref.LinearLocation Maven / Gradle / Ivy
The newest version!
/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* 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
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jts.linearref;
import com.vividsolutions.jts.geom.*;
/**
* Represents a location along a {@link LineString} or {@link MultiLineString}.
* The referenced geometry is not maintained within
* this location, but must be provided for operations which require it.
* Various methods are provided to manipulate the location value
* and query the geometry it references.
*/
public class LinearLocation
implements Comparable
{
/**
* Gets a location which refers to the end of a linear {@link Geometry}.
* @param linear the linear geometry
* @return a new LinearLocation
*/
public static LinearLocation getEndLocation(Geometry linear)
{
// assert: linear is LineString or MultiLineString
LinearLocation loc = new LinearLocation();
loc.setToEnd(linear);
return loc;
}
/**
* Computes the {@link Coordinate} of a point a given fraction
* along the line segment (p0, p1).
* If the fraction is greater than 1.0 the last
* point of the segment is returned.
* If the fraction is less than or equal to 0.0 the first point
* of the segment is returned.
* The Z ordinate is interpolated from the Z-ordinates of the given points,
* if they are specified.
*
* @param p0 the first point of the line segment
* @param p1 the last point of the line segment
* @param frac the length to the desired point
* @return the Coordinate of the desired point
*/
public static Coordinate pointAlongSegmentByFraction(Coordinate p0, Coordinate p1, double frac)
{
if (frac <= 0.0) return p0;
if (frac >= 1.0) return p1;
double x = (p1.x - p0.x) * frac + p0.x;
double y = (p1.y - p0.y) * frac + p0.y;
// interpolate Z value. If either input Z is NaN, result z will be NaN as well.
double z = (p1.z - p0.z) * frac + p0.z;
return new Coordinate(x, y, z);
}
private int componentIndex = 0;
private int segmentIndex = 0;
private double segmentFraction = 0.0;
/**
* Creates a location referring to the start of a linear geometry
*/
public LinearLocation()
{
}
public LinearLocation(int segmentIndex, double segmentFraction) {
this(0, segmentIndex, segmentFraction);
}
public LinearLocation(int componentIndex, int segmentIndex, double segmentFraction)
{
this.componentIndex = componentIndex;
this.segmentIndex = segmentIndex;
this.segmentFraction = segmentFraction;
normalize();
}
private LinearLocation(int componentIndex, int segmentIndex, double segmentFraction, boolean doNormalize)
{
this.componentIndex = componentIndex;
this.segmentIndex = segmentIndex;
this.segmentFraction = segmentFraction;
if (doNormalize)
normalize();
}
/**
* Creates a new location equal to a given one.
*
* @param loc a LinearLocation
*/
public LinearLocation(LinearLocation loc)
{
this.componentIndex = loc.componentIndex;
this.segmentIndex = loc.segmentIndex;
this.segmentFraction = loc.segmentFraction;
}
/**
* Ensures the individual values are locally valid.
* Does not ensure that the indexes are valid for
* a particular linear geometry.
*
* @see clamp
*/
private void normalize()
{
if (segmentFraction < 0.0) {
segmentFraction = 0.0;
}
if (segmentFraction > 1.0) {
segmentFraction = 1.0;
}
if (componentIndex < 0) {
componentIndex = 0;
segmentIndex = 0;
segmentFraction = 0.0;
}
if (segmentIndex < 0) {
segmentIndex = 0;
segmentFraction = 0.0;
}
if (segmentFraction == 1.0) {
segmentFraction = 0.0;
segmentIndex += 1;
}
}
/**
* Ensures the indexes are valid for a given linear {@link Geometry}.
*
* @param linear a linear geometry
*/
public void clamp(Geometry linear)
{
if (componentIndex >= linear.getNumGeometries()) {
setToEnd(linear);
return;
}
if (segmentIndex >= linear.getNumPoints()) {
LineString line = (LineString) linear.getGeometryN(componentIndex);
segmentIndex = line.getNumPoints() - 1;
segmentFraction = 1.0;
}
}
/**
* Snaps the value of this location to
* the nearest vertex on the given linear {@link Geometry},
* if the vertex is closer than minDistance.
*
* @param linearGeom a linear geometry
* @param minDistance the minimum allowable distance to a vertex
*/
public void snapToVertex(Geometry linearGeom, double minDistance)
{
if (segmentFraction <= 0.0 || segmentFraction >= 1.0)
return;
double segLen = getSegmentLength(linearGeom);
double lenToStart = segmentFraction * segLen;
double lenToEnd = segLen - lenToStart;
if (lenToStart <= lenToEnd && lenToStart < minDistance) {
segmentFraction = 0.0;
}
else if (lenToEnd <= lenToStart && lenToEnd < minDistance) {
segmentFraction = 1.0;
}
}
/**
* Gets the length of the segment in the given
* Geometry containing this location.
*
* @param linearGeom a linear geometry
* @return the length of the segment
*/
public double getSegmentLength(Geometry linearGeom)
{
LineString lineComp = (LineString) linearGeom.getGeometryN(componentIndex);
// ensure segment index is valid
int segIndex = segmentIndex;
if (segmentIndex >= lineComp.getNumPoints() - 1)
segIndex = lineComp.getNumPoints() - 2;
Coordinate p0 = lineComp.getCoordinateN(segIndex);
Coordinate p1 = lineComp.getCoordinateN(segIndex + 1);
return p0.distance(p1);
}
/**
* Sets the value of this location to
* refer to the end of a linear geometry.
*
* @param linear the linear geometry to use to set the end
*/
public void setToEnd(Geometry linear)
{
componentIndex = linear.getNumGeometries() - 1;
LineString lastLine = (LineString) linear.getGeometryN(componentIndex);
segmentIndex = lastLine.getNumPoints() - 1;
segmentFraction = 1.0;
}
/**
* Gets the component index for this location.
*
* @return the component index
*/
public int getComponentIndex() { return componentIndex; }
/**
* Gets the segment index for this location
*
* @return the segment index
*/
public int getSegmentIndex() { return segmentIndex; }
/**
* Gets the segment fraction for this location
*
* @return the segment fraction
*/
public double getSegmentFraction() { return segmentFraction; }
/**
* Tests whether this location refers to a vertex
*
* @return true if the location is a vertex
*/
public boolean isVertex()
{
return segmentFraction <= 0.0 || segmentFraction >= 1.0;
}
/**
* Gets the {@link Coordinate} along the
* given linear {@link Geometry} which is
* referenced by this location.
*
* @param linearGeom the linear geometry referenced by this location
* @return the Coordinate at the location
*/
public Coordinate getCoordinate(Geometry linearGeom)
{
LineString lineComp = (LineString) linearGeom.getGeometryN(componentIndex);
Coordinate p0 = lineComp.getCoordinateN(segmentIndex);
if (segmentIndex >= lineComp.getNumPoints() - 1)
return p0;
Coordinate p1 = lineComp.getCoordinateN(segmentIndex + 1);
return pointAlongSegmentByFraction(p0, p1, segmentFraction);
}
/**
* Gets a {@link LineSegment} representing the segment of the
* given linear {@link Geometry} which contains this location.
*
* @param linearGeom a linear geometry
* @return the LineSegment containing the location
*/
public LineSegment getSegment(Geometry linearGeom)
{
LineString lineComp = (LineString) linearGeom.getGeometryN(componentIndex);
Coordinate p0 = lineComp.getCoordinateN(segmentIndex);
// check for endpoint - return last segment of the line if so
if (segmentIndex >= lineComp.getNumPoints() - 1) {
Coordinate prev = lineComp.getCoordinateN(lineComp.getNumPoints() - 2);
return new LineSegment(prev, p0);
}
Coordinate p1 = lineComp.getCoordinateN(segmentIndex + 1);
return new LineSegment(p0, p1);
}
/**
* Tests whether this location refers to a valid
* location on the given linear {@link Geometry}.
*
* @param linearGeom a linear geometry
* @return true if this location is valid
*/
public boolean isValid(Geometry linearGeom)
{
if (componentIndex < 0 || componentIndex >= linearGeom.getNumGeometries())
return false;
LineString lineComp = (LineString) linearGeom.getGeometryN(componentIndex);
if (segmentIndex < 0 || segmentIndex > lineComp.getNumPoints())
return false;
if (segmentIndex == lineComp.getNumPoints() && segmentFraction != 0.0)
return false;
if (segmentFraction < 0.0 || segmentFraction > 1.0)
return false;
return true;
}
/**
* Compares this object with the specified object for order.
*
*@param o the LineStringLocation
with which this Coordinate
* is being compared
*@return a negative integer, zero, or a positive integer as this LineStringLocation
* is less than, equal to, or greater than the specified LineStringLocation
*/
public int compareTo(Object o) {
LinearLocation other = (LinearLocation) o;
// compare component indices
if (componentIndex < other.componentIndex) return -1;
if (componentIndex > other.componentIndex) return 1;
// compare segments
if (segmentIndex < other.segmentIndex) return -1;
if (segmentIndex > other.segmentIndex) return 1;
// same segment, so compare segment fraction
if (segmentFraction < other.segmentFraction) return -1;
if (segmentFraction > other.segmentFraction) return 1;
// same location
return 0;
}
/**
* Compares this object with the specified index values for order.
*
* @param componentIndex1 a component index
* @param segmentIndex1 a segment index
* @param segmentFraction1 a segment fraction
* @return a negative integer, zero, or a positive integer as this LineStringLocation
* is less than, equal to, or greater than the specified locationValues
*/
public int compareLocationValues(int componentIndex1, int segmentIndex1, double segmentFraction1) {
// compare component indices
if (componentIndex < componentIndex1) return -1;
if (componentIndex > componentIndex1) return 1;
// compare segments
if (segmentIndex < segmentIndex1) return -1;
if (segmentIndex > segmentIndex1) return 1;
// same segment, so compare segment fraction
if (segmentFraction < segmentFraction1) return -1;
if (segmentFraction > segmentFraction1) return 1;
// same location
return 0;
}
/**
* Compares two sets of location values for order.
*
* @param componentIndex0 a component index
* @param segmentIndex0 a segment index
* @param segmentFraction0 a segment fraction
* @param componentIndex1 another component index
* @param segmentIndex1 another segment index
* @param segmentFraction1 another segment fraction
*@return a negative integer, zero, or a positive integer
* as the first set of location values
* is less than, equal to, or greater than the second set of locationValues
*/
public static int compareLocationValues(
int componentIndex0, int segmentIndex0, double segmentFraction0,
int componentIndex1, int segmentIndex1, double segmentFraction1)
{
// compare component indices
if (componentIndex0 < componentIndex1) return -1;
if (componentIndex0 > componentIndex1) return 1;
// compare segments
if (segmentIndex0 < segmentIndex1) return -1;
if (segmentIndex0 > segmentIndex1) return 1;
// same segment, so compare segment fraction
if (segmentFraction0 < segmentFraction1) return -1;
if (segmentFraction0 > segmentFraction1) return 1;
// same location
return 0;
}
/**
* Tests whether two locations
* are on the same segment in the parent {@link Geometry}.
*
* @param loc a location on the same geometry
* @return true if the locations are on the same segment of the parent geometry
*/
public boolean isOnSameSegment(LinearLocation loc)
{
if (componentIndex != loc.componentIndex) return false;
if (segmentIndex == loc.segmentIndex) return true;
if (loc.segmentIndex - segmentIndex == 1
&& loc.segmentFraction == 0.0)
return true;
if (segmentIndex - loc.segmentIndex == 1
&& segmentFraction == 0.0)
return true;
return false;
}
/**
* Tests whether this location is an endpoint of
* the linear component it refers to.
*
* @param linearGeom the linear geometry referenced by this location
* @return true if the location is a component endpoint
*/
public boolean isEndpoint(Geometry linearGeom)
{
LineString lineComp = (LineString) linearGeom.getGeometryN(componentIndex);
// check for endpoint
int nseg = lineComp.getNumPoints() - 1;
return segmentIndex >= nseg
|| (segmentIndex == nseg && segmentFraction >= 1.0);
}
/**
* Converts a linear location to the lowest equivalent location index.
* The lowest index has the lowest possible component and segment indices.
*
* Specifically:
*
* - if the location point is an endpoint, a location value is returned as (nseg-1, 1.0)
*
- if the location point is ambiguous (i.e. an endpoint and a startpoint), the lowest endpoint location is returned
*
* If the location index is already the lowest possible value, the original location is returned.
*
* @param linearGeom the linear geometry referenced by this location
* @return the lowest equivalent location
*/
public LinearLocation toLowest(Geometry linearGeom)
{
// TODO: compute lowest component index
LineString lineComp = (LineString) linearGeom.getGeometryN(componentIndex);
int nseg = lineComp.getNumPoints() - 1;
// if not an endpoint can be returned directly
if (segmentIndex < nseg) return this;
return new LinearLocation(componentIndex, nseg, 1.0, false);
}
/**
* Copies this location
*
* @return a copy of this location
*/
public Object clone()
{
return new LinearLocation(componentIndex, segmentIndex, segmentFraction);
}
public String toString()
{
return "LinearLoc["
+ componentIndex + ", "
+ segmentIndex + ", "
+ segmentFraction + "]";
}
}