com.hazelcast.shaded.org.locationtech.jts.linearref.LengthLocationMap Maven / Gradle / Ivy
/*
* Copyright (c) 2016 Vivid Solutions.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package com.hazelcast.shaded.org.locationtech.jts.linearref;
import com.hazelcast.shaded.org.locationtech.jts.geom.Coordinate;
import com.hazelcast.shaded.org.locationtech.jts.geom.Geometry;
/**
* Computes the {@link LinearLocation} for a given length
* along a linear {@link Geometry}.
* Negative lengths are measured in reverse from end of the linear geometry.
* Out-of-range values are clamped.
*
* Note:
* This class is intended for internal use only, and it
* might be made package-private in a future version of this library
*/
public class LengthLocationMap
{
// TODO: cache computed cumulative length for each vertex
// TODO: support user-defined measures
// TODO: support measure index for fast mapping to a location
/**
* Computes the {@link LinearLocation} for a
* given length along a linear {@link Geometry}.
*
* @param linearGeom the linear geometry to use
* @param length the length index of the location
* @return the {@link LinearLocation} for the length
*/
public static LinearLocation getLocation(Geometry linearGeom, double length)
{
LengthLocationMap locater = new LengthLocationMap(linearGeom);
return locater.getLocation(length);
}
/**
* Computes the {@link LinearLocation} for a
* given length along a linear {@link Geometry},
* with control over how the location
* is resolved at component endpoints.
*
* @param linearGeom the linear geometry to use
* @param length the length index of the location
* @param resolveLower if true lengths are resolved to the lowest possible index
* @return the {@link LinearLocation} for the length
*/
public static LinearLocation getLocation(Geometry linearGeom, double length, boolean resolveLower)
{
LengthLocationMap locater = new LengthLocationMap(linearGeom);
return locater.getLocation(length, resolveLower);
}
/**
* Computes the length for a given {@link LinearLocation}
* on a linear {@link Geometry}.
*
* @param linearGeom the linear geometry to use
* @param loc the {@link LinearLocation} index of the location
* @return the length for the {@link LinearLocation}
*/
public static double getLength(Geometry linearGeom, LinearLocation loc)
{
LengthLocationMap locater = new LengthLocationMap(linearGeom);
return locater.getLength(loc);
}
private final Geometry linearGeom;
public LengthLocationMap(Geometry linearGeom)
{
this.linearGeom = linearGeom;
}
/**
* Compute the {@link LinearLocation} corresponding to a length.
* Negative lengths are measured in reverse from end of the linear geometry.
* Out-of-range values are clamped.
* Ambiguous indexes are resolved to the lowest possible location value.
*
* @param length the length index
* @return the corresponding LinearLocation
*/
public LinearLocation getLocation(double length)
{
return getLocation(length, true);
}
/**
* Compute the {@link LinearLocation} corresponding to a length.
* Negative lengths are measured in reverse from end of the linear geometry.
* Out-of-range values are clamped.
* Ambiguous indexes are resolved to the lowest or highest possible location value,
* depending on the value of resolveLower
*
* @param length the length index
* @return the corresponding LinearLocation
*/
public LinearLocation getLocation(double length, boolean resolveLower)
{
double forwardLength = length;
// negative values are measured from end of geometry
if (length < 0.0) {
double lineLen = linearGeom.getLength();
forwardLength = lineLen + length;
}
LinearLocation loc = getLocationForward(forwardLength);
if (resolveLower) {
return loc;
}
return resolveHigher(loc);
}
private LinearLocation getLocationForward(double length)
{
if (length <= 0.0)
return new LinearLocation();
double totalLength = 0.0;
LinearIterator it = new LinearIterator(linearGeom);
while (it.hasNext()) {
/*
* Special handling is required for the situation when the
* length references exactly to a component endpoint.
* In this case, the endpoint location of the current component
* is returned,
* rather than the startpoint location of the next component.
* This produces consistent behaviour with the project method.
*/
if (it.isEndOfLine()) {
if (totalLength == length) {
int compIndex = it.getComponentIndex();
int segIndex = it.getVertexIndex();
return new LinearLocation(compIndex, segIndex, 0.0);
}
}
else {
Coordinate p0 = it.getSegmentStart();
Coordinate p1 = it.getSegmentEnd();
double segLen = p1.distance(p0);
// length falls in this segment
if (totalLength + segLen > length) {
double frac = (length - totalLength) / segLen;
int compIndex = it.getComponentIndex();
int segIndex = it.getVertexIndex();
return new LinearLocation(compIndex, segIndex, frac);
}
totalLength += segLen;
}
it.next();
}
// length is longer than line - return end location
return LinearLocation.getEndLocation(linearGeom);
}
private LinearLocation resolveHigher(LinearLocation loc)
{
if (! loc.isEndpoint(linearGeom))
return loc;
int compIndex = loc.getComponentIndex();
// if last component can't resolve any higher
if (compIndex >= linearGeom.getNumGeometries() - 1) return loc;
do {
compIndex++;
} while (compIndex < linearGeom.getNumGeometries() - 1
&& linearGeom.getGeometryN(compIndex).getLength() == 0);
// resolve to next higher location
return new LinearLocation(compIndex, 0, 0.0);
}
public double getLength(LinearLocation loc)
{
double totalLength = 0.0;
LinearIterator it = new LinearIterator(linearGeom);
while (it.hasNext()) {
if (! it.isEndOfLine()) {
Coordinate p0 = it.getSegmentStart();
Coordinate p1 = it.getSegmentEnd();
double segLen = p1.distance(p0);
// length falls in this segment
if (loc.getComponentIndex() == it.getComponentIndex()
&& loc.getSegmentIndex() == it.getVertexIndex()) {
return totalLength + segLen * loc.getSegmentFraction();
}
totalLength += segLen;
}
else
{
// At the end of the component
if (loc.getComponentIndex() == it.getComponentIndex()) {
return totalLength;
}
}
it.next();
}
return totalLength;
}
}