org.locationtech.spatial4j.shape.impl.BufferedLineString Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spatial4j Show documentation
Show all versions of spatial4j Show documentation
Spatial4j is a general purpose spatial / geospatial ASL licensed open-source Java library. It's
core capabilities are 3-fold: to provide common geospatially-aware shapes, to provide distance
calculations and other math, and to read shape formats like WKT and GeoJSON.
The newest version!
/*******************************************************************************
* Copyright (c) 2015 MITRE
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License, Version 2.0 which
* accompanies this distribution and is available at
* http://www.apache.org/licenses/LICENSE-2.0.txt
******************************************************************************/
package org.locationtech.spatial4j.shape.impl;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.shape.BaseShape;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Rectangle;
import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.ShapeCollection;
import org.locationtech.spatial4j.shape.SpatialRelation;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A BufferedLineString is a collection of {@link org.locationtech.spatial4j.shape.impl.BufferedLine} shapes,
* resulting in what some call a "Track" or "Polyline" (ESRI terminology).
* The buffer can be 0. Note that BufferedLine isn't yet aware of geodesics (e.g. the anti-meridian).
*/
public class BufferedLineString extends BaseShape {
//TODO add some geospatial awareness like:
// segment that spans at the dateline (split it at DL?).
private final ShapeCollection segments;
private final double buf;
/**
* Needs at least 1 point, usually more than that. If just one then it's
* internally treated like 2 points.
*/
public BufferedLineString(List points, double buf, SpatialContext ctx) {
this(points, buf, false, ctx);
}
/**
* @param points ordered control points. If empty then this shape is empty.
* @param buf Buffer >= 0
* @param expandBufForLongitudeSkew See {@link BufferedLine
* #expandBufForLongitudeSkew(org.locationtech.spatial4j.shape.Point,
* org.locationtech.spatial4j.shape.Point, double)}.
* If true then the buffer for each segment
* is computed.
*/
public BufferedLineString(List points, double buf, boolean expandBufForLongitudeSkew,
SpatialContext ctx) {
super(ctx);
this.buf = buf;
if (points.isEmpty()) {
this.segments = ctx.makeCollection(Collections.emptyList());
} else {
List segments = new ArrayList<>(points.size() - 1);
Point prevPoint = null;
for (Point point : points) {
if (prevPoint != null) {
double segBuf = buf;
if (expandBufForLongitudeSkew) {
//TODO this is faulty in that it over-buffers. See Issue#60.
segBuf = BufferedLine.expandBufForLongitudeSkew(prevPoint, point, buf);
}
segments.add(new BufferedLine(prevPoint, point, segBuf, ctx));
}
prevPoint = point;
}
if (segments.isEmpty()) {//TODO throw exception instead?
segments.add(new BufferedLine(prevPoint, prevPoint, buf, ctx));
}
this.segments = ctx.makeCollection(segments);
}
}
@Override
public boolean isEmpty() {
return segments.isEmpty();
}
@Override
public Shape getBuffered(double distance, SpatialContext ctx) {
return ctx.makeBufferedLineString(getPoints(), buf + distance);
}
public ShapeCollection getSegments() {
return segments;
}
public double getBuf() {
return buf;
}
@Override
public double getArea(SpatialContext ctx) {
return segments.getArea(ctx);
}
@Override
public SpatialRelation relate(Shape other) {
return segments.relate(other);
}
@Override
public boolean hasArea() {
return segments.hasArea();
}
@Override
public Point getCenter() {
return segments.getCenter();
}
@Override
public Rectangle getBoundingBox() {
return segments.getBoundingBox();
}
@Override
public String toString() {
StringBuilder str = new StringBuilder(100);
str.append("BufferedLineString(buf=").append(buf).append(" pts=");
boolean first = true;
for (Point point : getPoints()) {
if (first) {
first = false;
} else {
str.append(", ");
}
str.append(point.getX()).append(' ').append(point.getY());
}
str.append(')');
return str.toString();
}
public List getPoints() {
if (segments.isEmpty())
return Collections.emptyList();
final List lines = segments.getShapes();
return new AbstractList() {
@Override
public Point get(int index) {
if (index == 0)
return lines.get(0).getA();
return lines.get(index - 1).getB();
}
@Override
public int size() {
return lines.size() + 1;
}
};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BufferedLineString that = (BufferedLineString) o;
if (Double.compare(that.buf, buf) != 0) return false;
if (!segments.equals(that.segments)) return false;
return true;
}
@Override
public int hashCode() {
int result;
long temp;
result = segments.hashCode();
temp = buf != +0.0d ? Double.doubleToLongBits(buf) : 0L;
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
}